on a7c3880 help sign in
servant/doc/tutorial/server.lhs
1256 lines 41 kB history raw
1---
2title: Serving an API
3toc: true
4---
6Enough chit-chat about type-level combinators and representing an API as a
7type. Can we have a webservice already?
9If you want to follow along with the code and run the examples while you read this guide:
11``` bash
12cabal get servant-examples
13cd servant-examples-<VERSION>
14cabal sandbox init
15cabal install --dependencies-only
16cabal configure && cabal build
17```
19This will produce a `tutorial` executable in the
20`dist/build/tutorial` directory that just runs the example corresponding
21to the number specified as a command line argument:
23``` bash
24$ dist/build/tutorial/tutorial
25Usage: tutorial N
26 where N is the number of the example you want to run.
27```
29A first example
30===============
32Equipped with some basic knowledge about the way we represent API, let's now write our first webservice.
34The source for this tutorial section is a literate haskell file, so first we
35need to have some language extensions and imports:
37``` haskell
38{-# LANGUAGE DataKinds #-}
39{-# LANGUAGE DeriveGeneric #-}
40{-# LANGUAGE FlexibleInstances #-}
41{-# LANGUAGE GeneralizedNewtypeDeriving #-}
42{-# LANGUAGE MultiParamTypeClasses #-}
43{-# LANGUAGE OverloadedStrings #-}
44{-# LANGUAGE ScopedTypeVariables #-}
45{-# LANGUAGE TypeOperators #-}
47module Server where
49import Control.Monad.IO.Class
50import Control.Monad.Reader
51import Control.Monad.Trans.Either
52import Data.Aeson
53import Data.Aeson.Types
54import Data.Attoparsec.ByteString
55import Data.ByteString (ByteString)
56import Data.Int
57import Data.List
58import Data.String.Conversions
59import Data.Time.Calendar
60import GHC.Generics
61import Lucid
62import Network.HTTP.Media ((//), (/:))
63import Network.Wai
64import Network.Wai.Handler.Warp
65import Servant
66import System.Directory
67import Text.Blaze
68import Text.Blaze.Html.Renderer.Utf8
69import qualified Data.Aeson.Parser
70import qualified Text.Blaze.Html
71```
73``` haskell ignore
74{-# LANGUAGE TypeFamilies #-}
75```
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.
79We will write a server that will serve the following API.
81``` haskell
82type UserAPI1 = "users" :> Get '[JSON] [User]
83```
85Here's what we would like to see when making a GET request to `/users`.
87``` javascript
88[ {"name": "Isaac Newton", "age": 372, "email": "isaac@newton.co.uk", "registration_date": "1683-03-01"}
89, {"name": "Albert Einstein", "age": 136, "email": "ae@mc2.org", "registration_date": "1905-12-01"}
91```
93Now let's define our `User` data type and write some instances for it.
95``` haskell
96data User = User
97 { name :: String
98 , age :: Int
99 , email :: String
100 , registration_date :: Day
101 } deriving (Eq, Show, Generic)
103instance ToJSON User
104```
106Nothing funny going on here. But we now can define our list of two users.
108``` haskell
109users1 :: [User]
110users1 =
111 [ User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
112 , User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
113 ]
114```
116Let's also write our API type.
118``` haskell ignore
119type UserAPI1 = "users" :> Get '[JSON] [User]
120```
122We 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
124endpoint. The type of the web application is determined by the API type,
125through a *type family* named `Server`. (Type families are just functions that
126take types as input and return types.) The `Server` type family will compute
127the right type that a bunch of request handlers should have just from the
128corresponding API type.
130The 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
132logic. The second thing to know is that for each endpoint, your handlers will
133by default run in the `EitherT ServantErr IO` monad. This is overridable very
134easily, 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
136HTTP method combinator used for the corresponding endpoint. In our case, it
137means we must provide a handler of type `EitherT ServantErr IO [User]`. Well,
138we have a monad, let's just `return` our list:
140``` haskell
141server1 :: Server UserAPI1
142server1 = return users1
143```
145That'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):
147``` haskell
148userAPI :: Proxy UserAPI1
149userAPI = Proxy
151-- 'serve' comes from servant and hands you a WAI Application,
152-- which you can think of as an "abstract" web application,
153-- not yet a webserver.
154app1 :: Application
155app1 = serve userAPI server1
156```
158The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
159But that's about as much boilerplate as you get.
161And we're done! Let's run our webservice on the port 8081.
163``` haskell
164main :: IO ()
165main = run 8081 app1
166```
168You can put this all into a file or just grab [servant's
169repo](http://github.com/haskell-servant/servant) and look at the
170*servant-examples* directory. The code we have just explored is in
171*tutorial/T1.hs*, runnable with
172`dist/build/tutorial/tutorial 1`.
174If you run it, you can go to `http://localhost:8081/users` in your browser or
175query it with curl and you see:
177``` bash
178$ 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"}]
180```
182More endpoints
183==============
185What if we want more than one endpoint? Let's add `/albert` and `/isaac` to view the corresponding users encoded in JSON.
187``` haskell
188type UserAPI2 = "users" :> Get '[JSON] [User]
189 :<|> "albert" :> Get '[JSON] User
190 :<|> "isaac" :> Get '[JSON] User
191```
193And let's adapt our code a bit.
195``` haskell
196isaac :: User
197isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
199albert :: User
200albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
202users2 :: [User]
203users2 = [isaac, albert]
204```
206Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
207are going to separate the handlers with `:<|>` too! They must be provided in
208the same order as the one they appear in in the API type.
210``` haskell
211server2 :: Server UserAPI2
212server2 = return users2
213 :<|> return albert
214 :<|> return isaac
215```
217And that's it! You can run this example with
218`dist/build/tutorial/tutorial 2` and check out the data available
219at `/users`, `/albert` and `/isaac`.
221From combinators to handler arguments
222=====================================
224Fine, 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`,
226`Capture` and `ReqBody` right away. You'll see how each occurence of these
227combinators in an endpoint makes the corresponding handler receive an
228argument of the appropriate type automatically. You don't have to worry about
229manually looking up URL captures or query string parameters, or
230decoding/encoding data from/to JSON. Never.
232We are going to use the following data types and functions to implement a server for `API`.
234``` haskell
235type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
236 :<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
237 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
239data Position = Position
240 { x :: Int
241 , y :: Int
242 } deriving Generic
244instance ToJSON Position
246newtype HelloMessage = HelloMessage { msg :: String }
247 deriving Generic
249instance ToJSON HelloMessage
251data ClientInfo = ClientInfo
252 { clientName :: String
253 , clientEmail :: String
254 , clientAge :: Int
255 , clientInterestedIn :: [String]
256 } deriving Generic
258instance FromJSON ClientInfo
259instance ToJSON ClientInfo
261data Email = Email
262 { from :: String
263 , to :: String
264 , subject :: String
265 , body :: String
266 } deriving Generic
268instance ToJSON Email
270emailForClient :: ClientInfo -> Email
271emailForClient c = Email from' to' subject' body'
273 where from' = "great@company.com"
274 to' = clientEmail c
275 subject' = "Hey " ++ clientName c ++ ", we miss you!"
276 body' = "Hi " ++ clientName c ++ ",\n\n"
277 ++ "Since you've recently turned " ++ show (clientAge c)
278 ++ ", have you checked out our latest "
279 ++ intercalate ", " (clientInterestedIn c)
280 ++ " products? Give us a visit!"
281```
283We can implement handlers for the three endpoints:
285``` haskell
286server3 :: Server API
287server3 = position
288 :<|> hello
289 :<|> marketing
291 where position :: Int -> Int -> EitherT ServantErr IO Position
292 position x y = return (Position x y)
294 hello :: Maybe String -> EitherT ServantErr IO HelloMessage
295 hello mname = return . HelloMessage $ case mname of
296 Nothing -> "Hello, anonymous coward"
297 Just n -> "Hello, " ++ n
299 marketing :: ClientInfo -> EitherT ServantErr IO Email
300 marketing clientinfo = return (emailForClient clientinfo)
301```
303Did you see that? The types for your handlers changed to be just what we
304needed! In particular:
306 - a `Capture "something" a` becomes an argument of type `a` (for `position`);
307 - a `QueryParam "something" a` becomes an argument of type `Maybe a` (because
308an endpoint can technically be accessed without specifying any query
309string parameter, we decided to "force" handlers to be aware that the
310parameter might not always be there);
312 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
314And that's it. You can see this example in action by running `dist/build/tutorial/tutorial 3`.
316``` bash
317$ curl http://localhost:8081/position/1/2
318{"x":1,"y":2}
319$ curl http://localhost:8081/hello
320{"msg":"Hello, anonymous coward"}
321$ curl http://localhost:8081/hello?name=Alp
322{"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
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"}
325```
327For reference, here's a list of some combinators from *servant* and for those
328that 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 `EitherT ServantErr IO <something>`.
331 > - `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.
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]`.
335 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
337The `FromText`/`ToText` classes
338===============================
340Wait... How does *servant* know how to decode the `Int`s from the URL? Or how
341to decode a `ClientInfo` value from the request body? This is what this and the
342following two sections address.
344`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
346corresponding (textual) value in the request's "metadata". This is why we
347decided to provide a pair of typeclasses, `FromText` and `ToText` which just
348let you say that you can respectively *extract* or *encode* values of some type
349*from*/*to* text. Here are the definitions:
351``` haskell ignore
352class FromText a where
353 fromText :: Text -> Maybe a
355class ToText a where
356 toText :: a -> Text
357```
359And as long as the type that a `Capture`/`QueryParam`/`Header`/etc will be
360decoded to provides a `FromText` instance, it will Just Work. *servant*
361provides a decent number of instances, but here are some examples of defining
362your own.
364``` haskell
365-- A typical enumeration
366data Direction
367 = Up
368 | Down
369 | Left
370 | Right
372instance FromText Direction where
373 -- requires {-# LANGUAGE OverloadedStrings #-}
374 fromText "up" = Just Up
375 fromText "down" = Just Down
376 fromText "left" = Just Server.Left
377 fromText "right" = Just Server.Right
378 fromText _ = Nothing
380instance ToText Direction where
381 toText Up = "up"
382 toText Down = "down"
383 toText Server.Left = "left"
384 toText Server.Right = "right"
386newtype UserId = UserId Int64
387 deriving (FromText, ToText)
388```
390or writing the instances by hand:
392``` haskell ignore
393instance FromText UserId where
394 fromText = fmap UserId fromText
396instance ToText UserId where
397 toText (UserId i) = toText i
398```
400There's not much else to say about these classes. You will need instances for
401them when using `Capture`, `QueryParam`, `QueryParams`, `MatrixParam`,
402`MatrixParams` and `Header` with your types. You will need `FromText` instances
403for server-side request handlers and `ToText` instances only when using
404*servant-client*, as described in the [section about deriving haskell
405functions to query an API](/tutorial/client.html).
407Using content-types with your data types
408========================================
410The same principle was operating when decoding request bodies from JSON, and
411responses *into* JSON. (JSON is just the running example - you can do this with
412any content-type.)
414This section introduces a couple of typeclasses provided by *servant* that make
415all of this work.
417The truth behind `JSON`
418-----------------------
420What exactly is `JSON`? Like the 3 other content types provided out of the box
421by *servant*, it's a really dumb data type.
423``` haskell ignore
424data JSON
425data PlainText
426data FormUrlEncoded
427data OctetStream
428```
430Obviously, this is not all there is to `JSON`, otherwise it would be quite
431pointless. Like most of the data types in *servant*, `JSON` is mostly there as
432a special *symbol* that's associated with encoding (resp. decoding) to (resp.
433from) the *JSON* format. The way this association is performed can be
434decomposed into two steps.
436The first step is to provide a proper
437[`MediaType`](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html)
438representation for `JSON`, or for your own content types. If you look at the
439haddocks from this link, you can see that we just have to specify
440`application/json` using the appropriate functions. In our case, we can just
441use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
442the `MediaType` is to write an instance for the `Accept` class:
444``` haskell ignore
445-- for reference:
446class Accept ctype where
447 contentType :: Proxy ctype -> MediaType
449instance Accept JSON where
450 contentType _ = "application" // "json"
451```
453The second step is centered around the `MimeRender` and `MimeUnrender` classes.
454These classes just let you specify a way to respectively encode and decode
455values respectively into or from your content-type's representation.
457``` haskell ignore
458class Accept ctype => MimeRender ctype a where
459 mimeRender :: Proxy ctype -> a -> ByteString
460 -- alternatively readable as:
461 mimeRender :: Proxy ctype -> (a -> ByteString)
462```
464Given a content-type and some user type, `MimeRender` provides a function that
465encodes values of type `a` to lazy `ByteString`s.
467In the case of `JSON`, this is easily dealt with! For any type `a` with a
468`ToJSON` instance, we can render values of that type to JSON using
469`Data.Aeson.encode`.
471``` haskell ignore
472instance ToJSON a => MimeRender JSON a where
473 mimeRender _ = encode
474```
476And now the `MimeUnrender` class, which lets us extract values from lazy
477`ByteString`s, alternatively failing with an error string.
479``` haskell ignore
480class Accept ctype => MimeUnrender ctype a where
481 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
482 -- alternatively:
483 mimeUnrender :: Proxy ctype -> (ByteString -> Either String a)
484```
486We don't have much work to do there either, `Data.Aeson.eitherDecode` is
487precisely what we need. However, it only allows arrays and objects as toplevel
488JSON values and this has proven to get in our way more than help us so we wrote
489our own little function around *aeson* and *attoparsec* that allows any type of
490JSON value at the toplevel of a "JSON document". Here's the definition in case
491you are curious.
493``` haskell
494eitherDecodeLenient :: FromJSON a => ByteString -> Either String a
495eitherDecodeLenient input = do
496 v :: Value <- parseOnly (Data.Aeson.Parser.value <* endOfInput) (cs input)
497 parseEither parseJSON v
498```
500This function is exactly what we need for our `MimeUnrender` instance.
502``` haskell ignore
503instance FromJSON a => MimeUnrender JSON a where
504 mimeUnrender _ = eitherDecodeLenient
505```
507And this is all the code that lets you use `JSON` for with `ReqBody`, `Get`,
508`Post` and friends. We can check our understanding by implementing support
509for an `HTML` content type, so that users of your webservice can access an
510HTML representation of the data they want, ready to be included in any HTML
511document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/), simply by adding `Accept:
512text/html` to their request headers.
514Case-studies: *servant-blaze* and *servant-lucid*
515-------------------------------------------------
517These days, most of the haskellers who write their HTML UIs directly from
518Haskell use either [blaze-html](http://hackage.haskell.org/package/blaze-html)
519or [lucid](http://hackage.haskell.org/package/lucid). The best option for
520*servant* is obviously to support both (and hopefully other templating
521solutions!).
523``` haskell
524data HTMLLucid
525```
527Once again, the data type is just there as a symbol for the encoding/decoding
528functions, except that this time we will only worry about encoding since
529*blaze-html* and *lucid* don't provide a way to extract data from HTML.
531Both packages also have the same `Accept` instance for their `HTMLLucid` type.
533``` haskell
534instance Accept HTMLLucid where
535 contentType _ = "text" // "html" /: ("charset", "utf-8")
536```
538Note that this instance uses the `(/:)` operator from *http-media* which lets
539us specify additional information about a content-type, like the charset here.
541The rendering instances for both packages both call similar functions that take
542types with an appropriate instance to an "abstract" HTML representation and
543then write that to a `ByteString`.
545For *lucid*:
547``` haskell
548instance ToHtml a => MimeRender HTMLLucid a where
549 mimeRender _ = renderBS . toHtml
551-- let's also provide an instance for lucid's
552-- 'Html' wrapper.
553instance MimeRender HTMLLucid (Html a) where
554 mimeRender _ = renderBS
555```
557For *blaze-html*:
559``` haskell
560-- For this tutorial to compile 'HTMLLucid' and 'HTMLBlaze' have to be
561-- distinct. Usually you would stick to one html rendering library and then
562-- you can go with one 'HTML' type.
563data HTMLBlaze
565instance Accept HTMLBlaze where
566 contentType _ = "text" // "html" /: ("charset", "utf-8")
568instance ToMarkup a => MimeRender HTMLBlaze a where
569 mimeRender _ = renderHtml . Text.Blaze.Html.toHtml
571-- while we're at it, just like for lucid we can
572-- provide an instance for rendering blaze's 'Html' type
573instance MimeRender HTMLBlaze Text.Blaze.Html.Html where
574 mimeRender _ = renderHtml
575```
577Both [servant-blaze](http://hackage.haskell.org/package/servant-blaze) and
578[servant-lucid](http://hackage.haskell.org/package/servant-lucid) let you use
579`HTMLLucid` in any content type list as long as you provide an instance of the
580appropriate class (`ToMarkup` for *blaze-html*, `ToHtml` for *lucid*).
582We can now write webservice that uses *servant-lucid* to show the `HTMLLucid`
583content type in action. First off, imports and pragmas as usual.
585We will be serving the following API:
587``` haskell
588type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
589```
591where `Person` is defined as follows:
593``` haskell
594data Person = Person
595 { firstName :: String
596 , lastName :: String
597 } deriving Generic -- for the JSON instance
599instance ToJSON Person
600```
602Now, let's teach *lucid* how to render a `Person` as a row in a table, and then
603a list of `Person`s as a table with a row per person.
605``` haskell
606-- HTML serialization of a single person
607instance ToHtml Person where
608 toHtml person =
609 tr_ $ do
610 td_ (toHtml $ firstName person)
611 td_ (toHtml $ lastName person)
613 -- do not worry too much about this
614 toHtmlRaw = toHtml
616-- HTML serialization of a list of persons
617instance ToHtml [Person] where
618 toHtml persons = table_ $ do
619 tr_ $ do
620 th_ "first name"
621 th_ "last name"
623 -- this just calls toHtml on each person of the list
624 -- and concatenates the resulting pieces of HTML together
625 foldMap toHtml persons
627 toHtmlRaw = toHtml
628```
630We create some `Person` values and serve them as a list:
632``` haskell
633persons :: [Person]
634persons =
635 [ Person "Isaac" "Newton"
636 , Person "Albert" "Einstein"
637 ]
639personAPI :: Proxy PersonAPI
640personAPI = Proxy
642server4 :: Server PersonAPI
643server4 = return persons
645app2 :: Application
646app2 = serve personAPI server4
647```
649And we're good to go. You can run this example with `dist/build/tutorial/tutorial 4`.
651``` bash
652 $ curl http://localhost:8081/persons
653 [{"lastName":"Newton","firstName":"Isaac"},{"lastName":"Einstein","firstName":"Albert"}]
654 $ curl -H 'Accept: text/html' http://localhost:8081/persons
655 <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>
656 # or just point your browser to http://localhost:8081/persons
657```
659The `EitherT ServantErr IO` monad
660=================================
662At the heart of the handlers is the monad they run in, namely `EitherT
663ServantErr IO`. One might wonder: why this monad? The answer is that it is the
664simplest monad with the following properties:
666- it lets us both return a successful result (with the `Right` branch of
667`Either`) or "fail" with a descriptive error (with the `Left` branch of
668`Either`);
669- it lets us perform IO, which is absolutely vital since most webservices exist
670as interfaces to databases that we interact with in `IO`;
672Let's recall some definitions.
674``` haskell ignore
675-- from the Prelude
676data Either e a = Left e | Right a
678-- from the 'either' package at
679-- http://hackage.haskell.org/package/either-4.3.3.2/docs/Control-Monad-Trans-Either.html
680newtype EitherT e m a
681 = EitherT { runEitherT :: m (Either e a) }
682```
684In short, this means that a handler of type `EitherT ServantErr IO a` is simply
685equivalent to a computation of type `IO (Either ServantErr a)`, that is, an IO
686action that either returns an error or a result.
688The aforementioned `either` package is worth taking a look at. Perhaps most
689importantly:
691``` haskell ignore
692left :: Monad m => e -> EitherT e m a
693```
694Allows you to return an error from your handler (whereas `return` is enough to
695return a success).
697Most of what you'll be doing in your handlers is running some IO and,
698depending on the result, you might sometimes want to throw an error of some
699kind and abort early. The next two sections cover how to do just that.
701Performing IO
702-------------
704Another important instance from the list above is `MonadIO m => MonadIO (EitherT 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:
706``` haskell ignore
707class Monad m => MonadIO m where
708 liftIO :: IO a -> m a
709```
711Obviously, the `IO` monad provides a `MonadIO` instance. Hence for any type `e`, `EitherT e IO` has a `MonadIO` instance. So if you want to run any kind of IO computation in your handlers, just use `liftIO`:
713``` haskell
714type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
716newtype FileContent = FileContent
717 { content :: String }
718 deriving Generic
720instance ToJSON FileContent
722server5 :: Server IOAPI1
723server5 = do
724 filecontent <- liftIO (readFile "myfile.txt")
725 return (FileContent filecontent)
726```
728Failing, through `ServantErr`
729-----------------------------
731If you want to explicitly fail at providing the result promised by an endpoint
732using the appropriate HTTP status code (not found, unauthorized, etc) and some
733error message, all you have to do is use the `left` function mentioned above
734and provide it with the appropriate value of type `ServantErr`, which is
735defined as:
737``` haskell ignore
738data ServantErr = ServantErr
739 { errHTTPCode :: Int
740 , errReasonPhrase :: String
741 , errBody :: ByteString -- lazy bytestring
742 , errHeaders :: [Header]
743 }
744```
746Many standard values are provided out of the box by the `Servant.Server`
747module. If you want to use these values but add a body or some headers, just
748use record update syntax:
750``` haskell
751failingHandler :: EitherT ServantErr IO ()
752failingHandler = left myerr
754 where myerr :: ServantErr
755 myerr = err503 { errBody = "Sorry dear user." }
756```
758Here's an example where we return a customised 404-Not-Found error message in
759the response body if "myfile.txt" isn't there:
761``` haskell
762server6 :: Server IOAPI1
763server6 = do
764 exists <- liftIO (doesFileExist "myfile.txt")
765 if exists
766 then liftIO (readFile "myfile.txt") >>= return . FileContent
767 else left custom404Err
769 where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }
770```
772Let's run this server (`dist/build/tutorial/tutorial 5`) and
773query it, first without the file and then with the file.
775``` bash
776 $ curl --verbose http://localhost:8081/myfile.txt
777 [snip]
778 * Connected to localhost (127.0.0.1) port 8081 (#0)
779 > GET /myfile.txt HTTP/1.1
780 > User-Agent: curl/7.30.0
781 > Host: localhost:8081
782 > Accept: */*
783 >
784 < HTTP/1.1 404 Not Found
785 [snip]
786 myfile.txt just isnt there, please leave this server alone.
788 $ echo Hello > myfile.txt
790 $ curl --verbose http://localhost:8081/myfile.txt
791 [snip]
792 * Connected to localhost (127.0.0.1) port 8081 (#0)
793 > GET /myfile.txt HTTP/1.1
794 > User-Agent: curl/7.30.0
795 > Host: localhost:8081
796 > Accept: */*
797 >
798 < HTTP/1.1 200 OK
799 [snip]
800 < Content-Type: application/json
801 [snip]
802 {"content":"Hello\n"}
803```
805Response headers
806================
808To add headers to your response, use [addHeader](http://hackage.haskell.org/package/servant-0.4.4/docs/Servant-API-ResponseHeaders.html).
809Note that this changes the type of your API, as we can see in the following example:
811``` haskell
812type MyHandler = Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
814myHandler :: Server MyHandler
815myHandler = return $ addHeader 1797 albert
816```
819Serving static files
820====================
822*servant-server* also provides a way to just serve the content of a directory
823under some path in your web API. As mentioned earlier in this document, the
824`Raw` combinator can be used in your APIs to mean "plug here any WAI
825application". Well, servant-server provides a function to get a file and
826directory serving WAI application, namely:
828``` haskell ignore
829-- exported by Servant and Servant.Server
830serveDirectory :: FilePath -> Server Raw
831```
833`serveDirectory`'s argument must be a path to a valid directory. You can see an
834example below, runnable with `dist/build/tutorial/tutorial 6`
835(you **must** run it from within the *servant-examples/* directory!), which is
836a webserver that serves the various bits of code covered in this
837getting-started.
839The API type will be the following.
841``` haskell
842type CodeAPI = "code" :> Raw
843```
845And the server:
847``` haskell
848codeAPI :: Proxy CodeAPI
849codeAPI = Proxy
850```
852``` haskell
853server7 :: Server CodeAPI
854server7 = serveDirectory "tutorial"
856app3 :: Application
857app3 = serve codeAPI server7
858```
860This 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.
862In other words:
864- If a client requests `/code/foo.txt`, the server will look for a file at `./tutorial/foo.txt` (and fail)
865- If a client requests `/code/T1.hs`, the server will look for a file at `./tutorial/T1.hs` (and succeed)
866- 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)
868Here is our little server in action.
870``` haskell ignore
871$ curl http://localhost:8081/code/T1.hs
872{-# LANGUAGE DataKinds #-}
873{-# LANGUAGE TypeFamilies #-}
874{-# LANGUAGE DeriveGeneric #-}
875{-# LANGUAGE TypeOperators #-}
876module T1 where
878import Data.Aeson
879import Data.Time.Calendar
880import GHC.Generics
881import Network.Wai
882import Servant
884data User = User
885 { name :: String
886 , age :: Int
887 , email :: String
888 , registration_date :: Day
889 } deriving (Eq, Show, Generic)
891-- orphan ToJSON instance for Day. necessary to derive one for User
892instance ToJSON Day where
893 -- display a day in YYYY-mm-dd format
894 toJSON d = toJSON (showGregorian d)
896instance ToJSON User
898type UserAPI = "users" :> Get '[JSON] [User]
900users :: [User]
901users =
902 [ User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
903 , User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
904 ]
906userAPI :: Proxy UserAPI
907userAPI = Proxy
909server :: Server UserAPI
910server = return users
912app :: Application
913app = serve userAPI server
914$ curl http://localhost:8081/code/tutorial.hs
915import Network.Wai
916import Network.Wai.Handler.Warp
917import System.Environment
919import qualified T1
920import qualified T2
921import qualified T3
922import qualified T4
923import qualified T5
924import qualified T6
925import qualified T7
926import qualified T9
927import qualified T10
929app :: String -> (Application -> IO ()) -> IO ()
930app n f = case n of
931 "1" -> f T1.app
932 "2" -> f T2.app
933 "3" -> f T3.app
934 "4" -> f T4.app
935 "5" -> f T5.app
936 "6" -> f T6.app
937 "7" -> f T7.app
938 "8" -> f T3.app
939 "9" -> T9.writeJSFiles >> f T9.app
940 "10" -> f T10.app
941 _ -> usage
943main :: IO ()
944main = do
945 args <- getArgs
946 case args of
947 [n] -> app n (run 8081)
948 _ -> usage
950usage :: IO ()
951usage = do
952 putStrLn "Usage:\t tutorial N"
953 putStrLn "\t\twhere N is the number of the example you want to run."
955$ curl http://localhost:8081/foo
956not found
957```
959Nested APIs
960===========
962Let's see how you can define APIs in a modular way, while avoiding repetition. Consider this simple example:
964``` haskell
965type UserAPI3 = -- view the user with given userid, in JSON
966 Capture "userid" Int :> Get '[JSON] User
968 :<|> -- delete the user with given userid. empty response
969 Capture "userid" Int :> Delete '[] ()
970```
972We can instead factor out the `userid`:
974``` haskell
975type UserAPI4 = Capture "userid" Int :>
976 ( Get '[JSON] User
977 :<|> Delete '[] ()
978 )
979```
981However, you have to be aware that this has an effect on the type of the corresponding `Server`:
983``` haskell ignore
984Server UserAPI3 = (Int -> EitherT ServantErr IO User)
985 :<|> (Int -> EitherT ServantErr IO ())
987Server UserAPI4 = Int -> ( EitherT ServantErr IO User
988 :<|> EitherT ServantErr IO ()
989 )
990```
992In the first case, each handler receives the *userid* argument. In the latter,
993the whole `Server` takes the *userid* and has handlers that are just computations in `EitherT`, with no arguments. In other words:
995``` haskell
996server8 :: Server UserAPI3
997server8 = getUser :<|> deleteUser
999 where getUser :: Int -> EitherT ServantErr IO User
1000 getUser _userid = error "..."
1002 deleteUser :: Int -> EitherT ServantErr IO ()
1003 deleteUser _userid = error "..."
1005-- notice how getUser and deleteUser
1006-- have a different type! no argument anymore,
1007-- the argument directly goes to the whole Server
1008server9 :: Server UserAPI4
1009server9 userid = getUser userid :<|> deleteUser userid
1011 where getUser :: Int -> EitherT ServantErr IO User
1012 getUser = error "..."
1014 deleteUser :: Int -> EitherT ServantErr IO ()
1015 deleteUser = error "..."
1016```
1018Note 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`.
1020``` haskell
1021-- we just factor out the "users" path fragment
1022type API1 = "users" :>
1023 ( Get '[JSON] [User] -- user listing
1024 :<|> Capture "userid" Int :> Get '[JSON] User -- view a particular user
1027-- we factor out the Request Body
1028type API2 = ReqBody '[JSON] User :>
1029 ( Get '[JSON] User -- just display the same user back, don't register it
1030 :<|> Post '[JSON] () -- register the user. empty response
1033-- we factor out a Header
1034type API3 = Header "Authorization" Token :>
1035 ( Get '[JSON] SecretData -- get some secret data, if authorized
1036 :<|> ReqBody '[JSON] SecretData :> Post '[] () -- add some secret data, if authorized
1039newtype Token = Token ByteString
1040newtype SecretData = SecretData ByteString
1041```
1043This approach lets you define APIs modularly and assemble them all into one big API type only at the end.
1045``` haskell
1046type UsersAPI =
1047 Get '[JSON] [User] -- list users
1048 :<|> ReqBody '[JSON] User :> Post '[] () -- add a user
1049 :<|> Capture "userid" Int :>
1050 ( Get '[JSON] User -- view a user
1051 :<|> ReqBody '[JSON] User :> Put '[] () -- update a user
1052 :<|> Delete '[] () -- delete a user
1055usersServer :: Server UsersAPI
1056usersServer = getUsers :<|> newUser :<|> userOperations
1058 where getUsers :: EitherT ServantErr IO [User]
1059 getUsers = error "..."
1061 newUser :: User -> EitherT ServantErr IO ()
1062 newUser = error "..."
1064 userOperations userid =
1065 viewUser userid :<|> updateUser userid :<|> deleteUser userid
1067 where
1068 viewUser :: Int -> EitherT ServantErr IO User
1069 viewUser = error "..."
1071 updateUser :: Int -> User -> EitherT ServantErr IO ()
1072 updateUser = error "..."
1074 deleteUser :: Int -> EitherT ServantErr IO ()
1075 deleteUser = error "..."
1076```
1078``` haskell
1079type ProductsAPI =
1080 Get '[JSON] [Product] -- list products
1081 :<|> ReqBody '[JSON] Product :> Post '[] () -- add a product
1082 :<|> Capture "productid" Int :>
1083 ( Get '[JSON] Product -- view a product
1084 :<|> ReqBody '[JSON] Product :> Put '[] () -- update a product
1085 :<|> Delete '[] () -- delete a product
1088data Product = Product { productId :: Int }
1090productsServer :: Server ProductsAPI
1091productsServer = getProducts :<|> newProduct :<|> productOperations
1093 where getProducts :: EitherT ServantErr IO [Product]
1094 getProducts = error "..."
1096 newProduct :: Product -> EitherT ServantErr IO ()
1097 newProduct = error "..."
1099 productOperations productid =
1100 viewProduct productid :<|> updateProduct productid :<|> deleteProduct productid
1102 where
1103 viewProduct :: Int -> EitherT ServantErr IO Product
1104 viewProduct = error "..."
1106 updateProduct :: Int -> Product -> EitherT ServantErr IO ()
1107 updateProduct = error "..."
1109 deleteProduct :: Int -> EitherT ServantErr IO ()
1110 deleteProduct = error "..."
1111```
1113``` haskell
1114type CombinedAPI = "users" :> UsersAPI
1115 :<|> "products" :> ProductsAPI
1117server10 :: Server CombinedAPI
1118server10 = usersServer :<|> productsServer
1119```
1121Finally, we can realize the user and product APIs are quite similar and abstract that away:
1123``` haskell
1124-- API for values of type 'a'
1125-- indexed by values of type 'i'
1126type APIFor a i =
1127 Get '[JSON] [a] -- list 'a's
1128 :<|> ReqBody '[JSON] a :> Post '[] () -- add an 'a'
1129 :<|> Capture "id" i :>
1130 ( Get '[JSON] a -- view an 'a' given its "identifier" of type 'i'
1131 :<|> ReqBody '[JSON] a :> Put '[] () -- update an 'a'
1132 :<|> Delete '[] () -- delete an 'a'
1135-- Build the appropriate 'Server'
1136-- given the handlers of the right type.
1137serverFor :: EitherT ServantErr IO [a] -- handler for listing of 'a's
1138 -> (a -> EitherT ServantErr IO ()) -- handler for adding an 'a'
1139 -> (i -> EitherT ServantErr IO a) -- handler for viewing an 'a' given its identifier of type 'i'
1140 -> (i -> a -> EitherT ServantErr IO ()) -- updating an 'a' with given id
1141 -> (i -> EitherT ServantErr IO ()) -- deleting an 'a' given its id
1142 -> Server (APIFor a i)
1143serverFor = error "..."
1144-- implementation left as an exercise. contact us on IRC
1145-- or the mailing list if you get stuck!
1146```
1148Using another monad for your handlers
1149=====================================
1151Remember how `Server` turns combinators for HTTP methods into `EitherT ServantErr IO`? Well, actually, there's more to that. `Server` is actually a simple type synonym.
1153``` haskell ignore
1154type Server api = ServerT api (EitherT ServantErr IO)
1155```
1157`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`.
1159The 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?
1161Natural transformations
1162-----------------------
1164If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1165do we have?
1167``` haskell ignore
1168newtype m :~> n = Nat { unNat :: forall a. m a -> n a}
1170-- For example
1171-- listToMaybeNat ::`[] :~> Maybe`
1172-- listToMaybeNat = Nat listToMaybe -- from Data.Maybe
1173```
1174(`Nat` comes from "natural transformation", in case you're wondering.)
1176So if you want to write handlers using another monad/type than `EitherT
1177ServantErr IO`, say the `Reader String` monad, the first thing you have to
1178prepare is a function:
1180``` haskell ignore
1181readerToEither :: Reader String :~> EitherT ServantErr IO
1182```
1184Let's start with `readerToEither'`. We obviously have to run the `Reader`
1185computation by supplying it with a `String`, like `"hi"`. We get an `a` out
1186from that and can then just `return` it into `EitherT`. We can then just wrap
1187that function with the `Nat` constructor to make it have the fancier type.
1189``` haskell
1190readerToEither' :: forall a. Reader String a -> EitherT ServantErr IO a
1191readerToEither' r = return (runReader r "hi")
1193readerToEither :: Reader String :~> EitherT ServantErr IO
1194readerToEither = Nat readerToEither'
1195```
1197We can write some simple webservice with the handlers running in `Reader String`.
1199``` haskell
1200type ReaderAPI = "a" :> Get '[JSON] Int
1201 :<|> "b" :> Get '[JSON] String
1203readerAPI :: Proxy ReaderAPI
1204readerAPI = Proxy
1206readerServerT :: ServerT ReaderAPI (Reader String)
1207readerServerT = a :<|> b
1209 where a :: Reader String Int
1210 a = return 1797
1212 b :: Reader String String
1213 b = ask
1214```
1216We unfortunately can't use `readerServerT` as an argument of `serve`, because
1217`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `EitherT
1218ServantErr IO`. But there's a simple solution to this.
1220Enter `enter`
1221-------------
1223That's right. We have just written `readerToEither`, which is exactly what we
1224would need to apply to the results of all handlers to make the handlers have the
1225right type for `serve`. Being cumbersome to do by hand, we provide a function
1226`enter` which takes a natural transformation between two parametrized types `m`
1227and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1229In our case, we can wrap up our little webservice by using `enter readerToEither` on our handlers.
1231``` haskell
1232readerServer :: Server ReaderAPI
1233readerServer = enter readerToEither readerServerT
1235app4 :: Application
1236app4 = serve readerAPI readerServer
1237```
1239And we can indeed see this webservice in action by running `dist/build/tutorial/tutorial 7`.
1241``` bash
1242$ curl http://localhost:8081/a
12431797
1244$ curl http://localhost:8081/b
1245"hi"
1246```
1248Conclusion
1249==========
1251You'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*.
1253<div style="text-align: center;">
1254 <p><a href="/tutorial/api-type.html">Previous page: A web API as a type</a></p>
1255 <p><a href="/tutorial/client.html">Next page: Deriving Haskell functions to query an API</a></p>
1256</div>