@@ -1,12 +1,35 @@
1−# Serving an API
2+title: Serving an API
3Enough chit-chat about type-level combinators and representing an API as a
6Enough chit-chat about type-level combinators and representing an API as a
4type. Can we have a webservice already?
7type. Can we have a webservice already?
6−## A first example
9+If you want to follow along with the code and run the examples while you read this guide:
12+cabal get servant-examples
13+cd servant-examples-<VERSION>
15+cabal install --dependencies-only
16+cabal configure && cabal build
19+This will produce a `tutorial` executable in the
20+`dist/build/tutorial` directory that just runs the example corresponding
21+to the number specified as a command line argument:
24+$ dist/build/tutorial/tutorial
26+ where N is the number of the example you want to run.
8−Equipped with some basic knowledge about the way we represent APIs, let's now
32+Equipped with some basic knowledge about the way we represent API, let's now write our first webservice.
9−write our first webservice.
11The source for this tutorial section is a literate haskell file, so first we
34The source for this tutorial section is a literate haskell file, so first we
12need to have some language extensions and imports:
35need to have some language extensions and imports:
@@ -18,7 +41,6 @@ need to have some language extensions and imports:
18{-# LANGUAGE GeneralizedNewtypeDeriving #-}
41{-# LANGUAGE GeneralizedNewtypeDeriving #-}
19{-# LANGUAGE MultiParamTypeClasses #-}
42{-# LANGUAGE MultiParamTypeClasses #-}
20{-# LANGUAGE OverloadedStrings #-}
43{-# LANGUAGE OverloadedStrings #-}
21−{-# LANGUAGE RankNTypes #-}
22{-# LANGUAGE ScopedTypeVariables #-}
44{-# LANGUAGE ScopedTypeVariables #-}
23{-# LANGUAGE TypeOperators #-}
45{-# LANGUAGE TypeOperators #-}
@@ -27,14 +49,15 @@ module Server where
27import Prelude ()
49import Prelude ()
28import Prelude.Compat
50import Prelude.Compat
30−import Control.Monad.Except
52+import Control.Monad.IO.Class
31import Control.Monad.Reader
53import Control.Monad.Reader
54+import Control.Monad.Trans.Except
32import Data.Aeson
55import Data.Aeson
33import Data.Aeson.Types
56import Data.Aeson.Types
34import Data.Attoparsec.ByteString
57import Data.Attoparsec.ByteString
35import Data.ByteString (ByteString)
58import Data.ByteString (ByteString)
36import Data.List
60import Data.List
38import Data.String.Conversions
61import Data.String.Conversions
39import Data.Time.Calendar
62import Data.Time.Calendar
40import GHC.Generics
63import GHC.Generics
@@ -46,18 +69,15 @@ import Servant
46import System.Directory
69import System.Directory
47import Text.Blaze
70import Text.Blaze
48import Text.Blaze.Html.Renderer.Utf8
71import Text.Blaze.Html.Renderer.Utf8
49−import Servant.Types.SourceT (source)
50import qualified Data.Aeson.Parser
72import qualified Data.Aeson.Parser
51import qualified Text.Blaze.Html
73import qualified Text.Blaze.Html
54−**Important**: the `Servant` module comes from the **servant-server** package,
76+``` haskell ignore
55−the one that lets us run webservers that implement a particular API type. It
77+{-# LANGUAGE TypeFamilies #-}
56−reexports all the types from the **servant** package that let you declare API
78+```
57−types as well as everything you need to turn your request handlers into a
79+
58−fully-fledged webserver. This means that in your applications, you can just add
80+**Important**: the `Servant` module comes from the *servant-server* package, the one that lets us run webservers that implement a particular API type. It reexports all the types from the *servant* package that let you declare API types as well as everything you need to turn your request handlers into a fully-fledged webserver. This means that in your applications, you can just add *servant-server* as a dependency, import `Servant` and not worry about anything else.
59−**servant-server** as a dependency, import `Servant` and not worry about anything
62We will write a server that will serve the following API.
82We will write a server that will serve the following API.
@@ -96,6 +116,12 @@ users1 =
119+Let's also write our API type.
122+type UserAPI1 = "users" :> Get '[JSON] [User]
99We can now take care of writing the actual webservice that will handle requests
125We can now take care of writing the actual webservice that will handle requests
100to such an API. This one will be very simple, being reduced to just a single
126to such an API. This one will be very simple, being reduced to just a single
101endpoint. The type of the web application is determined by the API type,
127endpoint. The type of the web application is determined by the API type,
@@ -107,11 +133,11 @@ corresponding API type.
107The first thing to know about the `Server` type family is that behind the
133The first thing to know about the `Server` type family is that behind the
108scenes it will drive the routing, letting you focus only on the business
134scenes it will drive the routing, letting you focus only on the business
109logic. The second thing to know is that for each endpoint, your handlers will
135logic. The second thing to know is that for each endpoint, your handlers will
110−by default run in the `Handler` monad. This is overridable very
136+by default run in the `ExceptT ServantErr IO` monad. This is overridable very
111easily, as explained near the end of this guide. Third thing, the type of the
137easily, as explained near the end of this guide. Third thing, the type of the
112value returned in that monad must be the same as the second argument of the
138value returned in that monad must be the same as the second argument of the
113HTTP method combinator used for the corresponding endpoint. In our case, it
139HTTP method combinator used for the corresponding endpoint. In our case, it
114−means we must provide a handler of type `Handler [User]`. Well,
140+means we must provide a handler of type `ExceptT ServantErr IO [User]`. Well,
115we have a monad, let's just `return` our list:
141we have a monad, let's just `return` our list:
117``` haskell
143``` haskell
@@ -119,9 +145,7 @@ server1 :: Server UserAPI1
119server1 = return users1
145server1 = return users1
122−That's it. Now we can turn `server` into an actual webserver using
148+That's it. Now we can turn `server` into an actual webserver using [wai](http://hackage.haskell.org/package/wai) and [warp](http://hackage.haskell.org/package/warp):
123−[wai](http://hackage.haskell.org/package/wai) and
124−[warp](http://hackage.haskell.org/package/warp):
126``` haskell
150``` haskell
127userAPI :: Proxy UserAPI1
151userAPI :: Proxy UserAPI1
@@ -131,7 +155,7 @@ userAPI = Proxy
131-- which you can think of as an "abstract" web application,
155-- which you can think of as an "abstract" web application,
132-- not yet a webserver.
156-- not yet a webserver.
133app1 :: Application
157app1 :: Application
134−app1 = serve userAPI server1
158+app1 = serve userAPI EmptyConfig server1
137The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
161The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
@@ -146,8 +170,9 @@ main = run 8081 app1
147You can put this all into a file or just grab [servant's
171You can put this all into a file or just grab [servant's
148repo](http://github.com/haskell-servant/servant) and look at the
172repo](http://github.com/haskell-servant/servant) and look at the
149−*doc/tutorial* directory. This code (the source of this web page) is in
173+*servant-examples* directory. The code we have just explored is in
150−*doc/tutorial/Server.lhs*.
174+*tutorial/T1.hs*, runnable with
175+`dist/build/tutorial/tutorial 1`.
152If you run it, you can go to `http://localhost:8081/users` in your browser or
177If you run it, you can go to `http://localhost:8081/users` in your browser or
153query it with curl and you see:
178query it with curl and you see:
@@ -157,10 +182,10 @@ $ curl http://localhost:8081/users
157[{"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[{"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"}]
160−## More endpoints
185+More endpoints
162−What if we want more than one endpoint? Let's add `/albert` and `/isaac` to
188+What if we want more than one endpoint? Let's add `/albert` and `/isaac` to view the corresponding users encoded in JSON.
163−view the corresponding users encoded in JSON.
165``` haskell
190``` haskell
166type UserAPI2 = "users" :> Get '[JSON] [User]
191type UserAPI2 = "users" :> Get '[JSON] [User]
@@ -183,7 +208,7 @@ users2 = [isaac, albert]
184Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
209Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
185are going to separate the handlers with `:<|>` too! They must be provided in
210are going to separate the handlers with `:<|>` too! They must be provided in
186−the same order as in the API type.
211+the same order as the one they appear in in the API type.
188``` haskell
213``` haskell
189server2 :: Server UserAPI2
214server2 :: Server UserAPI2
@@ -192,21 +217,22 @@ server2 = return users2
192 :<|> return isaac
217 :<|> return isaac
195−And that's it! You can run this example in the same way that we showed for
220+And that's it! You can run this example with
221+`dist/build/tutorial/tutorial 2` and check out the data available
196−`server1` and check out the data available at `/users`, `/albert` and `/isaac`.
222+at `/users`, `/albert` and `/isaac`.
198−## From combinators to handler arguments
224+From combinators to handler arguments
225+=====================================
200Fine, we can write trivial webservices easily, but none of the two above use
227Fine, we can write trivial webservices easily, but none of the two above use
201any "fancy" combinator from servant. Let's address this and use `QueryParam`,
228any "fancy" combinator from servant. Let's address this and use `QueryParam`,
202−`Capture` and `ReqBody` right away. You'll see how each occurrence of these
229+`Capture` and `ReqBody` right away. You'll see how each occurence of these
203combinators in an endpoint makes the corresponding handler receive an
230combinators in an endpoint makes the corresponding handler receive an
204argument of the appropriate type automatically. You don't have to worry about
231argument of the appropriate type automatically. You don't have to worry about
205manually looking up URL captures or query string parameters, or
232manually looking up URL captures or query string parameters, or
206decoding/encoding data from/to JSON. Never.
233decoding/encoding data from/to JSON. Never.
208−We are going to use the following data types and functions to implement a
235+We are going to use the following data types and functions to implement a server for `API`.
211``` haskell
237``` haskell
212type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
238type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
@@ -214,8 +240,8 @@ type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Posit
214 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
240 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
216data Position = Position
242data Position = Position
217− { xCoord :: Int
243+ { x :: Int
218− , yCoord :: Int
244+ , y :: Int
219 } deriving Generic
245 } deriving Generic
221instance ToJSON Position
247instance ToJSON Position
@@ -265,15 +291,15 @@ server3 = position
265 :<|> hello
291 :<|> hello
266 :<|> marketing
292 :<|> marketing
268− where position :: Int -> Int -> Handler Position
294+ where position :: Int -> Int -> ExceptT ServantErr IO Position
269 position x y = return (Position x y)
295 position x y = return (Position x y)
271− hello :: Maybe String -> Handler HelloMessage
297+ hello :: Maybe String -> ExceptT ServantErr IO HelloMessage
272 hello mname = return . HelloMessage $ case mname of
298 hello mname = return . HelloMessage $ case mname of
273 Nothing -> "Hello, anonymous coward"
299 Nothing -> "Hello, anonymous coward"
274 Just n -> "Hello, " ++ n
300 Just n -> "Hello, " ++ n
276− marketing :: ClientInfo -> Handler Email
302+ marketing :: ClientInfo -> ExceptT ServantErr IO Email
277 marketing clientinfo = return (emailForClient clientinfo)
303 marketing clientinfo = return (emailForClient clientinfo)
@@ -288,86 +314,99 @@ parameter might not always be there);
289 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
315 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
291−And that's it. Here's the example in action:
317+And that's it. You can see this example in action by running `dist/build/tutorial/tutorial 3`.
294$ curl http://localhost:8081/position/1/2
320$ curl http://localhost:8081/position/1/2
295−{"xCoord":1,"yCoord":2}
321+{"x":1,"y":2}
296$ curl http://localhost:8081/hello
322$ curl http://localhost:8081/hello
297{"msg":"Hello, anonymous coward"}
323{"msg":"Hello, anonymous coward"}
298$ curl http://localhost:8081/hello?name=Alp
324$ curl http://localhost:8081/hello?name=Alp
299{"msg":"Hello, Alp"}
325{"msg":"Hello, Alp"}
300−$ curl -X POST -d '{"clientName":"Alp Mestanogullari", "clientEmail" : "alp@foo.com", "clientAge": 25, "clientInterestedIn": ["haskell", "mathematics"]}' -H 'Accept: application/json' -H 'Content-type: application/json' http://localhost:8081/marketing
326+$ curl -X POST -d '{"name":"Alp Mestanogullari", "email" : "alp@foo.com", "age": 25, "interested_in": ["haskell", "mathematics"]}' -H 'Accept: application/json' -H 'Content-type: application/json' http://localhost:8081/marketing
301{"subject":"Hey Alp Mestanogullari, we miss you!","body":"Hi Alp Mestanogullari,\n\nSince you've recently turned 25, have you checked out our latest haskell, mathematics products? Give us a visit!","to":"alp@foo.com","from":"great@company.com"}
327{"subject":"Hey Alp Mestanogullari, we miss you!","body":"Hi Alp Mestanogullari,\n\nSince you've recently turned 25, have you checked out our latest haskell, mathematics products? Give us a visit!","to":"alp@foo.com","from":"great@company.com"}
304−For reference, here's a list of some combinators from **servant**:
330+For reference, here's a list of some combinators from *servant* and for those
331+that get turned into arguments to the handlers, the type of the argument.
306− > - `Delete`, `Get`, `Patch`, `Post`, `Put`: these do not become arguments. They provide the return type of handlers, which usually is `Handler <something>`.
333+ > - `Delete`, `Get`, `Patch`, `Post`, `Put`: these do not become arguments. They provide the return type of handlers, which usually is `ExceptT ServantErr IO <something>`.
307 > - `Capture "something" a` becomes an argument of type `a`.
334 > - `Capture "something" a` becomes an argument of type `a`.
308− > - `QueryParam "something" a`, `Header "something" a` all become arguments of type `Maybe a`, because there might be no value at all specified by the client for these.
335+ > - `QueryParam "something" a`, `MatrixParam "something" a`, `Header "something" a` all become arguments of type `Maybe a`, because there might be no value at all specified by the client for these.
309− > - `QueryFlag "something"` gets turned into an argument of type `Bool`.
336+ > - `QueryFlag "something"` and `MatrixFlag "something"` get turned into arguments of type `Bool`.
310− > - `QueryParams "something" a` gets turned into an argument of type `[a]`.
337+ > - `QueryParams "something" a` and `MatrixParams "something" a` get turned into arguments of type `[a]`.
311 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
338 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
313−## The `FromHttpApiData`/`ToHttpApiData` classes
340+The `FromText`/`ToText` classes
341+===============================
315−Wait... How does **servant** know how to decode the `Int`s from the URL? Or how
343+Wait... How does *servant* know how to decode the `Int`s from the URL? Or how
316−to decode a `ClientInfo` value from the request body? The following three sections will
344+to decode a `ClientInfo` value from the request body? This is what this and the
317−help us answer these questions.
345+following two sections address.
319`Capture`s and `QueryParam`s are represented by some textual value in URLs.
347`Capture`s and `QueryParam`s are represented by some textual value in URLs.
320`Header`s are similarly represented by a pair of a header name and a
348`Header`s are similarly represented by a pair of a header name and a
321−corresponding (textual) value in the request's "metadata". How types are
349+corresponding (textual) value in the request's "metadata". This is why we
322−decoded from headers, captures, and query params is expressed in a class
350+decided to provide a pair of typeclasses, `FromText` and `ToText` which just
323−`FromHttpApiData` (from the package
351+let you say that you can respectively *extract* or *encode* values of some type
324−[**http-api-data**](http://hackage.haskell.org/package/http-api-data)):
352+*from*/*to* text. Here are the definitions:
326``` haskell ignore
354``` haskell ignore
327−class FromHttpApiData a where
355+class FromText a where
328− {-# MINIMAL parseUrlPiece | parseQueryParam #-}
356+ fromText :: Text -> Maybe a
329− -- | Parse URL path piece.
357+
330− parseUrlPiece :: Text -> Either Text a
358+class ToText a where
331− parseUrlPiece = parseQueryParam
359+ toText :: a -> Text
333− -- | Parse HTTP header value.
362+And as long as the type that a `Capture`/`QueryParam`/`Header`/etc will be
334− parseHeader :: ByteString -> Either Text a
363+decoded to provides a `FromText` instance, it will Just Work. *servant*
335− parseHeader = parseUrlPiece . decodeUtf8
364+provides a decent number of instances, but here are some examples of defining
337− -- | Parse query param value.
367+``` haskell
338− parseQueryParam :: Text -> Either Text a
368+-- A typical enumeration
339− parseQueryParam = parseUrlPiece
369+data Direction
375+newtype UserId = UserId Int64
342−As you can see, as long as you provide either `parseUrlPiece` (for `Capture`s)
378+or writing the instances by hand:
343−or `parseQueryParam` (for `QueryParam`s), the other methods will be defined in
346−**http-api-data** provides a decent number of instances, helpers for defining new
380+``` haskell ignore
347−ones, and wonderful documentation.
381+instance FromText UserId where
382+ fromText = fmap UserId fromText
384+instance ToText UserId where
385+ toText (UserId i) = toText i
349There's not much else to say about these classes. You will need instances for
388There's not much else to say about these classes. You will need instances for
350−them when using `Capture`, `QueryParam`, `QueryParams`, and `Header` with your
389+them when using `Capture`, `QueryParam`, `QueryParams`, `MatrixParam`,
351−types. You will need `FromHttpApiData` instances for server-side request
390+`MatrixParams` and `Header` with your types. You will need `FromText` instances
352−handlers and `ToHttpApiData` instances only when using
391+for server-side request handlers and `ToText` instances only when using
353−**servant-client**, as described in the [section about deriving haskell
392+*servant-client*, as described in the [section about deriving haskell
354−functions to query an API](Client.html).
393+functions to query an API](/tutorial/client.html).
356−## Using content-types with your data types
395+Using content-types with your data types
396+========================================
358The same principle was operating when decoding request bodies from JSON, and
398The same principle was operating when decoding request bodies from JSON, and
359responses *into* JSON. (JSON is just the running example - you can do this with
399responses *into* JSON. (JSON is just the running example - you can do this with
360any content-type.)
400any content-type.)
362−This section introduces a couple of typeclasses provided by **servant** that make
402+This section introduces a couple of typeclasses provided by *servant* that make
363all of this work.
403all of this work.
365−### The truth behind `JSON`
405+The truth behind `JSON`
366−
406+-----------------------
368−What exactly is `JSON` (the type as used in `Get '[JSON] User`)? Like the 3
369−other content-types provided out of the box by **servant**, it's a really dumb
408+What exactly is `JSON`? Like the 3 other content types provided out of the box
370−data type.
409+by *servant*, it's a really dumb data type.
372``` haskell ignore
411``` haskell ignore
@@ -377,15 +416,14 @@ data OctetStream
379Obviously, this is not all there is to `JSON`, otherwise it would be quite
418Obviously, this is not all there is to `JSON`, otherwise it would be quite
380−pointless. Like most of the data types in **servant**, `JSON` is mostly there as
419+pointless. Like most of the data types in *servant*, `JSON` is mostly there as
381a special *symbol* that's associated with encoding (resp. decoding) to (resp.
420a special *symbol* that's associated with encoding (resp. decoding) to (resp.
382from) the *JSON* format. The way this association is performed can be
421from) the *JSON* format. The way this association is performed can be
383decomposed into two steps.
422decomposed into two steps.
385The first step is to provide a proper
424The first step is to provide a proper
387−[**http-media**](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html))
425+[`MediaType`](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html)
388−representation for `JSON`, or for your own content-types. If you look at the
426+representation for `JSON`, or for your own content types. If you look at the
389haddocks from this link, you can see that we just have to specify
427haddocks from this link, you can see that we just have to specify
390`application/json` using the appropriate functions. In our case, we can just
428`application/json` using the appropriate functions. In our case, we can just
391use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
429use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
@@ -401,14 +439,14 @@ instance Accept JSON where
403The second step is centered around the `MimeRender` and `MimeUnrender` classes.
441The second step is centered around the `MimeRender` and `MimeUnrender` classes.
404−These classes just let you specify a way to encode and decode
442+These classes just let you specify a way to respectively encode and decode
405−values into or from your content-type's representation.
443+values respectively into or from your content-type's representation.
407``` haskell ignore
445``` haskell ignore
408class Accept ctype => MimeRender ctype a where
446class Accept ctype => MimeRender ctype a where
409− mimeRender :: Proxy ctype -> a -> ByteString
447+ mimeRender :: Proxy ctype -> a -> ByteString
410 -- alternatively readable as:
448 -- alternatively readable as:
411− mimeRender :: Proxy ctype -> (a -> ByteString)
449+ mimeRender :: Proxy ctype -> (a -> ByteString)
414Given a content-type and some user type, `MimeRender` provides a function that
452Given a content-type and some user type, `MimeRender` provides a function that
@@ -429,12 +467,14 @@ And now the `MimeUnrender` class, which lets us extract values from lazy
429``` haskell ignore
467``` haskell ignore
430class Accept ctype => MimeUnrender ctype a where
468class Accept ctype => MimeUnrender ctype a where
431 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
469 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
471+ mimeUnrender :: Proxy ctype -> (ByteString -> Either String a)
434We don't have much work to do there either, `Data.Aeson.eitherDecode` is
474We don't have much work to do there either, `Data.Aeson.eitherDecode` is
435precisely what we need. However, it only allows arrays and objects as toplevel
475precisely what we need. However, it only allows arrays and objects as toplevel
436JSON values and this has proven to get in our way more than help us so we wrote
476JSON values and this has proven to get in our way more than help us so we wrote
437−our own little function around **aeson** and **attoparsec** that allows any type of
477+our own little function around *aeson* and *attoparsec* that allows any type of
438JSON value at the toplevel of a "JSON document". Here's the definition in case
478JSON value at the toplevel of a "JSON document". Here's the definition in case
439you are curious.
479you are curious.
@@ -452,20 +492,21 @@ instance FromJSON a => MimeUnrender JSON a where
452 mimeUnrender _ = eitherDecodeLenient
492 mimeUnrender _ = eitherDecodeLenient
455−And this is all the code that lets you use `JSON` with `ReqBody`, `Get`,
495+And this is all the code that lets you use `JSON` for with `ReqBody`, `Get`,
456`Post` and friends. We can check our understanding by implementing support
496`Post` and friends. We can check our understanding by implementing support
457−for an `HTML` content-type, so that users of your webservice can access an
497+for an `HTML` content type, so that users of your webservice can access an
458HTML representation of the data they want, ready to be included in any HTML
498HTML representation of the data they want, ready to be included in any HTML
459−document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/),
499+document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/), simply by adding `Accept:
460−simply by adding `Accept: text/html` to their request headers.
500+text/html` to their request headers.
462−### Case-studies: **servant-blaze** and **servant-lucid**
502+Case-studies: *servant-blaze* and *servant-lucid*
503+-------------------------------------------------
464These days, most of the haskellers who write their HTML UIs directly from
505These days, most of the haskellers who write their HTML UIs directly from
465−Haskell use either [**blaze-html**](http://hackage.haskell.org/package/blaze-html)
506+Haskell use either [blaze-html](http://hackage.haskell.org/package/blaze-html)
466−or [**lucid**](http://hackage.haskell.org/package/lucid). The best option for
507+or [lucid](http://hackage.haskell.org/package/lucid). The best option for
467−**servant** is obviously to support both (and hopefully other templating
508+*servant* is obviously to support both (and hopefully other templating
468−solutions!). We're first going to look at **lucid**:
509+solutions!).
470``` haskell
511``` haskell
471data HTMLLucid
512data HTMLLucid
@@ -473,20 +514,24 @@ data HTMLLucid
474Once again, the data type is just there as a symbol for the encoding/decoding
515Once again, the data type is just there as a symbol for the encoding/decoding
475functions, except that this time we will only worry about encoding since
516functions, except that this time we will only worry about encoding since
476−**lucid** doesn't provide a way to extract data from HTML.
517+*blaze-html* and *lucid* don't provide a way to extract data from HTML.
519+Both packages also have the same `Accept` instance for their `HTMLLucid` type.
478``` haskell
521``` haskell
479instance Accept HTMLLucid where
522instance Accept HTMLLucid where
480 contentType _ = "text" // "html" /: ("charset", "utf-8")
523 contentType _ = "text" // "html" /: ("charset", "utf-8")
483−Note that this instance uses the `(/:)` operator from **http-media** which lets
526+Note that this instance uses the `(/:)` operator from *http-media* which lets
484us specify additional information about a content-type, like the charset here.
527us specify additional information about a content-type, like the charset here.
486−The rendering instances call similar functions that take
529+The rendering instances for both packages both call similar functions that take
487types with an appropriate instance to an "abstract" HTML representation and
530types with an appropriate instance to an "abstract" HTML representation and
488then write that to a `ByteString`.
531then write that to a `ByteString`.
490``` haskell
535``` haskell
491instance ToHtml a => MimeRender HTMLLucid a where
536instance ToHtml a => MimeRender HTMLLucid a where
492 mimeRender _ = renderBS . toHtml
537 mimeRender _ = renderBS . toHtml
@@ -497,7 +542,7 @@ instance MimeRender HTMLLucid (Html a) where
497 mimeRender _ = renderBS
542 mimeRender _ = renderBS
500−For **blaze-html** everything works very similarly:
545+For *blaze-html*:
502``` haskell
547``` haskell
503-- For this tutorial to compile 'HTMLLucid' and 'HTMLBlaze' have to be
548-- For this tutorial to compile 'HTMLLucid' and 'HTMLBlaze' have to be
@@ -517,13 +562,15 @@ instance MimeRender HTMLBlaze Text.Blaze.Html.Html where
517 mimeRender _ = renderHtml
562 mimeRender _ = renderHtml
520−Both [**servant-blaze**](http://hackage.haskell.org/package/servant-blaze) and
565+Both [servant-blaze](http://hackage.haskell.org/package/servant-blaze) and
521−[**servant-lucid**](http://hackage.haskell.org/package/servant-lucid) let you use
566+[servant-lucid](http://hackage.haskell.org/package/servant-lucid) let you use
522−`HTMLLucid` and `HTMLBlaze` in any content-type list as long as you provide an instance of the
567+`HTMLLucid` in any content type list as long as you provide an instance of the
523−appropriate class (`ToMarkup` for **blaze-html**, `ToHtml` for **lucid**).
568+appropriate class (`ToMarkup` for *blaze-html*, `ToHtml` for *lucid*).
570+We can now write webservice that uses *servant-lucid* to show the `HTMLLucid`
571+content type in action. First off, imports and pragmas as usual.
525−We can now write a webservice that uses **servant-lucid** to show the `HTMLLucid`
526−content-type in action. We will be serving the following API:
573+We will be serving the following API:
528``` haskell
575``` haskell
529type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
576type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
@@ -540,7 +587,7 @@ data Person = Person
540instance ToJSON Person
587instance ToJSON Person
543−Now, let's teach **lucid** how to render a `Person` as a row in a table, and then
590+Now, let's teach *lucid* how to render a `Person` as a row in a table, and then
544a list of `Person`s as a table with a row per person.
591a list of `Person`s as a table with a row per person.
546``` haskell
593``` haskell
@@ -571,8 +618,8 @@ instance ToHtml [Person] where
571We create some `Person` values and serve them as a list:
618We create some `Person` values and serve them as a list:
573``` haskell
620``` haskell
574−people :: [Person]
621+persons :: [Person]
575−people =
622+persons =
576 [ Person "Isaac" "Newton"
623 [ Person "Isaac" "Newton"
577 , Person "Albert" "Einstein"
624 , Person "Albert" "Einstein"
@@ -581,69 +628,75 @@ personAPI :: Proxy PersonAPI
581personAPI = Proxy
628personAPI = Proxy
583server4 :: Server PersonAPI
630server4 :: Server PersonAPI
584−server4 = return people
631+server4 = return persons
586app2 :: Application
633app2 :: Application
587−app2 = serve personAPI server4
634+app2 = serve personAPI EmptyConfig server4
590−And we're good to go:
637+And we're good to go. You can run this example with `dist/build/tutorial/tutorial 4`.
593−$ curl http://localhost:8081/persons
640+ $ curl http://localhost:8081/persons
594−[{"lastName":"Newton","firstName":"Isaac"},{"lastName":"Einstein","firstName":"Albert"}]
641+ [{"lastName":"Newton","firstName":"Isaac"},{"lastName":"Einstein","firstName":"Albert"}]
595−$ curl -H 'Accept: text/html' http://localhost:8081/persons
642+ $ curl -H 'Accept: text/html' http://localhost:8081/persons
596−<table><tr><td>first name</td><td>last name</td></tr><tr><td>Isaac</td><td>Newton</td></tr><tr><td>Albert</td><td>Einstein</td></tr></table>
643+ <table><tr><td>first name</td><td>last name</td></tr><tr><td>Isaac</td><td>Newton</td></tr><tr><td>Albert</td><td>Einstein</td></tr></table>
597−# or just point your browser to http://localhost:8081/persons
644+ # or just point your browser to http://localhost:8081/persons
600−## The `Handler` monad
647+The `ExceptT ServantErr IO` monad
648+=================================
602−At the heart of the handlers is the monad they run in, namely a newtype `Handler` around `ExceptT ServerError IO`
650+At the heart of the handlers is the monad they run in, namely `ExceptT
603−([haddock documentation for `ExceptT`](http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT)).
604−One might wonder: why this monad? The answer is that it is the
651+ServantErr IO`. One might wonder: why this monad? The answer is that it is the
605simplest monad with the following properties:
652simplest monad with the following properties:
607−- it lets us both return a successful result (using `return`)
654+- it lets us both return a successful result (with the `Right` branch of
608−or "fail" with a descriptive error (using `throwError`);
655+`Either`) or "fail" with a descriptive error (with the `Left` branch of
609- it lets us perform IO, which is absolutely vital since most webservices exist
657- it lets us perform IO, which is absolutely vital since most webservices exist
610−as interfaces to databases that we interact with in `IO`.
658+as interfaces to databases that we interact with in `IO`;
612Let's recall some definitions.
660Let's recall some definitions.
614``` haskell ignore
662``` haskell ignore
664+data Either e a = Left e | Right a
615−-- from the 'mtl' package at
666+-- from the 'either' package at
667+-- http://hackage.haskell.org/package/either-4.3.3.2/docs/Control-Monad-Trans-Either.html
616−newtype ExceptT e m a = ExceptT (m (Either e a))
668+newtype ExceptT e m a
669+ = ExceptT { runEitherT :: m (Either e a) }
619−In short, this means that a handler of type `Handler a` is simply
672+In short, this means that a handler of type `ExceptT ServantErr IO a` is simply
620−equivalent to a computation of type `IO (Either ServerError a)`, that is, an IO
673+equivalent to a computation of type `IO (Either ServantErr a)`, that is, an IO
621action that either returns an error or a result.
674action that either returns an error or a result.
623−The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl/docs/Control-Monad-Except.html#t:ExceptT)
676+The aforementioned `either` package is worth taking a look at. Perhaps most
624−from which `ExceptT` comes is worth looking at.
677+importantly:
625−Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so
678+
680+left :: Monad m => e -> ExceptT e m a
626−`throwError` can be used to return an error from your handler (whereas `return`
682+Allows you to return an error from your handler (whereas `return` is enough to
627− is enough to return a success).
683+return a success).
629Most of what you'll be doing in your handlers is running some IO and,
685Most of what you'll be doing in your handlers is running some IO and,
630depending on the result, you might sometimes want to throw an error of some
686depending on the result, you might sometimes want to throw an error of some
631kind and abort early. The next two sections cover how to do just that.
687kind and abort early. The next two sections cover how to do just that.
633−### Performing IO
689+Performing IO
635−Other important instances from the list above are `MonadIO m => MonadIO
636−(ExceptT e m)`, and therefore also `MonadIO Handler` as there is a `MonadIO IO` instance.
637−[`MonadIO`](http://hackage.haskell.org/package/base/docs/Control-Monad-IO-Class.html#t:MonadIO)
692+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:
638−is a class from the **transformers** package defined as:
640``` haskell ignore
694``` haskell ignore
641class Monad m => MonadIO m where
695class Monad m => MonadIO m where
642 liftIO :: IO a -> m a
696 liftIO :: IO a -> m a
645−So if you want to run any kind of
646−IO computation in your handlers, just use `liftIO`:
699+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`:
648``` haskell
701``` haskell
649type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
702type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
@@ -660,16 +713,17 @@ server5 = do
660 return (FileContent filecontent)
713 return (FileContent filecontent)
663−### Failing, through `ServerError`
716+Failing, through `ServantErr`
717+-----------------------------
665If you want to explicitly fail at providing the result promised by an endpoint
719If you want to explicitly fail at providing the result promised by an endpoint
666using the appropriate HTTP status code (not found, unauthorized, etc) and some
720using the appropriate HTTP status code (not found, unauthorized, etc) and some
667−error message, all you have to do is use the `throwError` function mentioned above
721+error message, all you have to do is use the `left` function mentioned above
668−and provide it with the appropriate value of type `ServerError`, which is
722+and provide it with the appropriate value of type `ServantErr`, which is
669defined as:
723defined as:
671``` haskell ignore
725``` haskell ignore
672−data ServerError = ServerError
726+data ServantErr = ServantErr
673 { errHTTPCode :: Int
727 { errHTTPCode :: Int
674 , errReasonPhrase :: String
728 , errReasonPhrase :: String
675 , errBody :: ByteString -- lazy bytestring
729 , errBody :: ByteString -- lazy bytestring
@@ -682,10 +736,10 @@ module. If you want to use these values but add a body or some headers, just
682use record update syntax:
736use record update syntax:
684``` haskell
738``` haskell
685−failingHandler :: Handler ()
739+failingHandler :: ExceptT ServantErr IO ()
686−failingHandler = throwError myerr
740+failingHandler = throwE myerr
688− where myerr :: ServerError
742+ where myerr :: ServantErr
689 myerr = err503 { errBody = "Sorry dear user." }
743 myerr = err503 { errBody = "Sorry dear user." }
@@ -698,47 +752,48 @@ server6 = do
698 exists <- liftIO (doesFileExist "myfile.txt")
752 exists <- liftIO (doesFileExist "myfile.txt")
699 if exists
753 if exists
700 then liftIO (readFile "myfile.txt") >>= return . FileContent
754 then liftIO (readFile "myfile.txt") >>= return . FileContent
701− else throwError custom404Err
755+ else throwE custom404Err
703 where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }
757 where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }
706−Here's how that server looks in action:
760+Let's run this server (`dist/build/tutorial/tutorial 5`) and
761+query it, first without the file and then with the file.
709−$ curl --verbose http://localhost:8081/myfile.txt
764+ $ curl --verbose http://localhost:8081/myfile.txt
711−* Connected to localhost (127.0.0.1) port 8081 (#0)
766+ * Connected to localhost (127.0.0.1) port 8081 (#0)
712−> GET /myfile.txt HTTP/1.1
767+ > GET /myfile.txt HTTP/1.1
713−> User-Agent: curl/7.30.0
768+ > User-Agent: curl/7.30.0
714−> Host: localhost:8081
769+ > Host: localhost:8081
715−> Accept: */*
770+ > Accept: */*
717−< HTTP/1.1 404 Not Found
772+ < HTTP/1.1 404 Not Found
719−myfile.txt just isn't there, please leave this server alone.
774+ myfile.txt just isnt there, please leave this server alone.
721−$ echo Hello > myfile.txt
776+ $ echo Hello > myfile.txt
723−$ curl --verbose http://localhost:8081/myfile.txt
778+ $ curl --verbose http://localhost:8081/myfile.txt
725−* Connected to localhost (127.0.0.1) port 8081 (#0)
780+ * Connected to localhost (127.0.0.1) port 8081 (#0)
726−> GET /myfile.txt HTTP/1.1
781+ > GET /myfile.txt HTTP/1.1
727−> User-Agent: curl/7.30.0
782+ > User-Agent: curl/7.30.0
728−> Host: localhost:8081
783+ > Host: localhost:8081
729−> Accept: */*
784+ > Accept: */*
731−< HTTP/1.1 200 OK
786+ < HTTP/1.1 200 OK
733−< Content-Type: application/json
788+ < Content-Type: application/json
735−{"content":"Hello\n"}
790+ {"content":"Hello\n"}
738−## Response headers
793+Response headers
740−To add headers to your response, use
795+
741−[addHeader](http://hackage.haskell.org/package/servant/docs/Servant-API-ResponseHeaders.html).
796+To add headers to your response, use [addHeader](http://hackage.haskell.org/package/servant-0.4.4/docs/Servant-API-ResponseHeaders.html).
742Note that this changes the type of your API, as we can see in the following example:
797Note that this changes the type of your API, as we can see in the following example:
744``` haskell
799``` haskell
@@ -748,89 +803,158 @@ myHandler :: Server MyHandler
748myHandler = return $ addHeader 1797 albert
803myHandler = return $ addHeader 1797 albert
751−Note that the type of `addHeader header x` is different than the type of `x`!
752−And if you add more headers, more headers will appear in the header list:
755−type MyHeadfulHandler = Get '[JSON] (Headers '[Header "X-A-Bool" Bool, Header "X-An-Int" Int] User)
757−myHeadfulHandler :: Server MyHeadfulHandler
758−myHeadfulHandler = return $ addHeader True $ addHeader 1797 albert
761−But what if your handler only *sometimes* adds a header? If you declare that
762−your handler adds headers, and you don't add one, the return type of your
763−handler will be different than expected. To solve this, you have to explicitly
764−*not* add a header by using `noHeader`:
766−``` haskell
807+Serving static files
767−type MyMaybeHeaderHandler
808+====================
768− = Capture "withHeader" Bool :> Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
770−myMaybeHeaderHandler :: Server MyMaybeHeaderHandler
771−myMaybeHeaderHandler x = return $ if x then addHeader 1797 albert
772− else noHeader albert
775−## Serving static files
777−**servant-server** also provides a way to just serve the content of a directory
810+*servant-server* also provides a way to just serve the content of a directory
778under some path in your web API. As mentioned earlier in this document, the
811under some path in your web API. As mentioned earlier in this document, the
779`Raw` combinator can be used in your APIs to mean "plug here any WAI
812`Raw` combinator can be used in your APIs to mean "plug here any WAI
780−application". Well, **servant-server** provides a function to get a file and
813+application". Well, servant-server provides a function to get a file and
781directory serving WAI application, namely:
814directory serving WAI application, namely:
783``` haskell ignore
816``` haskell ignore
784-- exported by Servant and Servant.Server
817-- exported by Servant and Servant.Server
785−serveDirectoryWebApp :: FilePath -> Server Raw
818+serveDirectory :: FilePath -> Server Raw
788−`serveDirectoryWebApp`'s argument must be a path to a valid directory.
821+`serveDirectory`'s argument must be a path to a valid directory. You can see an
822+example below, runnable with `dist/build/tutorial/tutorial 6`
823+(you **must** run it from within the *servant-examples/* directory!), which is
824+a webserver that serves the various bits of code covered in this
790−Here's an example API that will serve some static files:
827+The API type will be the following.
792``` haskell
829``` haskell
793−type StaticAPI = "static" :> Raw
830+type CodeAPI = "code" :> Raw
796And the server:
833And the server:
798``` haskell
835``` haskell
799−staticAPI :: Proxy StaticAPI
836+codeAPI :: Proxy CodeAPI
800−staticAPI = Proxy
837+codeAPI = Proxy
803``` haskell
840``` haskell
804−server7 :: Server StaticAPI
841+server7 :: Server CodeAPI
805−server7 = serveDirectoryWebApp "static-files"
842+server7 = serveDirectory "tutorial"
807app3 :: Application
844app3 :: Application
808−app3 = serve staticAPI server7
845+app3 = serve codeAPI EmptyConfig server7
811−This server will match any request whose path starts with `/static` and will look
812−for a file at the path described by the rest of the request path, inside the
848+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.
813− *static-files/* directory of the path you run the program from.
815−In other words: If a client requests `/static/foo.txt`, the server will look for a file at
850+In other words:
816−`./static-files/foo.txt`. If that file exists it'll succeed and serve the file.
817−If it doesn't exist, the handler will fail with a `404` status code.
819−`serveDirectoryWebApp` uses some standard settings that fit the use case of
852+- If a client requests `/code/foo.txt`, the server will look for a file at `./tutorial/foo.txt` (and fail)
820−serving static files for most web apps. You can find out about the other
853+- If a client requests `/code/T1.hs`, the server will look for a file at `./tutorial/T1.hs` (and succeed)
821−options in the documentation of the `Servant.Server.StaticFiles` module.
854+- If a client requests `/code/foo/bar/baz/movie.mp4`, the server will look for a file at `./tutorial/foo/bar/baz/movie.mp4` (and fail)
823−## Nested APIs
856+Here is our little server in action.
859+$ curl http://localhost:8081/code/T1.hs
860+{-# LANGUAGE DataKinds #-}
861+{-# LANGUAGE TypeFamilies #-}
862+{-# LANGUAGE DeriveGeneric #-}
863+{-# LANGUAGE TypeOperators #-}
867+import Data.Time.Calendar
876+ , registration_date :: Day
877+ } deriving (Eq, Show, Generic)
879+-- orphan ToJSON instance for Day. necessary to derive one for User
880+instance ToJSON Day where
881+ -- display a day in YYYY-mm-dd format
882+ toJSON d = toJSON (showGregorian d)
886+type UserAPI = "users" :> Get '[JSON] [User]
890+ [ User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
891+ , User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
894+userAPI :: Proxy UserAPI
897+server :: Server UserAPI
898+server = return users
901+app = serve userAPI server
902+$ curl http://localhost:8081/code/tutorial.hs
904+import Network.Wai.Handler.Warp
905+import System.Environment
917+app :: String -> (Application -> IO ()) -> IO ()
927+ "9" -> T9.writeJSFiles >> f T9.app
935+ [n] -> app n (run 8081)
940+ putStrLn "Usage:\t tutorial N"
941+ putStrLn "\t\twhere N is the number of the example you want to run."
943+$ curl http://localhost:8081/foo
825−Let's see how you can define APIs in a modular way, while avoiding repetition.
950+Let's see how you can define APIs in a modular way, while avoiding repetition. Consider this simple example:
826−Consider this simple example:
828``` haskell
952``` haskell
829type UserAPI3 = -- view the user with given userid, in JSON
953type UserAPI3 = -- view the user with given userid, in JSON
830 Capture "userid" Int :> Get '[JSON] User
954 Capture "userid" Int :> Get '[JSON] User
832 :<|> -- delete the user with given userid. empty response
956 :<|> -- delete the user with given userid. empty response
833− Capture "userid" Int :> DeleteNoContent
957+ Capture "userid" Int :> Delete '[] ()
836We can instead factor out the `userid`:
960We can instead factor out the `userid`:
@@ -838,34 +962,32 @@ We can instead factor out the `userid`:
838``` haskell
962``` haskell
839type UserAPI4 = Capture "userid" Int :>
963type UserAPI4 = Capture "userid" Int :>
840 ( Get '[JSON] User
964 ( Get '[JSON] User
841− :<|> DeleteNoContent
965+ :<|> Delete '[] ()
845−However, you have to be aware that this has an effect on the type of the
969+However, you have to be aware that this has an effect on the type of the corresponding `Server`:
846−corresponding `Server`:
848``` haskell ignore
971``` haskell ignore
849−Server UserAPI3 = (Int -> Handler User)
972+Server UserAPI3 = (Int -> ExceptT ServantErr IO User)
850− :<|> (Int -> Handler NoContent)
973+ :<|> (Int -> ExceptT ServantErr IO ())
852−Server UserAPI4 = Int -> ( Handler User
975+Server UserAPI4 = Int -> ( ExceptT ServantErr IO User
853− :<|> Handler NoContent
976+ :<|> ExceptT ServantErr IO ()
857In the first case, each handler receives the *userid* argument. In the latter,
980In the first case, each handler receives the *userid* argument. In the latter,
858−the whole `Server` takes the *userid* and has handlers that are just
981+the whole `Server` takes the *userid* and has handlers that are just computations in `ExceptT`, with no arguments. In other words:
859−computations in `Handler`, with no arguments. In other words:
861``` haskell
983``` haskell
862server8 :: Server UserAPI3
984server8 :: Server UserAPI3
863server8 = getUser :<|> deleteUser
985server8 = getUser :<|> deleteUser
865− where getUser :: Int -> Handler User
987+ where getUser :: Int -> ExceptT ServantErr IO User
866 getUser _userid = error "..."
988 getUser _userid = error "..."
868− deleteUser :: Int -> Handler NoContent
990+ deleteUser :: Int -> ExceptT ServantErr IO ()
869 deleteUser _userid = error "..."
991 deleteUser _userid = error "..."
871-- notice how getUser and deleteUser
993-- notice how getUser and deleteUser
@@ -874,17 +996,14 @@ server8 = getUser :<|> deleteUser
874server9 :: Server UserAPI4
996server9 :: Server UserAPI4
875server9 userid = getUser userid :<|> deleteUser userid
997server9 userid = getUser userid :<|> deleteUser userid
877− where getUser :: Int -> Handler User
999+ where getUser :: Int -> ExceptT ServantErr IO User
878 getUser = error "..."
1000 getUser = error "..."
880− deleteUser :: Int -> Handler NoContent
1002+ deleteUser :: Int -> ExceptT ServantErr IO ()
881 deleteUser = error "..."
1003 deleteUser = error "..."
884−Note that there's nothing special about `Capture` that lets you "factor it
1006+Note that there's nothing special about `Capture` that lets you "factor it out": this can be done with any combinator. Here are a few examples of APIs with a combinator factored out for which we can write a perfectly valid `Server`.
885−out": this can be done with any combinator. Here are a few examples of APIs
886−with a combinator factored out for which we can write a perfectly valid
889``` haskell
1008``` haskell
890-- we just factor out the "users" path fragment
1009-- we just factor out the "users" path fragment
@@ -896,63 +1015,62 @@ type API1 = "users" :>
896-- we factor out the Request Body
1015-- we factor out the Request Body
897type API2 = ReqBody '[JSON] User :>
1016type API2 = ReqBody '[JSON] User :>
898 ( Get '[JSON] User -- just display the same user back, don't register it
1017 ( Get '[JSON] User -- just display the same user back, don't register it
899− :<|> PostNoContent -- register the user. empty response
1018+ :<|> Post '[JSON] () -- register the user. empty response
902-- we factor out a Header
1021-- we factor out a Header
903type API3 = Header "Authorization" Token :>
1022type API3 = Header "Authorization" Token :>
904 ( Get '[JSON] SecretData -- get some secret data, if authorized
1023 ( Get '[JSON] SecretData -- get some secret data, if authorized
905− :<|> ReqBody '[JSON] SecretData :> PostNoContent -- add some secret data, if authorized
1024+ :<|> ReqBody '[JSON] SecretData :> Post '[] () -- add some secret data, if authorized
908newtype Token = Token ByteString
1027newtype Token = Token ByteString
909newtype SecretData = SecretData ByteString
1028newtype SecretData = SecretData ByteString
912−This approach lets you define APIs modularly and assemble them all into one big
1031+This approach lets you define APIs modularly and assemble them all into one big API type only at the end.
913−API type only at the end.
915``` haskell
1033``` haskell
916type UsersAPI =
1034type UsersAPI =
917 Get '[JSON] [User] -- list users
1035 Get '[JSON] [User] -- list users
918− :<|> ReqBody '[JSON] User :> PostNoContent -- add a user
1036+ :<|> ReqBody '[JSON] User :> Post '[] () -- add a user
919 :<|> Capture "userid" Int :>
1037 :<|> Capture "userid" Int :>
920 ( Get '[JSON] User -- view a user
1038 ( Get '[JSON] User -- view a user
921− :<|> ReqBody '[JSON] User :> PutNoContent -- update a user
1039+ :<|> ReqBody '[JSON] User :> Put '[] () -- update a user
922− :<|> DeleteNoContent -- delete a user
1040+ :<|> Delete '[] () -- delete a user
925usersServer :: Server UsersAPI
1043usersServer :: Server UsersAPI
926usersServer = getUsers :<|> newUser :<|> userOperations
1044usersServer = getUsers :<|> newUser :<|> userOperations
928− where getUsers :: Handler [User]
1046+ where getUsers :: ExceptT ServantErr IO [User]
929 getUsers = error "..."
1047 getUsers = error "..."
931− newUser :: User -> Handler NoContent
1049+ newUser :: User -> ExceptT ServantErr IO ()
932 newUser = error "..."
1050 newUser = error "..."
934 userOperations userid =
1052 userOperations userid =
935 viewUser userid :<|> updateUser userid :<|> deleteUser userid
1053 viewUser userid :<|> updateUser userid :<|> deleteUser userid
938− viewUser :: Int -> Handler User
1056+ viewUser :: Int -> ExceptT ServantErr IO User
939 viewUser = error "..."
1057 viewUser = error "..."
941− updateUser :: Int -> User -> Handler NoContent
1059+ updateUser :: Int -> User -> ExceptT ServantErr IO ()
942 updateUser = error "..."
1060 updateUser = error "..."
944− deleteUser :: Int -> Handler NoContent
1062+ deleteUser :: Int -> ExceptT ServantErr IO ()
945 deleteUser = error "..."
1063 deleteUser = error "..."
948``` haskell
1066``` haskell
949type ProductsAPI =
1067type ProductsAPI =
950 Get '[JSON] [Product] -- list products
1068 Get '[JSON] [Product] -- list products
951− :<|> ReqBody '[JSON] Product :> PostNoContent -- add a product
1069+ :<|> ReqBody '[JSON] Product :> Post '[] () -- add a product
952 :<|> Capture "productid" Int :>
1070 :<|> Capture "productid" Int :>
953 ( Get '[JSON] Product -- view a product
1071 ( Get '[JSON] Product -- view a product
954− :<|> ReqBody '[JSON] Product :> PutNoContent -- update a product
1072+ :<|> ReqBody '[JSON] Product :> Put '[] () -- update a product
955− :<|> DeleteNoContent -- delete a product
1073+ :<|> Delete '[] () -- delete a product
958data Product = Product { productId :: Int }
1076data Product = Product { productId :: Int }
@@ -960,23 +1078,23 @@ data Product = Product { productId :: Int }
960productsServer :: Server ProductsAPI
1078productsServer :: Server ProductsAPI
961productsServer = getProducts :<|> newProduct :<|> productOperations
1079productsServer = getProducts :<|> newProduct :<|> productOperations
963− where getProducts :: Handler [Product]
1081+ where getProducts :: ExceptT ServantErr IO [Product]
964 getProducts = error "..."
1082 getProducts = error "..."
966− newProduct :: Product -> Handler NoContent
1084+ newProduct :: Product -> ExceptT ServantErr IO ()
967 newProduct = error "..."
1085 newProduct = error "..."
969 productOperations productid =
1087 productOperations productid =
970 viewProduct productid :<|> updateProduct productid :<|> deleteProduct productid
1088 viewProduct productid :<|> updateProduct productid :<|> deleteProduct productid
973− viewProduct :: Int -> Handler Product
1091+ viewProduct :: Int -> ExceptT ServantErr IO Product
974 viewProduct = error "..."
1092 viewProduct = error "..."
976− updateProduct :: Int -> Product -> Handler NoContent
1094+ updateProduct :: Int -> Product -> ExceptT ServantErr IO ()
977 updateProduct = error "..."
1095 updateProduct = error "..."
979− deleteProduct :: Int -> Handler NoContent
1097+ deleteProduct :: Int -> ExceptT ServantErr IO ()
980 deleteProduct = error "..."
1098 deleteProduct = error "..."
@@ -988,213 +1106,139 @@ server10 :: Server CombinedAPI
988server10 = usersServer :<|> productsServer
1106server10 = usersServer :<|> productsServer
991−Finally, we can realize the user and product APIs are quite similar and
1109+Finally, we can realize the user and product APIs are quite similar and abstract that away:
994``` haskell
1111``` haskell
995-- API for values of type 'a'
1112-- API for values of type 'a'
996-- indexed by values of type 'i'
1113-- indexed by values of type 'i'
997type APIFor a i =
1114type APIFor a i =
998 Get '[JSON] [a] -- list 'a's
1115 Get '[JSON] [a] -- list 'a's
999− :<|> ReqBody '[JSON] a :> PostNoContent -- add an 'a'
1116+ :<|> ReqBody '[JSON] a :> Post '[] () -- add an 'a'
1000 :<|> Capture "id" i :>
1117 :<|> Capture "id" i :>
1001 ( Get '[JSON] a -- view an 'a' given its "identifier" of type 'i'
1118 ( Get '[JSON] a -- view an 'a' given its "identifier" of type 'i'
1002− :<|> ReqBody '[JSON] a :> PutNoContent -- update an 'a'
1119+ :<|> ReqBody '[JSON] a :> Put '[] () -- update an 'a'
1003− :<|> DeleteNoContent -- delete an 'a'
1120+ :<|> Delete '[] () -- delete an 'a'
1006-- Build the appropriate 'Server'
1123-- Build the appropriate 'Server'
1007-- given the handlers of the right type.
1124-- given the handlers of the right type.
1008−serverFor :: Handler [a] -- handler for listing of 'a's
1125+serverFor :: ExceptT ServantErr IO [a] -- handler for listing of 'a's
1009− -> (a -> Handler NoContent) -- handler for adding an 'a'
1126+ -> (a -> ExceptT ServantErr IO ()) -- handler for adding an 'a'
1010− -> (i -> Handler a) -- handler for viewing an 'a' given its identifier of type 'i'
1127+ -> (i -> ExceptT ServantErr IO a) -- handler for viewing an 'a' given its identifier of type 'i'
1011− -> (i -> a -> Handler NoContent) -- updating an 'a' with given id
1128+ -> (i -> a -> ExceptT ServantErr IO ()) -- updating an 'a' with given id
1012− -> (i -> Handler NoContent) -- deleting an 'a' given its id
1129+ -> (i -> ExceptT ServantErr IO ()) -- deleting an 'a' given its id
1013 -> Server (APIFor a i)
1130 -> Server (APIFor a i)
1014serverFor = error "..."
1131serverFor = error "..."
1015-- implementation left as an exercise. contact us on IRC
1132-- implementation left as an exercise. contact us on IRC
1016-- or the mailing list if you get stuck!
1133-- or the mailing list if you get stuck!
1019−When your API contains the `EmptyAPI` combinator, you'll want to use
1136+Using another monad for your handlers
1020−`emptyServer` in the corresponding slot for your server, which will simply fail
1137+=====================================
1021−with 404 whenever a request reaches it:
1024−type CombinedAPI2 = API :<|> "empty" :> EmptyAPI
1026−server11 :: Server CombinedAPI2
1027−server11 = server3 :<|> emptyServer
1030−## Using another monad for your handlers
1032−Remember how `Server` turns combinators for HTTP methods into `Handler`? Well, actually, there's more to that. `Server` is actually a
1139+Remember 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.
1033−simple type synonym.
1035``` haskell ignore
1141``` haskell ignore
1036−type Server api = ServerT api Handler
1142+type Server api = ServerT api (ExceptT ServantErr IO)
1039−`ServerT` is the actual type family that computes the required types for the
1145+`ServerT` is the actual type family that computes the required types for the handlers that's part of the `HasServer` class. It's like `Server` except that it takes a third parameter which is the monad you want your handlers to run in, or more generally the return types of your handlers. This third parameter is used for specifying the return type of the handler for an endpoint, e.g when computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be `SomeMonad Person`.
1040−handlers that's part of the `HasServer` class. It's like `Server` except that
1041−it takes another parameter which is the monad you want your handlers to run in,
1042−or more generally the return types of your handlers. This third parameter is
1043−used for specifying the return type of the handler for an endpoint, e.g when
1044−computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be
1047−The first and main question one might have then is: how do we write handlers
1147+The 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?
1048−that run in another monad? How can we "bring back" the value from a given monad
1049−into something **servant** can understand?
1051−### Natural transformations
1149+Natural transformations
1150+-----------------------
1053If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1152If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1054do we have?
1153do we have?
1056−``` haskell
1155+``` haskell ignore
1057−type (~>) m n = forall a. m a -> n a
1156+newtype m :~> n = Nat { unNat :: forall a. m a -> n a}
1062−``` haskell
1158+-- For example
1063−listToMaybe' :: [] ~> Maybe
1159+-- listToMaybeNat ::`[] :~> Maybe`
1064−listToMaybe' = listToMaybe -- from Data.Maybe
1160+-- listToMaybeNat = Nat listToMaybe -- from Data.Maybe
1162+(`Nat` comes from "natural transformation", in case you're wondering.)
1067−Note that `servant` doesn't declare the `~>` type-alias, as the unfolded
1068−variant isn't much longer to write, as we'll see shortly.
1070−So if you want to write handlers using another monad/type than `Handler`, say the `Reader String` monad, the first thing you have to
1164+So if you want to write handlers using another monad/type than `ExceptT
1165+ServantErr IO`, say the `Reader String` monad, the first thing you have to
1071prepare is a function:
1166prepare is a function:
1073``` haskell ignore
1168``` haskell ignore
1074−readerToHandler :: Reader String a -> Handler a
1169+readerToEither :: Reader String :~> ExceptT ServantErr IO
1172+Let's start with `readerToEither'`. We obviously have to run the `Reader`
1077−We obviously have to run the `Reader` computation by supplying it with a
1173+computation by supplying it with a `String`, like `"hi"`. We get an `a` out
1078−`String`, like `"hi"`. We get an `a` out from that and can then just `return`
1174+from that and can then just `return` it into `ExceptT`. We can then just wrap
1079−it into `Handler`.
1175+that function with the `Nat` constructor to make it have the fancier type.
1081``` haskell
1177``` haskell
1082−readerToHandler :: Reader String a -> Handler a
1178+readerToEither' :: forall a. Reader String a -> ExceptT ServantErr IO a
1083−readerToHandler r = return (runReader r "hi")
1179+readerToEither' r = return (runReader r "hi")
1181+readerToEither :: Reader String :~> ExceptT ServantErr IO
1182+readerToEither = Nat readerToEither'
1086We can write some simple webservice with the handlers running in `Reader String`.
1185We can write some simple webservice with the handlers running in `Reader String`.
1088``` haskell
1187``` haskell
1089type ReaderAPI = "a" :> Get '[JSON] Int
1188type ReaderAPI = "a" :> Get '[JSON] Int
1090− :<|> "b" :> ReqBody '[JSON] Double :> Get '[JSON] Bool
1189+ :<|> "b" :> Get '[JSON] String
1092readerAPI :: Proxy ReaderAPI
1191readerAPI :: Proxy ReaderAPI
1093readerAPI = Proxy
1192readerAPI = Proxy
1095readerServerT :: ServerT ReaderAPI (Reader String)
1194readerServerT :: ServerT ReaderAPI (Reader String)
1096−readerServerT = a :<|> b where
1195+readerServerT = a :<|> b
1097− a :: Reader String Int
1197+ where a :: Reader String Int
1098− a = return 1797
1198+ a = return 1797
1100− b :: Double -> Reader String Bool
1200+ b :: Reader String String
1101− b _ = asks (== "hi")
1201+ b = ask
1104We unfortunately can't use `readerServerT` as an argument of `serve`, because
1204We unfortunately can't use `readerServerT` as an argument of `serve`, because
1105−`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `Handler`. But there's a simple solution to this.
1205+`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `ExceptT
1206+ServantErr IO`. But there's a simple solution to this.
1107−### Welcome `hoistServer`
1208+Enter `enter`
1109−That's right. We have just written `readerToHandler`, which is exactly what we
1211+That's right. We have just written `readerToEither`, which is exactly what we
1110−would need to apply to all handlers to make the handlers have the
1212+would need to apply to the results of all handlers to make the handlers have the
1111right type for `serve`. Being cumbersome to do by hand, we provide a function
1213right type for `serve`. Being cumbersome to do by hand, we provide a function
1112−`hoistServer` which takes a natural transformation between two parameterized types `m`
1214+`enter` which takes a natural transformation between two parametrized types `m`
1113and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1215and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1115−In our case, we can wrap up our little webservice by using
1217+In our case, we can wrap up our little webservice by using `enter readerToEither` on our handlers.
1116−`hoistServer readerAPI readerToHandler` on our handlers.
1118``` haskell
1219``` haskell
1119readerServer :: Server ReaderAPI
1220readerServer :: Server ReaderAPI
1120−readerServer = hoistServer readerAPI readerToHandler readerServerT
1221+readerServer = enter readerToEither readerServerT
1122app4 :: Application
1223app4 :: Application
1123−app4 = serve readerAPI readerServer
1224+app4 = serve readerAPI EmptyConfig readerServer
1126−This is the webservice in action:
1227+And we can indeed see this webservice in action by running `dist/build/tutorial/tutorial 7`.
1129$ curl http://localhost:8081/a
1230$ curl http://localhost:8081/a
1131−$ curl http://localhost:8081/b -X GET -d '42.0' -H 'Content-Type: application/json'
1232+$ curl http://localhost:8081/b
1135−### An arrow is a reader too.
1137−In previous versions of `servant` we had an `enter` to do what `hoistServer`
1138−does now. `enter` had an ambitious design goals, but was problematic in practice.
1140−One problematic situation was when the source monad was `(->) r`, yet it's
1141−handy in practice, because `(->) r` is isomorphic to `Reader r`.
1143−We can rewrite the previous example without `Reader`:
1146−funServerT :: ServerT ReaderAPI ((->) String)
1147−funServerT = a :<|> b where
1151− -- unfortunately, we cannot make `String` the first argument.
1152− b :: Double -> String -> Bool
1153− b _ s = s == "hi"
1233+"hi"
1155−funToHandler :: (String -> a) -> Handler a
1156−funToHandler f = return (f "hi")
1159−app5 = serve readerAPI (hoistServer readerAPI funToHandler funServerT)
1162−## Streaming endpoints
1164−We can create endpoints that don't just give back a single result, but give
1165−back a *stream* of results, served one at a time. Stream endpoints only provide
1166−a single content type, and also specify what framing strategy is used to
1167−delineate the results. To serve these results, we need to give back a stream
1168−producer. Adapters can be written to *Pipes*, *Conduit* and the like, or
1169−written directly as `SourceIO`s. SourceIO builds upon servant's own `SourceT`
1170−stream type (it's simpler than *Pipes* or *Conduit*).
1171−The API of a streaming endpoint needs to explicitly specify which sort of
1172−generator it produces. Note that the generator itself is returned by a
1173−`Handler` action, so that additional IO may be done in the creation of one.
1176−type StreamAPI = "userStream" :> StreamGet NewlineFraming JSON (SourceIO User)
1177−streamAPI :: Proxy StreamAPI
1180−streamUsers :: SourceIO User
1181−streamUsers = source [isaac, albert, albert]
1184−app6 = serve streamAPI (return streamUsers)
1187−This simple application returns a stream of `User` values encoded in JSON
1236+Conclusion
1188−format, with each value separated by a newline. In this case, the stream will
1237+==========
1189−consist of the value of `isaac`, followed by the value of `albert`, then the
1190−value of `albert` a second time. Importantly, the stream is written back as
1191−results are produced, rather than all at once. This means first that results
1192−are delivered when they are available, and second, that if an exception
1193−interrupts production of the full stream, nonetheless partial results have
1194−already been written back.
1196−## Conclusion
1239+You'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*.
1198−You're now equipped to write webservices/web-applications using
1241+<div style="text-align: center;">
1199−**servant**. The rest of this document focuses on **servant-client**,
1242+ <p><a href="/tutorial/api-type.html">Previous page: A web API as a type</a></p>
1200−**servant-js** and **servant-docs**.
1243+ <p><a href="/tutorial/client.html">Next page: Deriving Haskell functions to query an API</a></p>