@@ -1,12 +1,35 @@
1−# Serving an API
1+---
2+title: Serving an API
3+toc: true
4+---
25
36Enough chit-chat about type-level combinators and representing an API as a
47type. Can we have a webservice already?
58
6−## A first example
9+If you want to follow along with the code and run the examples while you read this guide:
710
8−Equipped with some basic knowledge about the way we represent APIs, let's now
9−write our first webservice.
11+``` bash
12+cabal get servant-examples
13+cd servant-examples-<VERSION>
14+cabal sandbox init
15+cabal install --dependencies-only
16+cabal configure && cabal build
17+```
18+
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:
22+
23+``` bash
24+$ dist/build/tutorial/tutorial
25+Usage: tutorial N
26+ where N is the number of the example you want to run.
27+```
28+
29+A first example
30+===============
31+
32+Equipped with some basic knowledge about the way we represent API, let's now write our first webservice.
1033
1134The source for this tutorial section is a literate haskell file, so first we
1235need to have some language extensions and imports:
@@ -18,23 +41,20 @@ need to have some language extensions and imports:
1841{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1942{-# LANGUAGE MultiParamTypeClasses #-}
2043{-# LANGUAGE OverloadedStrings #-}
21−{-# LANGUAGE RankNTypes #-}
2244{-# LANGUAGE ScopedTypeVariables #-}
2345{-# LANGUAGE TypeOperators #-}
2446
2547module Server where
2648
27−import Prelude ()
28−import Prelude.Compat
29−
30−import Control.Monad.Except
49+import Control.Monad.IO.Class
3150import Control.Monad.Reader
51+import Control.Monad.Trans.Except
3252import Data.Aeson
3353import Data.Aeson.Types
3454import Data.Attoparsec.ByteString
3555import Data.ByteString (ByteString)
56+import Data.Int
3657import Data.List
37−import Data.Maybe
3858import Data.String.Conversions
3959import Data.Time.Calendar
4060import GHC.Generics
@@ -46,18 +66,15 @@ import Servant
4666import System.Directory
4767import Text.Blaze
4868import Text.Blaze.Html.Renderer.Utf8
49−import Servant.Types.SourceT (source)
5069import qualified Data.Aeson.Parser
5170import qualified Text.Blaze.Html
5271```
5372
54−**Important**: the `Servant` module comes from the **servant-server** package,
55−the one that lets us run webservers that implement a particular API type. It
56−reexports all the types from the **servant** package that let you declare API
57−types as well as everything you need to turn your request handlers into a
58−fully-fledged webserver. This means that in your applications, you can just add
59−**servant-server** as a dependency, import `Servant` and not worry about anything
60−else.
73+``` haskell ignore
74+{-# LANGUAGE TypeFamilies #-}
75+```
76+
77+**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.
6178
6279We will write a server that will serve the following API.
6380
@@ -96,6 +113,12 @@ users1 =
96113 ]
97114```
98115
116+Let's also write our API type.
117+
118+``` haskell ignore
119+type UserAPI1 = "users" :> Get '[JSON] [User]
120+```
121+
99122We can now take care of writing the actual webservice that will handle requests
100123to such an API. This one will be very simple, being reduced to just a single
101124endpoint. The type of the web application is determined by the API type,
@@ -107,11 +130,11 @@ corresponding API type.
107130The first thing to know about the `Server` type family is that behind the
108131scenes it will drive the routing, letting you focus only on the business
109132logic. 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
133+by default run in the `ExceptT ServantErr IO` monad. This is overridable very
111134easily, as explained near the end of this guide. Third thing, the type of the
112135value returned in that monad must be the same as the second argument of the
113136HTTP method combinator used for the corresponding endpoint. In our case, it
114−means we must provide a handler of type `Handler [User]`. Well,
137+means we must provide a handler of type `ExceptT ServantErr IO [User]`. Well,
115138we have a monad, let's just `return` our list:
116139
117140``` haskell
@@ -119,9 +142,7 @@ server1 :: Server UserAPI1
119142server1 = return users1
120143```
121144
122−That's it. Now we can turn `server` into an actual webserver using
123−[wai](http://hackage.haskell.org/package/wai) and
124−[warp](http://hackage.haskell.org/package/warp):
145+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):
125146
126147``` haskell
127148userAPI :: Proxy UserAPI1
@@ -131,7 +152,7 @@ userAPI = Proxy
131152-- which you can think of as an "abstract" web application,
132153-- not yet a webserver.
133154app1 :: Application
134−app1 = serve userAPI server1
155+app1 = serve userAPI EmptyConfig server1
135156```
136157
137158The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
@@ -146,8 +167,9 @@ main = run 8081 app1
146167
147168You can put this all into a file or just grab [servant's
148169repo](http://github.com/haskell-servant/servant) and look at the
149−*doc/tutorial* directory. This code (the source of this web page) is in
150−*doc/tutorial/Server.lhs*.
170+*servant-examples* directory. The code we have just explored is in
171+*tutorial/T1.hs*, runnable with
172+`dist/build/tutorial/tutorial 1`.
151173
152174If you run it, you can go to `http://localhost:8081/users` in your browser or
153175query it with curl and you see:
@@ -157,10 +179,10 @@ $ curl http://localhost:8081/users
157179[{"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"}]
158180```
159181
160−## More endpoints
182+More endpoints
183+==============
161184
162−What if we want more than one endpoint? Let's add `/albert` and `/isaac` to
163−view the corresponding users encoded in JSON.
185+What if we want more than one endpoint? Let's add `/albert` and `/isaac` to view the corresponding users encoded in JSON.
164186
165187``` haskell
166188type UserAPI2 = "users" :> Get '[JSON] [User]
@@ -183,7 +205,7 @@ users2 = [isaac, albert]
183205
184206Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
185207are going to separate the handlers with `:<|>` too! They must be provided in
186−the same order as in the API type.
208+the same order as the one they appear in in the API type.
187209
188210``` haskell
189211server2 :: Server UserAPI2
@@ -192,21 +214,22 @@ server2 = return users2
192214 :<|> return isaac
193215```
194216
195−And that's it! You can run this example in the same way that we showed for
196−`server1` and check out the data available at `/users`, `/albert` and `/isaac`.
217+And that's it! You can run this example with
218+`dist/build/tutorial/tutorial 2` and check out the data available
219+at `/users`, `/albert` and `/isaac`.
197220
198−## From combinators to handler arguments
221+From combinators to handler arguments
222+=====================================
199223
200224Fine, we can write trivial webservices easily, but none of the two above use
201225any "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
226+`Capture` and `ReqBody` right away. You'll see how each occurence of these
203227combinators in an endpoint makes the corresponding handler receive an
204228argument of the appropriate type automatically. You don't have to worry about
205229manually looking up URL captures or query string parameters, or
206230decoding/encoding data from/to JSON. Never.
207231
208−We are going to use the following data types and functions to implement a
209−server for `API`.
232+We are going to use the following data types and functions to implement a server for `API`.
210233
211234``` haskell
212235type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
@@ -214,8 +237,8 @@ type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Posit
214237 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
215238
216239data Position = Position
217− { xCoord :: Int
218− , yCoord :: Int
240+ { x :: Int
241+ , y :: Int
219242 } deriving Generic
220243
221244instance ToJSON Position
@@ -265,15 +288,15 @@ server3 = position
265288 :<|> hello
266289 :<|> marketing
267290
268− where position :: Int -> Int -> Handler Position
291+ where position :: Int -> Int -> ExceptT ServantErr IO Position
269292 position x y = return (Position x y)
270293
271− hello :: Maybe String -> Handler HelloMessage
294+ hello :: Maybe String -> ExceptT ServantErr IO HelloMessage
272295 hello mname = return . HelloMessage $ case mname of
273296 Nothing -> "Hello, anonymous coward"
274297 Just n -> "Hello, " ++ n
275298
276− marketing :: ClientInfo -> Handler Email
299+ marketing :: ClientInfo -> ExceptT ServantErr IO Email
277300 marketing clientinfo = return (emailForClient clientinfo)
278301```
279302
@@ -288,86 +311,99 @@ parameter might not always be there);
288311
289312 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
290313
291−And that's it. Here's the example in action:
314+And that's it. You can see this example in action by running `dist/build/tutorial/tutorial 3`.
292315
293316``` bash
294317$ curl http://localhost:8081/position/1/2
295−{"xCoord":1,"yCoord":2}
318+{"x":1,"y":2}
296319$ curl http://localhost:8081/hello
297320{"msg":"Hello, anonymous coward"}
298321$ curl http://localhost:8081/hello?name=Alp
299322{"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
323+$ 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
301324{"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"}
302325```
303326
304−For reference, here's a list of some combinators from **servant**:
327+For reference, here's a list of some combinators from *servant* and for those
328+that get turned into arguments to the handlers, the type of the argument.
305329
306− > - `Delete`, `Get`, `Patch`, `Post`, `Put`: these do not become arguments. They provide the return type of handlers, which usually is `Handler <something>`.
330+ > - `Delete`, `Get`, `Patch`, `Post`, `Put`: these do not become arguments. They provide the return type of handlers, which usually is `ExceptT ServantErr IO <something>`.
307331 > - `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.
309− > - `QueryFlag "something"` gets turned into an argument of type `Bool`.
310− > - `QueryParams "something" a` gets turned into an argument of type `[a]`.
332+ > - `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.
333+ > - `QueryFlag "something"` and `MatrixFlag "something"` get turned into arguments of type `Bool`.
334+ > - `QueryParams "something" a` and `MatrixParams "something" a` get turned into arguments of type `[a]`.
311335 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
312336
313−## The `FromHttpApiData`/`ToHttpApiData` classes
337+The `FromText`/`ToText` classes
338+===============================
314339
315−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
317−help us answer these questions.
340+Wait... How does *servant* know how to decode the `Int`s from the URL? Or how
341+to decode a `ClientInfo` value from the request body? This is what this and the
342+following two sections address.
318343
319344`Capture`s and `QueryParam`s are represented by some textual value in URLs.
320345`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
322−decoded from headers, captures, and query params is expressed in a class
323−`FromHttpApiData` (from the package
324−[**http-api-data**](http://hackage.haskell.org/package/http-api-data)):
346+corresponding (textual) value in the request's "metadata". This is why we
347+decided to provide a pair of typeclasses, `FromText` and `ToText` which just
348+let you say that you can respectively *extract* or *encode* values of some type
349+*from*/*to* text. Here are the definitions:
325350
326351``` haskell ignore
327−class FromHttpApiData a where
328− {-# MINIMAL parseUrlPiece | parseQueryParam #-}
329− -- | Parse URL path piece.
330− parseUrlPiece :: Text -> Either Text a
331− parseUrlPiece = parseQueryParam
352+class FromText a where
353+ fromText :: Text -> Maybe a
332354
333− -- | Parse HTTP header value.
334− parseHeader :: ByteString -> Either Text a
335− parseHeader = parseUrlPiece . decodeUtf8
355+class ToText a where
356+ toText :: a -> Text
357+```
358+
359+And as long as the type that a `Capture`/`QueryParam`/`Header`/etc will be
360+decoded to provides a `FromText` instance, it will Just Work. *servant*
361+provides a decent number of instances, but here are some examples of defining
362+your own.
363+
364+``` haskell
365+-- A typical enumeration
366+data Direction
367+ = Up
368+ | Down
369+ | Left
370+ | Right
336371
337− -- | Parse query param value.
338− parseQueryParam :: Text -> Either Text a
339− parseQueryParam = parseUrlPiece
372+newtype UserId = UserId Int64
340373```
341374
342−As you can see, as long as you provide either `parseUrlPiece` (for `Capture`s)
343−or `parseQueryParam` (for `QueryParam`s), the other methods will be defined in
344−terms of this.
375+or writing the instances by hand:
345376
346−**http-api-data** provides a decent number of instances, helpers for defining new
347−ones, and wonderful documentation.
377+``` haskell ignore
378+instance FromText UserId where
379+ fromText = fmap UserId fromText
380+
381+instance ToText UserId where
382+ toText (UserId i) = toText i
383+```
348384
349385There'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
351−types. You will need `FromHttpApiData` instances for server-side request
352−handlers and `ToHttpApiData` instances only when using
353−**servant-client**, as described in the [section about deriving haskell
354−functions to query an API](Client.html).
386+them when using `Capture`, `QueryParam`, `QueryParams`, `MatrixParam`,
387+`MatrixParams` and `Header` with your types. You will need `FromText` instances
388+for server-side request handlers and `ToText` instances only when using
389+*servant-client*, as described in the [section about deriving haskell
390+functions to query an API](/tutorial/client.html).
355391
356−## Using content-types with your data types
392+Using content-types with your data types
393+========================================
357394
358395The same principle was operating when decoding request bodies from JSON, and
359396responses *into* JSON. (JSON is just the running example - you can do this with
360397any content-type.)
361398
362−This section introduces a couple of typeclasses provided by **servant** that make
399+This section introduces a couple of typeclasses provided by *servant* that make
363400all of this work.
364401
365−### The truth behind `JSON`
402+The truth behind `JSON`
403+-----------------------
366404
367−
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
370−data type.
405+What exactly is `JSON`? Like the 3 other content types provided out of the box
406+by *servant*, it's a really dumb data type.
371407
372408``` haskell ignore
373409data JSON
@@ -377,15 +413,14 @@ data OctetStream
377413```
378414
379415Obviously, 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
416+pointless. Like most of the data types in *servant*, `JSON` is mostly there as
381417a special *symbol* that's associated with encoding (resp. decoding) to (resp.
382418from) the *JSON* format. The way this association is performed can be
383419decomposed into two steps.
384420
385421The first step is to provide a proper
386−`MediaType` (from
387−[**http-media**](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
422+[`MediaType`](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html)
423+representation for `JSON`, or for your own content types. If you look at the
389424haddocks from this link, you can see that we just have to specify
390425`application/json` using the appropriate functions. In our case, we can just
391426use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
@@ -401,14 +436,14 @@ instance Accept JSON where
401436```
402437
403438The second step is centered around the `MimeRender` and `MimeUnrender` classes.
404−These classes just let you specify a way to encode and decode
405−values into or from your content-type's representation.
439+These classes just let you specify a way to respectively encode and decode
440+values respectively into or from your content-type's representation.
406441
407442``` haskell ignore
408443class Accept ctype => MimeRender ctype a where
409− mimeRender :: Proxy ctype -> a -> ByteString
444+ mimeRender :: Proxy ctype -> a -> ByteString
410445 -- alternatively readable as:
411− mimeRender :: Proxy ctype -> (a -> ByteString)
446+ mimeRender :: Proxy ctype -> (a -> ByteString)
412447```
413448
414449Given a content-type and some user type, `MimeRender` provides a function that
@@ -429,12 +464,14 @@ And now the `MimeUnrender` class, which lets us extract values from lazy
429464``` haskell ignore
430465class Accept ctype => MimeUnrender ctype a where
431466 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
467+ -- alternatively:
468+ mimeUnrender :: Proxy ctype -> (ByteString -> Either String a)
432469```
433470
434471We don't have much work to do there either, `Data.Aeson.eitherDecode` is
435472precisely what we need. However, it only allows arrays and objects as toplevel
436473JSON 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
474+our own little function around *aeson* and *attoparsec* that allows any type of
438475JSON value at the toplevel of a "JSON document". Here's the definition in case
439476you are curious.
440477
@@ -452,20 +489,21 @@ instance FromJSON a => MimeUnrender JSON a where
452489 mimeUnrender _ = eitherDecodeLenient
453490```
454491
455−And this is all the code that lets you use `JSON` with `ReqBody`, `Get`,
492+And this is all the code that lets you use `JSON` for with `ReqBody`, `Get`,
456493`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
494+for an `HTML` content type, so that users of your webservice can access an
458495HTML 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/),
460−simply by adding `Accept: text/html` to their request headers.
496+document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/), simply by adding `Accept:
497+text/html` to their request headers.
461498
462−### Case-studies: **servant-blaze** and **servant-lucid**
499+Case-studies: *servant-blaze* and *servant-lucid*
500+-------------------------------------------------
463501
464502These 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)
466−or [**lucid**](http://hackage.haskell.org/package/lucid). The best option for
467−**servant** is obviously to support both (and hopefully other templating
468−solutions!). We're first going to look at **lucid**:
503+Haskell use either [blaze-html](http://hackage.haskell.org/package/blaze-html)
504+or [lucid](http://hackage.haskell.org/package/lucid). The best option for
505+*servant* is obviously to support both (and hopefully other templating
506+solutions!).
469507
470508``` haskell
471509data HTMLLucid
@@ -473,20 +511,24 @@ data HTMLLucid
473511
474512Once again, the data type is just there as a symbol for the encoding/decoding
475513functions, except that this time we will only worry about encoding since
476−**lucid** doesn't provide a way to extract data from HTML.
514+*blaze-html* and *lucid* don't provide a way to extract data from HTML.
515+
516+Both packages also have the same `Accept` instance for their `HTMLLucid` type.
477517
478518``` haskell
479519instance Accept HTMLLucid where
480520 contentType _ = "text" // "html" /: ("charset", "utf-8")
481521```
482522
483−Note that this instance uses the `(/:)` operator from **http-media** which lets
523+Note that this instance uses the `(/:)` operator from *http-media* which lets
484524us specify additional information about a content-type, like the charset here.
485525
486−The rendering instances call similar functions that take
526+The rendering instances for both packages both call similar functions that take
487527types with an appropriate instance to an "abstract" HTML representation and
488528then write that to a `ByteString`.
489529
530+For *lucid*:
531+
490532``` haskell
491533instance ToHtml a => MimeRender HTMLLucid a where
492534 mimeRender _ = renderBS . toHtml
@@ -497,7 +539,7 @@ instance MimeRender HTMLLucid (Html a) where
497539 mimeRender _ = renderBS
498540```
499541
500−For **blaze-html** everything works very similarly:
542+For *blaze-html*:
501543
502544``` haskell
503545-- For this tutorial to compile 'HTMLLucid' and 'HTMLBlaze' have to be
@@ -517,13 +559,15 @@ instance MimeRender HTMLBlaze Text.Blaze.Html.Html where
517559 mimeRender _ = renderHtml
518560```
519561
520−Both [**servant-blaze**](http://hackage.haskell.org/package/servant-blaze) and
521−[**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
523−appropriate class (`ToMarkup` for **blaze-html**, `ToHtml` for **lucid**).
562+Both [servant-blaze](http://hackage.haskell.org/package/servant-blaze) and
563+[servant-lucid](http://hackage.haskell.org/package/servant-lucid) let you use
564+`HTMLLucid` in any content type list as long as you provide an instance of the
565+appropriate class (`ToMarkup` for *blaze-html*, `ToHtml` for *lucid*).
524566
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:
567+We can now write webservice that uses *servant-lucid* to show the `HTMLLucid`
568+content type in action. First off, imports and pragmas as usual.
569+
570+We will be serving the following API:
527571
528572``` haskell
529573type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
@@ -540,7 +584,7 @@ data Person = Person
540584instance ToJSON Person
541585```
542586
543−Now, let's teach **lucid** how to render a `Person` as a row in a table, and then
587+Now, let's teach *lucid* how to render a `Person` as a row in a table, and then
544588a list of `Person`s as a table with a row per person.
545589
546590``` haskell
@@ -571,8 +615,8 @@ instance ToHtml [Person] where
571615We create some `Person` values and serve them as a list:
572616
573617``` haskell
574−people :: [Person]
575−people =
618+persons :: [Person]
619+persons =
576620 [ Person "Isaac" "Newton"
577621 , Person "Albert" "Einstein"
578622 ]
@@ -581,69 +625,75 @@ personAPI :: Proxy PersonAPI
581625personAPI = Proxy
582626
583627server4 :: Server PersonAPI
584−server4 = return people
628+server4 = return persons
585629
586630app2 :: Application
587−app2 = serve personAPI server4
631+app2 = serve personAPI EmptyConfig server4
588632```
589633
590−And we're good to go:
634+And we're good to go. You can run this example with `dist/build/tutorial/tutorial 4`.
591635
592636``` bash
593−$ curl http://localhost:8081/persons
594−[{"lastName":"Newton","firstName":"Isaac"},{"lastName":"Einstein","firstName":"Albert"}]
595−$ 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>
597−# or just point your browser to http://localhost:8081/persons
637+ $ curl http://localhost:8081/persons
638+ [{"lastName":"Newton","firstName":"Isaac"},{"lastName":"Einstein","firstName":"Albert"}]
639+ $ curl -H 'Accept: text/html' http://localhost:8081/persons
640+ <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>
641+ # or just point your browser to http://localhost:8081/persons
598642```
599643
600−## The `Handler` monad
644+The `ExceptT ServantErr IO` monad
645+=================================
601646
602−At the heart of the handlers is the monad they run in, namely a newtype `Handler` around `ExceptT ServerError IO`
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
647+At the heart of the handlers is the monad they run in, namely `ExceptT
648+ServantErr IO`. One might wonder: why this monad? The answer is that it is the
605649simplest monad with the following properties:
606650
607−- it lets us both return a successful result (using `return`)
608−or "fail" with a descriptive error (using `throwError`);
651+- it lets us both return a successful result (with the `Right` branch of
652+`Either`) or "fail" with a descriptive error (with the `Left` branch of
653+`Either`);
609654- it lets us perform IO, which is absolutely vital since most webservices exist
610−as interfaces to databases that we interact with in `IO`.
655+as interfaces to databases that we interact with in `IO`;
611656
612657Let's recall some definitions.
613658
614659``` haskell ignore
615−-- from the 'mtl' package at
616−newtype ExceptT e m a = ExceptT (m (Either e a))
660+-- from the Prelude
661+data Either e a = Left e | Right a
662+
663+-- from the 'either' 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
666+ = ExceptT { runEitherT :: m (Either e a) }
617667```
618668
619−In short, this means that a handler of type `Handler a` is simply
620−equivalent to a computation of type `IO (Either ServerError a)`, that is, an IO
669+In short, this means that a handler of type `ExceptT ServantErr IO a` is simply
670+equivalent to a computation of type `IO (Either ServantErr a)`, that is, an IO
621671action that either returns an error or a result.
622672
623−The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl/docs/Control-Monad-Except.html#t:ExceptT)
624−from which `ExceptT` comes is worth looking at.
625−Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so
626−`throwError` can be used to return an error from your handler (whereas `return`
627− is enough to return a success).
673+The aforementioned `either` package is worth taking a look at. Perhaps most
674+importantly:
675+
676+``` haskell ignore
677+left :: Monad m => e -> ExceptT e m a
678+```
679+Allows you to return an error from your handler (whereas `return` is enough to
680+return a success).
628681
629682Most of what you'll be doing in your handlers is running some IO and,
630683depending on the result, you might sometimes want to throw an error of some
631684kind and abort early. The next two sections cover how to do just that.
632685
633−### Performing IO
686+Performing IO
687+-------------
634688
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)
638−is a class from the **transformers** package defined as:
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:
639690
640691``` haskell ignore
641692class Monad m => MonadIO m where
642693 liftIO :: IO a -> m a
643694```
644695
645−So if you want to run any kind of
646−IO computation in your handlers, just use `liftIO`:
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`:
647697
648698``` haskell
649699type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
@@ -660,16 +710,17 @@ server5 = do
660710 return (FileContent filecontent)
661711```
662712
663−### Failing, through `ServerError`
713+Failing, through `ServantErr`
714+-----------------------------
664715
665716If you want to explicitly fail at providing the result promised by an endpoint
666717using 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
668−and provide it with the appropriate value of type `ServerError`, which is
718+error message, all you have to do is use the `left` function mentioned above
719+and provide it with the appropriate value of type `ServantErr`, which is
669720defined as:
670721
671722``` haskell ignore
672−data ServerError = ServerError
723+data ServantErr = ServantErr
673724 { errHTTPCode :: Int
674725 , errReasonPhrase :: String
675726 , errBody :: ByteString -- lazy bytestring
@@ -682,10 +733,10 @@ module. If you want to use these values but add a body or some headers, just
682733use record update syntax:
683734
684735``` haskell
685−failingHandler :: Handler ()
686−failingHandler = throwError myerr
736+failingHandler :: ExceptT ServantErr IO ()
737+failingHandler = throwE myerr
687738
688− where myerr :: ServerError
739+ where myerr :: ServantErr
689740 myerr = err503 { errBody = "Sorry dear user." }
690741```
691742
@@ -698,47 +749,48 @@ server6 = do
698749 exists <- liftIO (doesFileExist "myfile.txt")
699750 if exists
700751 then liftIO (readFile "myfile.txt") >>= return . FileContent
701− else throwError custom404Err
752+ else throwE custom404Err
702753
703754 where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }
704755```
705756
706−Here's how that server looks in action:
757+Let's run this server (`dist/build/tutorial/tutorial 5`) and
758+query it, first without the file and then with the file.
707759
708760``` bash
709−$ curl --verbose http://localhost:8081/myfile.txt
710−[snip]
711−* Connected to localhost (127.0.0.1) port 8081 (#0)
712−> GET /myfile.txt HTTP/1.1
713−> User-Agent: curl/7.30.0
714−> Host: localhost:8081
715−> Accept: */*
716−>
717−< HTTP/1.1 404 Not Found
718−[snip]
719−myfile.txt just isn't there, please leave this server alone.
720−
721−$ echo Hello > myfile.txt
722−
723−$ curl --verbose http://localhost:8081/myfile.txt
724−[snip]
725−* Connected to localhost (127.0.0.1) port 8081 (#0)
726−> GET /myfile.txt HTTP/1.1
727−> User-Agent: curl/7.30.0
728−> Host: localhost:8081
729−> Accept: */*
730−>
731−< HTTP/1.1 200 OK
732−[snip]
733−< Content-Type: application/json
734−[snip]
735−{"content":"Hello\n"}
736−```
737−
738−## Response headers
739−
740−To add headers to your response, use
741−[addHeader](http://hackage.haskell.org/package/servant/docs/Servant-API-ResponseHeaders.html).
761+ $ curl --verbose http://localhost:8081/myfile.txt
762+ [snip]
763+ * Connected to localhost (127.0.0.1) port 8081 (#0)
764+ > GET /myfile.txt HTTP/1.1
765+ > User-Agent: curl/7.30.0
766+ > Host: localhost:8081
767+ > Accept: */*
768+ >
769+ < HTTP/1.1 404 Not Found
770+ [snip]
771+ myfile.txt just isnt there, please leave this server alone.
772+
773+ $ echo Hello > myfile.txt
774+
775+ $ curl --verbose http://localhost:8081/myfile.txt
776+ [snip]
777+ * Connected to localhost (127.0.0.1) port 8081 (#0)
778+ > GET /myfile.txt HTTP/1.1
779+ > User-Agent: curl/7.30.0
780+ > Host: localhost:8081
781+ > Accept: */*
782+ >
783+ < HTTP/1.1 200 OK
784+ [snip]
785+ < Content-Type: application/json
786+ [snip]
787+ {"content":"Hello\n"}
788+```
789+
790+Response headers
791+================
792+
793+To add headers to your response, use [addHeader](http://hackage.haskell.org/package/servant-0.4.4/docs/Servant-API-ResponseHeaders.html).
742794Note that this changes the type of your API, as we can see in the following example:
743795
744796``` haskell
@@ -748,89 +800,158 @@ myHandler :: Server MyHandler
748800myHandler = return $ addHeader 1797 albert
749801```
750802
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:
753−
754−``` haskell
755−type MyHeadfulHandler = Get '[JSON] (Headers '[Header "X-A-Bool" Bool, Header "X-An-Int" Int] User)
756−
757−myHeadfulHandler :: Server MyHeadfulHandler
758−myHeadfulHandler = return $ addHeader True $ addHeader 1797 albert
759−```
760−
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`:
765−
766−``` haskell
767−type MyMaybeHeaderHandler
768− = Capture "withHeader" Bool :> Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
769−
770−myMaybeHeaderHandler :: Server MyMaybeHeaderHandler
771−myMaybeHeaderHandler x = return $ if x then addHeader 1797 albert
772− else noHeader albert
773−```
774803
775−## Serving static files
804+Serving static files
805+====================
776806
777−**servant-server** also provides a way to just serve the content of a directory
807+*servant-server* also provides a way to just serve the content of a directory
778808under some path in your web API. As mentioned earlier in this document, the
779809`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
810+application". Well, servant-server provides a function to get a file and
781811directory serving WAI application, namely:
782812
783813``` haskell ignore
784814-- exported by Servant and Servant.Server
785−serveDirectoryWebApp :: FilePath -> Server Raw
815+serveDirectory :: FilePath -> Server Raw
786816```
787817
788−`serveDirectoryWebApp`'s argument must be a path to a valid directory.
818+`serveDirectory`'s argument must be a path to a valid directory. You can see an
819+example below, runnable with `dist/build/tutorial/tutorial 6`
820+(you **must** run it from within the *servant-examples/* directory!), which is
821+a webserver that serves the various bits of code covered in this
822+getting-started.
789823
790−Here's an example API that will serve some static files:
824+The API type will be the following.
791825
792826``` haskell
793−type StaticAPI = "static" :> Raw
827+type CodeAPI = "code" :> Raw
794828```
795829
796830And the server:
797831
798832``` haskell
799−staticAPI :: Proxy StaticAPI
800−staticAPI = Proxy
833+codeAPI :: Proxy CodeAPI
834+codeAPI = Proxy
801835```
802836
803837``` haskell
804−server7 :: Server StaticAPI
805−server7 = serveDirectoryWebApp "static-files"
838+server7 :: Server CodeAPI
839+server7 = serveDirectory "tutorial"
806840
807841app3 :: Application
808−app3 = serve staticAPI server7
842+app3 = serve codeAPI EmptyConfig server7
809843```
810844
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
813− *static-files/* directory of the path you run the program from.
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.
846+
847+In other words:
848+
849+- If a client requests `/code/foo.txt`, the server will look for a file at `./tutorial/foo.txt` (and fail)
850+- If a client requests `/code/T1.hs`, the server will look for a file at `./tutorial/T1.hs` (and succeed)
851+- 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)
852+
853+Here is our little server in action.
854+
855+``` haskell ignore
856+$ curl http://localhost:8081/code/T1.hs
857+{-# LANGUAGE DataKinds #-}
858+{-# LANGUAGE TypeFamilies #-}
859+{-# LANGUAGE DeriveGeneric #-}
860+{-# LANGUAGE TypeOperators #-}
861+module T1 where
862+
863+import Data.Aeson
864+import Data.Time.Calendar
865+import GHC.Generics
866+import Network.Wai
867+import Servant
868+
869+data User = User
870+ { name :: String
871+ , age :: Int
872+ , email :: String
873+ , registration_date :: Day
874+ } deriving (Eq, Show, Generic)
875+
876+-- orphan ToJSON instance for Day. necessary to derive one for User
877+instance ToJSON Day where
878+ -- display a day in YYYY-mm-dd format
879+ toJSON d = toJSON (showGregorian d)
814880
815−In other words: If a client requests `/static/foo.txt`, the server will look for a file at
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.
881+instance ToJSON User
818882
819−`serveDirectoryWebApp` uses some standard settings that fit the use case of
820−serving static files for most web apps. You can find out about the other
821−options in the documentation of the `Servant.Server.StaticFiles` module.
883+type UserAPI = "users" :> Get '[JSON] [User]
822884
823−## Nested APIs
885+users :: [User]
886+users =
887+ [ User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
888+ , User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
889+ ]
824890
825−Let's see how you can define APIs in a modular way, while avoiding repetition.
826−Consider this simple example:
891+userAPI :: Proxy UserAPI
892+userAPI = Proxy
893+
894+server :: Server UserAPI
895+server = return users
896+
897+app :: Application
898+app = serve userAPI server
899+$ curl http://localhost:8081/code/tutorial.hs
900+import Network.Wai
901+import Network.Wai.Handler.Warp
902+import System.Environment
903+
904+import qualified T1
905+import qualified T2
906+import qualified T3
907+import qualified T4
908+import qualified T5
909+import qualified T6
910+import qualified T7
911+import qualified T9
912+import qualified T10
913+
914+app :: String -> (Application -> IO ()) -> IO ()
915+app n f = case n of
916+ "1" -> f T1.app
917+ "2" -> f T2.app
918+ "3" -> f T3.app
919+ "4" -> f T4.app
920+ "5" -> f T5.app
921+ "6" -> f T6.app
922+ "7" -> f T7.app
923+ "8" -> f T3.app
924+ "9" -> T9.writeJSFiles >> f T9.app
925+ "10" -> f T10.app
926+ _ -> usage
927+
928+main :: IO ()
929+main = do
930+ args <- getArgs
931+ case args of
932+ [n] -> app n (run 8081)
933+ _ -> usage
934+
935+usage :: IO ()
936+usage = do
937+ putStrLn "Usage:\t tutorial N"
938+ putStrLn "\t\twhere N is the number of the example you want to run."
939+
940+$ curl http://localhost:8081/foo
941+not found
942+```
943+
944+Nested APIs
945+===========
946+
947+Let's see how you can define APIs in a modular way, while avoiding repetition. Consider this simple example:
827948
828949``` haskell
829950type UserAPI3 = -- view the user with given userid, in JSON
830951 Capture "userid" Int :> Get '[JSON] User
831952
832953 :<|> -- delete the user with given userid. empty response
833− Capture "userid" Int :> DeleteNoContent
954+ Capture "userid" Int :> Delete '[] ()
834955```
835956
836957We can instead factor out the `userid`:
@@ -838,34 +959,32 @@ We can instead factor out the `userid`:
838959``` haskell
839960type UserAPI4 = Capture "userid" Int :>
840961 ( Get '[JSON] User
841− :<|> DeleteNoContent
962+ :<|> Delete '[] ()
842963 )
843964```
844965
845−However, you have to be aware that this has an effect on the type of the
846−corresponding `Server`:
966+However, you have to be aware that this has an effect on the type of the corresponding `Server`:
847967
848968``` haskell ignore
849−Server UserAPI3 = (Int -> Handler User)
850− :<|> (Int -> Handler NoContent)
969+Server UserAPI3 = (Int -> ExceptT ServantErr IO User)
970+ :<|> (Int -> ExceptT ServantErr IO ())
851971
852−Server UserAPI4 = Int -> ( Handler User
853− :<|> Handler NoContent
972+Server UserAPI4 = Int -> ( ExceptT ServantErr IO User
973+ :<|> ExceptT ServantErr IO ()
854974 )
855975```
856976
857977In 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
859−computations in `Handler`, with no arguments. In other words:
978+the whole `Server` takes the *userid* and has handlers that are just computations in `ExceptT`, with no arguments. In other words:
860979
861980``` haskell
862981server8 :: Server UserAPI3
863982server8 = getUser :<|> deleteUser
864983
865− where getUser :: Int -> Handler User
984+ where getUser :: Int -> ExceptT ServantErr IO User
866985 getUser _userid = error "..."
867986
868− deleteUser :: Int -> Handler NoContent
987+ deleteUser :: Int -> ExceptT ServantErr IO ()
869988 deleteUser _userid = error "..."
870989
871990-- notice how getUser and deleteUser
@@ -874,17 +993,14 @@ server8 = getUser :<|> deleteUser
874993server9 :: Server UserAPI4
875994server9 userid = getUser userid :<|> deleteUser userid
876995
877− where getUser :: Int -> Handler User
996+ where getUser :: Int -> ExceptT ServantErr IO User
878997 getUser = error "..."
879998
880− deleteUser :: Int -> Handler NoContent
999+ deleteUser :: Int -> ExceptT ServantErr IO ()
8811000 deleteUser = error "..."
8821001```
8831002
884−Note that there's nothing special about `Capture` that lets you "factor it
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
887−`Server`.
1003+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`.
8881004
8891005``` haskell
8901006-- we just factor out the "users" path fragment
@@ -896,63 +1012,62 @@ type API1 = "users" :>
8961012-- we factor out the Request Body
8971013type API2 = ReqBody '[JSON] User :>
8981014 ( Get '[JSON] User -- just display the same user back, don't register it
899− :<|> PostNoContent -- register the user. empty response
1015+ :<|> Post '[JSON] () -- register the user. empty response
9001016 )
9011017
9021018-- we factor out a Header
9031019type API3 = Header "Authorization" Token :>
9041020 ( Get '[JSON] SecretData -- get some secret data, if authorized
905− :<|> ReqBody '[JSON] SecretData :> PostNoContent -- add some secret data, if authorized
1021+ :<|> ReqBody '[JSON] SecretData :> Post '[] () -- add some secret data, if authorized
9061022 )
9071023
9081024newtype Token = Token ByteString
9091025newtype SecretData = SecretData ByteString
9101026```
9111027
912−This approach lets you define APIs modularly and assemble them all into one big
913−API type only at the end.
1028+This approach lets you define APIs modularly and assemble them all into one big API type only at the end.
9141029
9151030``` haskell
9161031type UsersAPI =
9171032 Get '[JSON] [User] -- list users
918− :<|> ReqBody '[JSON] User :> PostNoContent -- add a user
1033+ :<|> ReqBody '[JSON] User :> Post '[] () -- add a user
9191034 :<|> Capture "userid" Int :>
9201035 ( Get '[JSON] User -- view a user
921− :<|> ReqBody '[JSON] User :> PutNoContent -- update a user
922− :<|> DeleteNoContent -- delete a user
1036+ :<|> ReqBody '[JSON] User :> Put '[] () -- update a user
1037+ :<|> Delete '[] () -- delete a user
9231038 )
9241039
9251040usersServer :: Server UsersAPI
9261041usersServer = getUsers :<|> newUser :<|> userOperations
9271042
928− where getUsers :: Handler [User]
1043+ where getUsers :: ExceptT ServantErr IO [User]
9291044 getUsers = error "..."
9301045
931− newUser :: User -> Handler NoContent
1046+ newUser :: User -> ExceptT ServantErr IO ()
9321047 newUser = error "..."
9331048
9341049 userOperations userid =
9351050 viewUser userid :<|> updateUser userid :<|> deleteUser userid
9361051
9371052 where
938− viewUser :: Int -> Handler User
1053+ viewUser :: Int -> ExceptT ServantErr IO User
9391054 viewUser = error "..."
9401055
941− updateUser :: Int -> User -> Handler NoContent
1056+ updateUser :: Int -> User -> ExceptT ServantErr IO ()
9421057 updateUser = error "..."
9431058
944− deleteUser :: Int -> Handler NoContent
1059+ deleteUser :: Int -> ExceptT ServantErr IO ()
9451060 deleteUser = error "..."
9461061```
9471062
9481063``` haskell
9491064type ProductsAPI =
9501065 Get '[JSON] [Product] -- list products
951− :<|> ReqBody '[JSON] Product :> PostNoContent -- add a product
1066+ :<|> ReqBody '[JSON] Product :> Post '[] () -- add a product
9521067 :<|> Capture "productid" Int :>
9531068 ( Get '[JSON] Product -- view a product
954− :<|> ReqBody '[JSON] Product :> PutNoContent -- update a product
955− :<|> DeleteNoContent -- delete a product
1069+ :<|> ReqBody '[JSON] Product :> Put '[] () -- update a product
1070+ :<|> Delete '[] () -- delete a product
9561071 )
9571072
9581073data Product = Product { productId :: Int }
@@ -960,23 +1075,23 @@ data Product = Product { productId :: Int }
9601075productsServer :: Server ProductsAPI
9611076productsServer = getProducts :<|> newProduct :<|> productOperations
9621077
963− where getProducts :: Handler [Product]
1078+ where getProducts :: ExceptT ServantErr IO [Product]
9641079 getProducts = error "..."
9651080
966− newProduct :: Product -> Handler NoContent
1081+ newProduct :: Product -> ExceptT ServantErr IO ()
9671082 newProduct = error "..."
9681083
9691084 productOperations productid =
9701085 viewProduct productid :<|> updateProduct productid :<|> deleteProduct productid
9711086
9721087 where
973− viewProduct :: Int -> Handler Product
1088+ viewProduct :: Int -> ExceptT ServantErr IO Product
9741089 viewProduct = error "..."
9751090
976− updateProduct :: Int -> Product -> Handler NoContent
1091+ updateProduct :: Int -> Product -> ExceptT ServantErr IO ()
9771092 updateProduct = error "..."
9781093
979− deleteProduct :: Int -> Handler NoContent
1094+ deleteProduct :: Int -> ExceptT ServantErr IO ()
9801095 deleteProduct = error "..."
9811096```
9821097
@@ -988,213 +1103,139 @@ server10 :: Server CombinedAPI
9881103server10 = usersServer :<|> productsServer
9891104```
9901105
991−Finally, we can realize the user and product APIs are quite similar and
992−abstract that away:
1106+Finally, we can realize the user and product APIs are quite similar and abstract that away:
9931107
9941108``` haskell
9951109-- API for values of type 'a'
9961110-- indexed by values of type 'i'
9971111type APIFor a i =
9981112 Get '[JSON] [a] -- list 'a's
999− :<|> ReqBody '[JSON] a :> PostNoContent -- add an 'a'
1113+ :<|> ReqBody '[JSON] a :> Post '[] () -- add an 'a'
10001114 :<|> Capture "id" i :>
10011115 ( Get '[JSON] a -- view an 'a' given its "identifier" of type 'i'
1002− :<|> ReqBody '[JSON] a :> PutNoContent -- update an 'a'
1003− :<|> DeleteNoContent -- delete an 'a'
1116+ :<|> ReqBody '[JSON] a :> Put '[] () -- update an 'a'
1117+ :<|> Delete '[] () -- delete an 'a'
10041118 )
10051119
10061120-- Build the appropriate 'Server'
10071121-- given the handlers of the right type.
1008−serverFor :: Handler [a] -- handler for listing of 'a's
1009− -> (a -> Handler NoContent) -- handler for adding an 'a'
1010− -> (i -> Handler a) -- handler for viewing an 'a' given its identifier of type 'i'
1011− -> (i -> a -> Handler NoContent) -- updating an 'a' with given id
1012− -> (i -> Handler NoContent) -- deleting an 'a' given its id
1122+serverFor :: ExceptT ServantErr IO [a] -- handler for listing of 'a's
1123+ -> (a -> ExceptT ServantErr IO ()) -- handler for adding an 'a'
1124+ -> (i -> ExceptT ServantErr IO a) -- handler for viewing an 'a' given its identifier of type 'i'
1125+ -> (i -> a -> ExceptT ServantErr IO ()) -- updating an 'a' with given id
1126+ -> (i -> ExceptT ServantErr IO ()) -- deleting an 'a' given its id
10131127 -> Server (APIFor a i)
10141128serverFor = error "..."
10151129-- implementation left as an exercise. contact us on IRC
10161130-- or the mailing list if you get stuck!
10171131```
10181132
1019−When your API contains the `EmptyAPI` combinator, you'll want to use
1020−`emptyServer` in the corresponding slot for your server, which will simply fail
1021−with 404 whenever a request reaches it:
1022−
1023−``` haskell
1024−type CombinedAPI2 = API :<|> "empty" :> EmptyAPI
1025−
1026−server11 :: Server CombinedAPI2
1027−server11 = server3 :<|> emptyServer
1028−```
1029−
1030−## Using another monad for your handlers
1133+Using another monad for your handlers
1134+=====================================
10311135
1032−Remember how `Server` turns combinators for HTTP methods into `Handler`? Well, actually, there's more to that. `Server` is actually a
1033−simple type synonym.
1136+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.
10341137
10351138``` haskell ignore
1036−type Server api = ServerT api Handler
1139+type Server api = ServerT api (ExceptT ServantErr IO)
10371140```
10381141
1039−`ServerT` is the actual type family that computes the required types for the
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
1045−`SomeMonad Person`.
1142+`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`.
10461143
1047−The first and main question one might have then is: how do we write handlers
1048−that run in another monad? How can we "bring back" the value from a given monad
1049−into something **servant** can understand?
1144+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?
10501145
1051−### Natural transformations
1146+Natural transformations
1147+-----------------------
10521148
10531149If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
10541150do we have?
10551151
1056−``` haskell
1057−type (~>) m n = forall a. m a -> n a
1058−```
1059−
1060−For example:
1152+``` haskell ignore
1153+newtype m :~> n = Nat { unNat :: forall a. m a -> n a}
10611154
1062−``` haskell
1063−listToMaybe' :: [] ~> Maybe
1064−listToMaybe' = listToMaybe -- from Data.Maybe
1155+-- For example
1156+-- listToMaybeNat ::`[] :~> Maybe`
1157+-- listToMaybeNat = Nat listToMaybe -- from Data.Maybe
10651158```
1159+(`Nat` comes from "natural transformation", in case you're wondering.)
10661160
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.
1069−
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
1161+So if you want to write handlers using another monad/type than `ExceptT
1162+ServantErr IO`, say the `Reader String` monad, the first thing you have to
10711163prepare is a function:
10721164
10731165``` haskell ignore
1074−readerToHandler :: Reader String a -> Handler a
1166+readerToEither :: Reader String :~> ExceptT ServantErr IO
10751167```
10761168
1077−We obviously have to run the `Reader` computation by supplying it with a
1078−`String`, like `"hi"`. We get an `a` out from that and can then just `return`
1079−it into `Handler`.
1169+Let's start with `readerToEither'`. We obviously have to run the `Reader`
1170+computation by supplying it with a `String`, like `"hi"`. We get an `a` out
1171+from that and can then just `return` it into `ExceptT`. We can then just wrap
1172+that function with the `Nat` constructor to make it have the fancier type.
10801173
10811174``` haskell
1082−readerToHandler :: Reader String a -> Handler a
1083−readerToHandler r = return (runReader r "hi")
1175+readerToEither' :: forall a. Reader String a -> ExceptT ServantErr IO a
1176+readerToEither' r = return (runReader r "hi")
1177+
1178+readerToEither :: Reader String :~> ExceptT ServantErr IO
1179+readerToEither = Nat readerToEither'
10841180```
10851181
10861182We can write some simple webservice with the handlers running in `Reader String`.
10871183
10881184``` haskell
10891185type ReaderAPI = "a" :> Get '[JSON] Int
1090− :<|> "b" :> ReqBody '[JSON] Double :> Get '[JSON] Bool
1186+ :<|> "b" :> Get '[JSON] String
10911187
10921188readerAPI :: Proxy ReaderAPI
10931189readerAPI = Proxy
10941190
10951191readerServerT :: ServerT ReaderAPI (Reader String)
1096−readerServerT = a :<|> b where
1097− a :: Reader String Int
1098− a = return 1797
1192+readerServerT = a :<|> b
1193+
1194+ where a :: Reader String Int
1195+ a = return 1797
10991196
1100− b :: Double -> Reader String Bool
1101− b _ = asks (== "hi")
1197+ b :: Reader String String
1198+ b = ask
11021199```
11031200
11041201We 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.
1202+`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `ExceptT
1203+ServantErr IO`. But there's a simple solution to this.
11061204
1107−### Welcome `hoistServer`
1205+Enter `enter`
1206+-------------
11081207
1109−That's right. We have just written `readerToHandler`, which is exactly what we
1110−would need to apply to all handlers to make the handlers have the
1208+That's right. We have just written `readerToEither`, which is exactly what we
1209+would need to apply to the results of all handlers to make the handlers have the
11111210right 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`
1211+`enter` which takes a natural transformation between two parametrized types `m`
11131212and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
11141213
1115−In our case, we can wrap up our little webservice by using
1116−`hoistServer readerAPI readerToHandler` on our handlers.
1214+In our case, we can wrap up our little webservice by using `enter readerToEither` on our handlers.
11171215
11181216``` haskell
11191217readerServer :: Server ReaderAPI
1120−readerServer = hoistServer readerAPI readerToHandler readerServerT
1218+readerServer = enter readerToEither readerServerT
11211219
11221220app4 :: Application
1123−app4 = serve readerAPI readerServer
1221+app4 = serve readerAPI EmptyConfig readerServer
11241222```
11251223
1126−This is the webservice in action:
1224+And we can indeed see this webservice in action by running `dist/build/tutorial/tutorial 7`.
11271225
11281226``` bash
11291227$ curl http://localhost:8081/a
113012281797
1131−$ curl http://localhost:8081/b -X GET -d '42.0' -H 'Content-Type: application/json'
1132−true
1133−```
1134−
1135−### An arrow is a reader too.
1136−
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.
1139−
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`.
1142−
1143−We can rewrite the previous example without `Reader`:
1144−
1145−```haskell
1146−funServerT :: ServerT ReaderAPI ((->) String)
1147−funServerT = a :<|> b where
1148− a :: String -> Int
1149− a _ = 1797
1150−
1151− -- unfortunately, we cannot make `String` the first argument.
1152− b :: Double -> String -> Bool
1153− b _ s = s == "hi"
1154−
1155−funToHandler :: (String -> a) -> Handler a
1156−funToHandler f = return (f "hi")
1157−
1158−app5 :: Application
1159−app5 = serve readerAPI (hoistServer readerAPI funToHandler funServerT)
1160−```
1161−
1162−## Streaming endpoints
1163−
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.
1174−
1175−``` haskell
1176−type StreamAPI = "userStream" :> StreamGet NewlineFraming JSON (SourceIO User)
1177−streamAPI :: Proxy StreamAPI
1178−streamAPI = Proxy
1179−
1180−streamUsers :: SourceIO User
1181−streamUsers = source [isaac, albert, albert]
1182−
1183−app6 :: Application
1184−app6 = serve streamAPI (return streamUsers)
1229+$ curl http://localhost:8081/b
1230+"hi"
11851231```
11861232
1187−This simple application returns a stream of `User` values encoded in JSON
1188−format, with each value separated by a newline. In this case, the stream will
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.
1233+Conclusion
1234+==========
11951235
1196−## Conclusion
1236+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*.
11971237
1198−You're now equipped to write webservices/web-applications using
1199−**servant**. The rest of this document focuses on **servant-client**,
1200−**servant-js** and **servant-docs**.
1238+<div style="text-align: center;">
1239+ <p><a href="/tutorial/api-type.html">Previous page: A web API as a type</a></p>
1240+ <p><a href="/tutorial/client.html">Next page: Deriving Haskell functions to query an API</a></p>
1241+</div>