paradox-servant
Usage
As a Library
import Paradox.Servant (GenerateConfig(..), generateFromFile)
main :: IO ()
main = do
let cfg = GenerateConfig
{ gcModuleName = "Api.Servant"
, gcApiTypeName = "API"
}
result <- generateFromFile cfg "api.dox"
case result of
Left err -> putStrLn $ "Error: " <> show err
Right src -> writeFile "Api/Servant.hs" (show src)
Example
Given this .dox specification:
import std
type User
id: Text
name: Text
email: Text
type CreateUserRequest
name: Text
email: Text
users: URI
http://localhost/api/users GET (Unit. [User])
createUser: URI
http://localhost/api/users POST (CreateUserRequest. User)
getUser: URI
http://localhost/api/users/${Integer} GET (Unit. User)
paradox-servant generates:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
module Api.Servant (API) where
import Data.Text (Text)
import Servant
import Dox (CreateUserRequest, User)
type API =
"api" :> "users" :> Get '[JSON] [User]
:<|> "api" :> "users" :> ReqBody '[JSON] CreateUserRequest :> Post '[JSON] User
:<|> "api" :> "users" :> Capture "integer" Integer :> Get '[JSON] User
How It Works
paradox-servant operates in three stages:
-
Parse and type-check — Runs the Paradox compiler on the
.doxfile viaParadox.Strap -
Extract endpoints — Walks the typed specification and pulls out every
URI-typed constant, reading itspath,meta(HTTP methods),io(request/response types), andaccept(MIME types) -
Generate Servant types — Maps each endpoint to a Servant route combinator chain
Path Segments
Path segments are mapped to Servant combinators:
-
Literal segments like
usersbecome"users" -
Interpolated type parameters like
${Integer}becomeCapture "integer" Integer -
Named captures like
${Integer id}becomeCapture "id" Integer
HTTP Methods
The meta field maps to Servant verbs:
.dox Method |
Servant Verb |
|---|---|
|
|
|
|
|
|
|
|
|
|
When the output type is Unit or (), the NoContent variants are used (GetNoContent, PostNoContent, etc.).
API Reference
generateFromFile
generateFromFile :: GenerateConfig -> FilePath -> IO (Either Text Text)
Parse a .dox file, type-check it, extract endpoints, and generate the Servant module. Returns Left on parse/type-check failure, Right with the generated source on success.
generateFromSpec
generateFromSpec :: GenerateConfig -> Map TypeIdentifier Text -> Specification Typed -> Text
Generate from an already-parsed specification. Useful when you have the specification in memory from another pipeline stage.
See Also
-
URI Types — URI literal syntax and structure
-
Haskell Code Generation — Generate the types module that Servant imports
-
paradox-express — Express.js equivalent
-
paradox-node — Full-stack CRUD server with PostgreSQL
-
paradox-gloo-net — Rust/WASM HTTP client