Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Servant Study Notes

Servant Study Notes

To Understand How Servant Itself Works

Data Kinds

The DataKinds extension allows us to promote data constructors into type constructors, which also promotes their type constructors into kind constructors. To promote something up a level, we prefix the name with an apostrophe, or tick: '.

This segment from Basic Type Level Programming in Haskell.

Type Families

The type of the web application is determined by the API type, through a type family named Server. (Type families are just functions that take types as input and return types.)

This segment from Serving an API.

Type Operators

In standard Haskell, operator symbols may only be used at the value level.The only exception to this is the function arrow, (->), which is a built-in type operator. GHC expands the role of operators with the TypeOperators extension, which makes it possible to use an operator as the name of a type.

This segment from Type Operators.

How to Use Servant

JSON Output

Aeson uses toEncoding by default, so if the type has an instance, it will work.

Parsing Input (via URL Fragment, URL Query or Header)

There must be an instance of the FromHttpApiData typeclass from http-api-data in order to parse directly to that type.

Parsing / Outputting Non-Supported Types

See Servant Docs - The truth behind JSON.

Outputting HTML

You can use blaze-html or lucid. For info about emitting them from servant, see Servant Docs - Case-studies: servant-blaze and servant-lucid.

Both servant-blaze and servant-lucid let you use HTMLLucid and HTMLBlaze in any content-type list as long as you provide an instance of the appropriate class (ToMarkup for blaze-html, ToHtml for lucid).

Multiple Return Types

type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]

It appears that the first listed is default if no Accept header is given. In this case JSON.

Handler Monad

By default, handlers run in the following Monad (infact, it is a newtype of this type):

ExceptT ServerError IO

Throwing Errors

Errors thrown should be of the following type (from Servant.Server):

data ServerError = ServerError
    { errHTTPCode     :: Int
    , errReasonPhrase :: String
    , errBody         :: ByteString -- lazy bytestring
    , errHeaders      :: [Header]

There are precompleted helpers such as err503 where you can override the part you want to change; usually errBody.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment