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