@@ -23,8 +23,7 @@ Usage: tutorial N
23 where N is the number of the example you want to run.
23 where N is the number of the example you want to run.
26−A first example
26+## A first example
29Equipped with some basic knowledge about the way we represent API, let's now write our first webservice.
28Equipped with some basic knowledge about the way we represent API, let's now write our first webservice.
@@ -53,7 +52,6 @@ import Data.Aeson
53import Data.Aeson.Types
52import Data.Aeson.Types
54import Data.Attoparsec.ByteString
53import Data.Attoparsec.ByteString
55import Data.ByteString (ByteString)
54import Data.ByteString (ByteString)
57import Data.List
55import Data.List
58import Data.String.Conversions
56import Data.String.Conversions
59import Data.Time.Calendar
57import Data.Time.Calendar
@@ -97,7 +95,7 @@ data User = User
97 { name :: String
95 { name :: String
98 , age :: Int
96 , age :: Int
99 , email :: String
97 , email :: String
100− , registration_date :: Day
98+ , registrationDate :: Day
101 } deriving (Eq, Show, Generic)
99 } deriving (Eq, Show, Generic)
103instance ToJSON User
101instance ToJSON User
@@ -179,8 +177,7 @@ $ curl http://localhost:8081/users
179[{"email":"isaac@newton.co.uk","registration_date":"1683-03-01","age":372,"name":"Isaac Newton"},{"email":"ae@mc2.org","registration_date":"1905-12-01","age":136,"name":"Albert Einstein"}]
177[{"email":"isaac@newton.co.uk","registration_date":"1683-03-01","age":372,"name":"Isaac Newton"},{"email":"ae@mc2.org","registration_date":"1905-12-01","age":136,"name":"Albert Einstein"}]
182−More endpoints
180+## More endpoints
185What if we want more than one endpoint? Let's add `/albert` and `/isaac` to view the corresponding users encoded in JSON.
182What if we want more than one endpoint? Let's add `/albert` and `/isaac` to view the corresponding users encoded in JSON.
@@ -218,8 +215,7 @@ And that's it! You can run this example with
218`dist/build/tutorial/tutorial 2` and check out the data available
215`dist/build/tutorial/tutorial 2` and check out the data available
219at `/users`, `/albert` and `/isaac`.
216at `/users`, `/albert` and `/isaac`.
221−From combinators to handler arguments
218+## From combinators to handler arguments
222−=====================================
224Fine, we can write trivial webservices easily, but none of the two above use
220Fine, we can write trivial webservices easily, but none of the two above use
225any "fancy" combinator from servant. Let's address this and use `QueryParam`,
221any "fancy" combinator from servant. Let's address this and use `QueryParam`,
@@ -237,8 +233,8 @@ type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Posit
237 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
233 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
239data Position = Position
235data Position = Position
240− { x :: Int
236+ { xCoord :: Int
241− , y :: Int
237+ , yCoord :: Int
242 } deriving Generic
238 } deriving Generic
244instance ToJSON Position
240instance ToJSON Position
@@ -334,8 +330,7 @@ that get turned into arguments to the handlers, the type of the argument.
334 > - `QueryParams "something" a` and `MatrixParams "something" a` get turned into arguments of type `[a]`.
330 > - `QueryParams "something" a` and `MatrixParams "something" a` get turned into arguments of type `[a]`.
335 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
331 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
337−The `FromText`/`ToText` classes
333+## The `FromHttpApiData`/`ToHttpApiData` classes
338−===============================
340Wait... How does *servant* know how to decode the `Int`s from the URL? Or how
335Wait... How does *servant* know how to decode the `Int`s from the URL? Or how
341to decode a `ClientInfo` value from the request body? This is what this and the
336to decode a `ClientInfo` value from the request body? This is what this and the
@@ -343,54 +338,42 @@ following two sections address.
344`Capture`s and `QueryParam`s are represented by some textual value in URLs.
339`Capture`s and `QueryParam`s are represented by some textual value in URLs.
345`Header`s are similarly represented by a pair of a header name and a
340`Header`s are similarly represented by a pair of a header name and a
346−corresponding (textual) value in the request's "metadata". This is why we
341+corresponding (textual) value in the request's "metadata". How types are
347−decided to provide a pair of typeclasses, `FromText` and `ToText` which just
342+decoded from headers, captures, and query params is expressed in a class
348−let you say that you can respectively *extract* or *encode* values of some type
343+`FromHttpApiData` (from the package
349−*from*/*to* text. Here are the definitions:
344+[*http-api-data*](http://hackage.haskell.org/package/http-api-data)):
351``` haskell ignore
346``` haskell ignore
352−class FromText a where
347+class FromHttpApiData a where
353− fromText :: Text -> Maybe a
348+ {-# MINIMAL parseUrlPiece | parseQueryParam #-}
354−
349+ -- | Parse URL path piece.
355−class ToText a where
350+ parseUrlPiece :: Text -> Either Text a
356− toText :: a -> Text
351+ parseUrlPiece = parseQueryParam
359−And as long as the type that a `Capture`/`QueryParam`/`Header`/etc will be
353+ -- | Parse HTTP header value.
360−decoded to provides a `FromText` instance, it will Just Work. *servant*
354+ parseHeader :: ByteString -> Either Text a
361−provides a decent number of instances, but here are some examples of defining
355+ parseHeader = parseUrlPiece . decodeUtf8
364−``` haskell
357+ -- | Parse query param value.
365−-- A typical enumeration
358+ parseQueryParam :: Text -> Either Text a
366−data Direction
359+ parseQueryParam = parseUrlPiece
372−newtype UserId = UserId Int64
375−or writing the instances by hand:
362+As you can see, as long as you provide either `parseUrlPiece` (for `Capture`s)
376−
363+or `parseQueryParam` (for `QueryParam`s), the other methods will be defined in
377−``` haskell ignore
364+terms of this.
378−instance FromText UserId where
379− fromText = fmap UserId fromText
381−instance ToText UserId where
366+*http-api-data* provides a decent number of instances, helpers for defining new
382− toText (UserId i) = toText i
367+ones, and wonderful documentation.
385There's not much else to say about these classes. You will need instances for
369There's not much else to say about these classes. You will need instances for
386−them when using `Capture`, `QueryParam`, `QueryParams`, `MatrixParam`,
370+them when using `Capture`, `QueryParam`, `QueryParams`, and `Header` with your
387−`MatrixParams` and `Header` with your types. You will need `FromText` instances
371+types. You will need `FromHttpApiData` instances for server-side request
388−for server-side request handlers and `ToText` instances only when using
372+handlers and `ToHttpApiData` instances only when using
389*servant-client*, as described in the [section about deriving haskell
373*servant-client*, as described in the [section about deriving haskell
390functions to query an API](/tutorial/client.html).
374functions to query an API](/tutorial/client.html).
392−Using content-types with your data types
376+## Using content-types with your data types
393−========================================
395The same principle was operating when decoding request bodies from JSON, and
378The same principle was operating when decoding request bodies from JSON, and
396responses *into* JSON. (JSON is just the running example - you can do this with
379responses *into* JSON. (JSON is just the running example - you can do this with
@@ -399,8 +382,8 @@ any content-type.)
399This section introduces a couple of typeclasses provided by *servant* that make
382This section introduces a couple of typeclasses provided by *servant* that make
400all of this work.
383all of this work.
402−The truth behind `JSON`
385+### The truth behind `JSON`
403−-----------------------
386+
405What exactly is `JSON`? Like the 3 other content types provided out of the box
388What exactly is `JSON`? Like the 3 other content types provided out of the box
406by *servant*, it's a really dumb data type.
389by *servant*, it's a really dumb data type.
@@ -464,8 +447,6 @@ And now the `MimeUnrender` class, which lets us extract values from lazy
464``` haskell ignore
447``` haskell ignore
465class Accept ctype => MimeUnrender ctype a where
448class Accept ctype => MimeUnrender ctype a where
466 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
449 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
468− mimeUnrender :: Proxy ctype -> (ByteString -> Either String a)
471We don't have much work to do there either, `Data.Aeson.eitherDecode` is
452We don't have much work to do there either, `Data.Aeson.eitherDecode` is
@@ -496,8 +477,7 @@ HTML representation of the data they want, ready to be included in any HTML
496document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/), simply by adding `Accept:
477document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/), simply by adding `Accept:
497text/html` to their request headers.
478text/html` to their request headers.
499−Case-studies: *servant-blaze* and *servant-lucid*
480+### Case-studies: *servant-blaze* and *servant-lucid*
500−-------------------------------------------------
502These days, most of the haskellers who write their HTML UIs directly from
482These days, most of the haskellers who write their HTML UIs directly from
503Haskell use either [blaze-html](http://hackage.haskell.org/package/blaze-html)
483Haskell use either [blaze-html](http://hackage.haskell.org/package/blaze-html)
@@ -615,8 +595,8 @@ instance ToHtml [Person] where
615We create some `Person` values and serve them as a list:
595We create some `Person` values and serve them as a list:
617``` haskell
597``` haskell
618−persons :: [Person]
598+people :: [Person]
619−persons =
599+people =
620 [ Person "Isaac" "Newton"
600 [ Person "Isaac" "Newton"
621 , Person "Albert" "Einstein"
601 , Person "Albert" "Einstein"
@@ -625,7 +605,7 @@ personAPI :: Proxy PersonAPI
625personAPI = Proxy
605personAPI = Proxy
627server4 :: Server PersonAPI
607server4 :: Server PersonAPI
628−server4 = return persons
608+server4 = return people
630app2 :: Application
610app2 :: Application
631app2 = serve personAPI EmptyConfig server4
611app2 = serve personAPI EmptyConfig server4
@@ -641,8 +621,7 @@ And we're good to go. You can run this example with `dist/build/tutorial/tutoria
641 # or just point your browser to http://localhost:8081/persons
621 # or just point your browser to http://localhost:8081/persons
644−The `ExceptT ServantErr IO` monad
624+## The `ExceptT ServantErr IO` monad
645−=================================
647At the heart of the handlers is the monad they run in, namely `ExceptT
626At the heart of the handlers is the monad they run in, namely `ExceptT
648ServantErr IO`. One might wonder: why this monad? The answer is that it is the
627ServantErr IO`. One might wonder: why this monad? The answer is that it is the
@@ -660,40 +639,39 @@ Let's recall some definitions.
660-- from the Prelude
639-- from the Prelude
661data Either e a = Left e | Right a
640data Either e a = Left e | Right a
663−-- from the 'either' package at
642+-- from the 'mtl' package at
664−-- http://hackage.haskell.org/package/either-4.3.3.2/docs/Control-Monad-Trans-Either.html
665−newtype ExceptT e m a
643+newtype ExceptT e m a = ExceptT ( m (Either e a) )
666− = ExceptT { runEitherT :: m (Either e a) }
669In short, this means that a handler of type `ExceptT ServantErr IO a` is simply
646In short, this means that a handler of type `ExceptT ServantErr IO a` is simply
670equivalent to a computation of type `IO (Either ServantErr a)`, that is, an IO
647equivalent to a computation of type `IO (Either ServantErr a)`, that is, an IO
671action that either returns an error or a result.
648action that either returns an error or a result.
673−The aforementioned `either` package is worth taking a look at. Perhaps most
650+The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT)
674−importantly:
651+from which `ExceptT` comes is worth looking at.
675−
652+Perhaps most importantly, `ExceptT` is an instance of `MonadError`, so
677−left :: Monad m => e -> ExceptT e m a
679−Allows you to return an error from your handler (whereas `return` is enough to
653+`throwError` can be used to return an error from your handler (whereas `return`
680−return a success).
654+ is enough to return a success).
682Most of what you'll be doing in your handlers is running some IO and,
656Most of what you'll be doing in your handlers is running some IO and,
683depending on the result, you might sometimes want to throw an error of some
657depending on the result, you might sometimes want to throw an error of some
684kind and abort early. The next two sections cover how to do just that.
658kind and abort early. The next two sections cover how to do just that.
686−Performing IO
660+### Performing IO
662+Another important instance from the list above is `MonadIO m => MonadIO
689−Another important instance from the list above is `MonadIO m => MonadIO (ExceptT e m)`. [`MonadIO`](http://hackage.haskell.org/package/transformers-0.4.3.0/docs/Control-Monad-IO-Class.html) is a class from the *transformers* package defined as:
664+[`MonadIO`](http://hackage.haskell.org/package/transformers-0.4.3.0/docs/Control-Monad-IO-Class.html)
665+is a class from the *transformers* package defined as:
691``` haskell ignore
667``` haskell ignore
692class Monad m => MonadIO m where
668class Monad m => MonadIO m where
693 liftIO :: IO a -> m a
669 liftIO :: IO a -> m a
696−Obviously, the `IO` monad provides a `MonadIO` instance. Hence for any type `e`, `ExceptT e IO` has a `MonadIO` instance. So if you want to run any kind of IO computation in your handlers, just use `liftIO`:
672+Obviously, the `IO` monad provides a `MonadIO` instance. Hence for any type
673+`e`, `ExceptT e IO` has a `MonadIO` instance. So if you want to run any kind of
674+IO computation in your handlers, just use `liftIO`:
698``` haskell
676``` haskell
699type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
677type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
@@ -710,8 +688,7 @@ server5 = do
710 return (FileContent filecontent)
688 return (FileContent filecontent)
713−Failing, through `ServantErr`
691+### Failing, through `ServantErr`
714−-----------------------------
716If you want to explicitly fail at providing the result promised by an endpoint
693If you want to explicitly fail at providing the result promised by an endpoint
717using the appropriate HTTP status code (not found, unauthorized, etc) and some
694using the appropriate HTTP status code (not found, unauthorized, etc) and some
@@ -787,8 +764,7 @@ query it, first without the file and then with the file.
787 {"content":"Hello\n"}
764 {"content":"Hello\n"}
790−Response headers
767+## Response headers
793To add headers to your response, use [addHeader](http://hackage.haskell.org/package/servant-0.4.4/docs/Servant-API-ResponseHeaders.html).
769To add headers to your response, use [addHeader](http://hackage.haskell.org/package/servant-0.4.4/docs/Servant-API-ResponseHeaders.html).
794Note that this changes the type of your API, as we can see in the following example:
770Note that this changes the type of your API, as we can see in the following example:
@@ -800,9 +776,9 @@ myHandler :: Server MyHandler
800myHandler = return $ addHeader 1797 albert
776myHandler = return $ addHeader 1797 albert
779+Note that the type of `addHeader x` is different than the type of `x`!
804−Serving static files
781+## Serving static files
807*servant-server* also provides a way to just serve the content of a directory
783*servant-server* also provides a way to just serve the content of a directory
808under some path in your web API. As mentioned earlier in this document, the
784under some path in your web API. As mentioned earlier in this document, the
@@ -842,7 +818,9 @@ app3 :: Application
842app3 = serve codeAPI EmptyConfig server7
818app3 = serve codeAPI EmptyConfig server7
845−This server will match any request whose path starts with `/code` and will look for a file at the path described by the rest of the request path, inside the *tutorial/* directory of the path you run the program from.
821+This server will match any request whose path starts with `/code` and will look
822+for a file at the path described by the rest of the request path, inside the
823+ *tutorial/* directory of the path you run the program from.
847In other words:
825In other words:
@@ -941,8 +919,7 @@ $ curl http://localhost:8081/foo
944−Nested APIs
922+## Nested APIs
947Let's see how you can define APIs in a modular way, while avoiding repetition. Consider this simple example:
924Let's see how you can define APIs in a modular way, while avoiding repetition. Consider this simple example:
@@ -1130,8 +1107,7 @@ serverFor = error "..."
1130-- or the mailing list if you get stuck!
1107-- or the mailing list if you get stuck!
1133−Using another monad for your handlers
1110+## Using another monad for your handlers
1134−=====================================
1136Remember how `Server` turns combinators for HTTP methods into `ExceptT ServantErr IO`? Well, actually, there's more to that. `Server` is actually a simple type synonym.
1112Remember how `Server` turns combinators for HTTP methods into `ExceptT ServantErr IO`? Well, actually, there's more to that. `Server` is actually a simple type synonym.
@@ -1143,8 +1119,7 @@ type Server api = ServerT api (ExceptT ServantErr IO)
1144The first and main question one might have then is: how do we write handlers that run in another monad? How can we "bring back" the value from a given monad into something *servant* can understand?
1120The first and main question one might have then is: how do we write handlers that run in another monad? How can we "bring back" the value from a given monad into something *servant* can understand?
1146−Natural transformations
1122+### Natural transformations
1147−-----------------------
1149If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1124If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1150do we have?
1125do we have?
@@ -1202,8 +1177,7 @@ We unfortunately can't use `readerServerT` as an argument of `serve`, because
1202`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `ExceptT
1177`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `ExceptT
1203ServantErr IO`. But there's a simple solution to this.
1178ServantErr IO`. But there's a simple solution to this.
1205−Enter `enter`
1180+### Enter `enter`
1208That's right. We have just written `readerToEither`, which is exactly what we
1182That's right. We have just written `readerToEither`, which is exactly what we
1209would need to apply to the results of all handlers to make the handlers have the
1183would need to apply to the results of all handlers to make the handlers have the
@@ -1230,8 +1204,7 @@ $ curl http://localhost:8081/b
1233−Conclusion
1207+## Conclusion
1236You're now equipped to write any kind of webservice/web-application using *servant*. One thing not covered here is how to incorporate your own combinators and will be the topic of a page on the website. The rest of this document focuses on *servant-client*, *servant-jquery* and *servant-docs*.
1209You're now equipped to write any kind of webservice/web-application using *servant*. One thing not covered here is how to incorporate your own combinators and will be the topic of a page on the website. The rest of this document focuses on *servant-client*, *servant-jquery* and *servant-docs*.