on 6995e39 help sign in
servant/doc/tutorial/Server.lhs
1147 lines 38 kB history raw
1# Serving an API
3Enough chit-chat about type-level combinators and representing an API as a
4type. Can we have a webservice already?
6## A first example
8Equipped with some basic knowledge about the way we represent APIs, let's now
9write our first webservice.
11The source for this tutorial section is a literate haskell file, so first we
12need to have some language extensions and imports:
14``` haskell
15{-# LANGUAGE DataKinds #-}
16{-# LANGUAGE DeriveGeneric #-}
17{-# LANGUAGE FlexibleInstances #-}
18{-# LANGUAGE GeneralizedNewtypeDeriving #-}
19{-# LANGUAGE MultiParamTypeClasses #-}
20{-# LANGUAGE OverloadedStrings #-}
21{-# LANGUAGE ScopedTypeVariables #-}
22{-# LANGUAGE TypeOperators #-}
24module Server where
26import Prelude ()
27import Prelude.Compat
29import Control.Monad.Except
30import Control.Monad.Reader
31import Data.Aeson.Compat
32import Data.Aeson.Types
33import Data.Attoparsec.ByteString
34import Data.ByteString (ByteString)
35import Data.List
36import Data.Maybe
37import Data.String.Conversions
38import Data.Time.Calendar
39import GHC.Generics
40import Lucid
41import Network.HTTP.Media ((//), (/:))
42import Network.Wai
43import Network.Wai.Handler.Warp
44import Servant
45import System.Directory
46import Text.Blaze
47import Text.Blaze.Html.Renderer.Utf8
48import qualified Data.Aeson.Parser
49import qualified Text.Blaze.Html
50```
52**Important**: the `Servant` module comes from the **servant-server** package,
53the one that lets us run webservers that implement a particular API type. It
54reexports all the types from the **servant** package that let you declare API
55types as well as everything you need to turn your request handlers into a
56fully-fledged webserver. This means that in your applications, you can just add
57**servant-server** as a dependency, import `Servant` and not worry about anything
58else.
60We will write a server that will serve the following API.
62``` haskell
63type UserAPI1 = "users" :> Get '[JSON] [User]
64```
66Here's what we would like to see when making a GET request to `/users`.
68``` javascript
69[ {"name": "Isaac Newton", "age": 372, "email": "isaac@newton.co.uk", "registration_date": "1683-03-01"}
70, {"name": "Albert Einstein", "age": 136, "email": "ae@mc2.org", "registration_date": "1905-12-01"}
72```
74Now let's define our `User` data type and write some instances for it.
76``` haskell
77data User = User
78 { name :: String
79 , age :: Int
80 , email :: String
81 , registration_date :: Day
82 } deriving (Eq, Show, Generic)
84instance ToJSON User
85```
87Nothing funny going on here. But we now can define our list of two users.
89``` haskell
90users1 :: [User]
91users1 =
92 [ User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
93 , User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
94 ]
95```
97Let's also write our API type.
99``` haskell ignore
100type UserAPI1 = "users" :> Get '[JSON] [User]
101```
103We can now take care of writing the actual webservice that will handle requests
104to such an API. This one will be very simple, being reduced to just a single
105endpoint. The type of the web application is determined by the API type,
106through a *type family* named `Server`. (Type families are just functions that
107take types as input and return types.) The `Server` type family will compute
108the right type that a bunch of request handlers should have just from the
109corresponding API type.
111The first thing to know about the `Server` type family is that behind the
112scenes it will drive the routing, letting you focus only on the business
113logic. The second thing to know is that for each endpoint, your handlers will
114by default run in the `Handler` monad. This is overridable very
115easily, as explained near the end of this guide. Third thing, the type of the
116value returned in that monad must be the same as the second argument of the
117HTTP method combinator used for the corresponding endpoint. In our case, it
118means we must provide a handler of type `Handler [User]`. Well,
119we have a monad, let's just `return` our list:
121``` haskell
122server1 :: Server UserAPI1
123server1 = return users1
124```
126That's it. Now we can turn `server` into an actual webserver using
127[wai](http://hackage.haskell.org/package/wai) and
128[warp](http://hackage.haskell.org/package/warp):
130``` haskell
131userAPI :: Proxy UserAPI1
132userAPI = Proxy
134-- 'serve' comes from servant and hands you a WAI Application,
135-- which you can think of as an "abstract" web application,
136-- not yet a webserver.
137app1 :: Application
138app1 = serve userAPI server1
139```
141The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
142But that's about as much boilerplate as you get.
144And we're done! Let's run our webservice on the port 8081.
146``` haskell
147main :: IO ()
148main = run 8081 app1
149```
151You can put this all into a file or just grab [servant's
152repo](http://github.com/haskell-servant/servant) and look at the
153*doc/tutorial* directory. This code (the source of this web page) is in
154*doc/tutorial/Server.lhs*.
156If you run it, you can go to `http://localhost:8081/users` in your browser or
157query it with curl and you see:
159``` bash
160$ curl http://localhost:8081/users
161[{"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"}]
162```
164## More endpoints
166What if we want more than one endpoint? Let's add `/albert` and `/isaac` to
167view the corresponding users encoded in JSON.
169``` haskell
170type UserAPI2 = "users" :> Get '[JSON] [User]
171 :<|> "albert" :> Get '[JSON] User
172 :<|> "isaac" :> Get '[JSON] User
173```
175And let's adapt our code a bit.
177``` haskell
178isaac :: User
179isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
181albert :: User
182albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
184users2 :: [User]
185users2 = [isaac, albert]
186```
188Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
189are going to separate the handlers with `:<|>` too! They must be provided in
190the same order as in in the API type.
192``` haskell
193server2 :: Server UserAPI2
194server2 = return users2
195 :<|> return albert
196 :<|> return isaac
197```
199And that's it! You can run this example in the same way that we showed for
200`server1` and check out the data available at `/users`, `/albert` and `/isaac`.
202## From combinators to handler arguments
204Fine, we can write trivial webservices easily, but none of the two above use
205any "fancy" combinator from servant. Let's address this and use `QueryParam`,
206`Capture` and `ReqBody` right away. You'll see how each occurence of these
207combinators in an endpoint makes the corresponding handler receive an
208argument of the appropriate type automatically. You don't have to worry about
209manually looking up URL captures or query string parameters, or
210decoding/encoding data from/to JSON. Never.
212We are going to use the following data types and functions to implement a
213server for `API`.
215``` haskell
216type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
217 :<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
218 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
220data Position = Position
221 { xCoord :: Int
222 , yCoord :: Int
223 } deriving Generic
225instance ToJSON Position
227newtype HelloMessage = HelloMessage { msg :: String }
228 deriving Generic
230instance ToJSON HelloMessage
232data ClientInfo = ClientInfo
233 { clientName :: String
234 , clientEmail :: String
235 , clientAge :: Int
236 , clientInterestedIn :: [String]
237 } deriving Generic
239instance FromJSON ClientInfo
240instance ToJSON ClientInfo
242data Email = Email
243 { from :: String
244 , to :: String
245 , subject :: String
246 , body :: String
247 } deriving Generic
249instance ToJSON Email
251emailForClient :: ClientInfo -> Email
252emailForClient c = Email from' to' subject' body'
254 where from' = "great@company.com"
255 to' = clientEmail c
256 subject' = "Hey " ++ clientName c ++ ", we miss you!"
257 body' = "Hi " ++ clientName c ++ ",\n\n"
258 ++ "Since you've recently turned " ++ show (clientAge c)
259 ++ ", have you checked out our latest "
260 ++ intercalate ", " (clientInterestedIn c)
261 ++ " products? Give us a visit!"
262```
264We can implement handlers for the three endpoints:
266``` haskell
267server3 :: Server API
268server3 = position
269 :<|> hello
270 :<|> marketing
272 where position :: Int -> Int -> Handler Position
273 position x y = return (Position x y)
275 hello :: Maybe String -> Handler HelloMessage
276 hello mname = return . HelloMessage $ case mname of
277 Nothing -> "Hello, anonymous coward"
278 Just n -> "Hello, " ++ n
280 marketing :: ClientInfo -> Handler Email
281 marketing clientinfo = return (emailForClient clientinfo)
282```
284Did you see that? The types for your handlers changed to be just what we
285needed! In particular:
287 - a `Capture "something" a` becomes an argument of type `a` (for `position`);
288 - a `QueryParam "something" a` becomes an argument of type `Maybe a` (because
289an endpoint can technically be accessed without specifying any query
290string parameter, we decided to "force" handlers to be aware that the
291parameter might not always be there);
293 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
295And that's it. Here's the example in action:
297``` bash
298$ curl http://localhost:8081/position/1/2
299{"xCoord":1,"yCoord":2}
300$ curl http://localhost:8081/hello
301{"msg":"Hello, anonymous coward"}
302$ curl http://localhost:8081/hello?name=Alp
303{"msg":"Hello, Alp"}
304$ 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
305{"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"}
306```
308For reference, here's a list of some combinators from **servant**:
310 > - `Delete`, `Get`, `Patch`, `Post`, `Put`: these do not become arguments. They provide the return type of handlers, which usually is `Handler <something>`.
311 > - `Capture "something" a` becomes an argument of type `a`.
312 > - `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.
313 > - `QueryFlag "something"` gets turned into an argument of type `Bool`.
314 > - `QueryParams "something" a` gets turned into an argument of type `[a]`.
315 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
317## The `FromHttpApiData`/`ToHttpApiData` classes
319Wait... How does **servant** know how to decode the `Int`s from the URL? Or how
320to decode a `ClientInfo` value from the request body? This is what this and the
321following two sections address.
323`Capture`s and `QueryParam`s are represented by some textual value in URLs.
324`Header`s are similarly represented by a pair of a header name and a
325corresponding (textual) value in the request's "metadata". How types are
326decoded from headers, captures, and query params is expressed in a class
327`FromHttpApiData` (from the package
328[**http-api-data**](http://hackage.haskell.org/package/http-api-data)):
330``` haskell ignore
331class FromHttpApiData a where
332 {-# MINIMAL parseUrlPiece | parseQueryParam #-}
333 -- | Parse URL path piece.
334 parseUrlPiece :: Text -> Either Text a
335 parseUrlPiece = parseQueryParam
337 -- | Parse HTTP header value.
338 parseHeader :: ByteString -> Either Text a
339 parseHeader = parseUrlPiece . decodeUtf8
341 -- | Parse query param value.
342 parseQueryParam :: Text -> Either Text a
343 parseQueryParam = parseUrlPiece
344```
346As you can see, as long as you provide either `parseUrlPiece` (for `Capture`s)
347or `parseQueryParam` (for `QueryParam`s), the other methods will be defined in
348terms of this.
350**http-api-data** provides a decent number of instances, helpers for defining new
351ones, and wonderful documentation.
353There's not much else to say about these classes. You will need instances for
354them when using `Capture`, `QueryParam`, `QueryParams`, and `Header` with your
355types. You will need `FromHttpApiData` instances for server-side request
356handlers and `ToHttpApiData` instances only when using
357**servant-client**, as described in the [section about deriving haskell
358functions to query an API](Client.html).
360## Using content-types with your data types
362The same principle was operating when decoding request bodies from JSON, and
363responses *into* JSON. (JSON is just the running example - you can do this with
364any content-type.)
366This section introduces a couple of typeclasses provided by **servant** that make
367all of this work.
369### The truth behind `JSON`
372What exactly is `JSON` (the type as used in `Get '[JSON] User`)? Like the 3
373other content-types provided out of the box by **servant**, it's a really dumb
374data type.
376``` haskell ignore
377data JSON
378data PlainText
379data FormUrlEncoded
380data OctetStream
381```
383Obviously, this is not all there is to `JSON`, otherwise it would be quite
384pointless. Like most of the data types in **servant**, `JSON` is mostly there as
385a special *symbol* that's associated with encoding (resp. decoding) to (resp.
386from) the *JSON* format. The way this association is performed can be
387decomposed into two steps.
389The first step is to provide a proper
390`MediaType` (from
391[**http-media**](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html))
392representation for `JSON`, or for your own content-types. If you look at the
393haddocks from this link, you can see that we just have to specify
394`application/json` using the appropriate functions. In our case, we can just
395use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
396the `MediaType` is to write an instance for the `Accept` class:
398``` haskell ignore
399-- for reference:
400class Accept ctype where
401 contentType :: Proxy ctype -> MediaType
403instance Accept JSON where
404 contentType _ = "application" // "json"
405```
407The second step is centered around the `MimeRender` and `MimeUnrender` classes.
408These classes just let you specify a way to encode and decode
409values into or from your content-type's representation.
411``` haskell ignore
412class Accept ctype => MimeRender ctype a where
413 mimeRender :: Proxy ctype -> a -> ByteString
414 -- alternatively readable as:
415 mimeRender :: Proxy ctype -> (a -> ByteString)
416```
418Given a content-type and some user type, `MimeRender` provides a function that
419encodes values of type `a` to lazy `ByteString`s.
421In the case of `JSON`, this is easily dealt with! For any type `a` with a
422`ToJSON` instance, we can render values of that type to JSON using
423`Data.Aeson.encode`.
425``` haskell ignore
426instance ToJSON a => MimeRender JSON a where
427 mimeRender _ = encode
428```
430And now the `MimeUnrender` class, which lets us extract values from lazy
431`ByteString`s, alternatively failing with an error string.
433``` haskell ignore
434class Accept ctype => MimeUnrender ctype a where
435 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
436```
438We don't have much work to do there either, `Data.Aeson.eitherDecode` is
439precisely what we need. However, it only allows arrays and objects as toplevel
440JSON values and this has proven to get in our way more than help us so we wrote
441our own little function around **aeson** and **attoparsec** that allows any type of
442JSON value at the toplevel of a "JSON document". Here's the definition in case
443you are curious.
445``` haskell
446eitherDecodeLenient :: FromJSON a => ByteString -> Either String a
447eitherDecodeLenient input = do
448 v :: Value <- parseOnly (Data.Aeson.Parser.value <* endOfInput) (cs input)
449 parseEither parseJSON v
450```
452This function is exactly what we need for our `MimeUnrender` instance.
454``` haskell ignore
455instance FromJSON a => MimeUnrender JSON a where
456 mimeUnrender _ = eitherDecodeLenient
457```
459And this is all the code that lets you use `JSON` with `ReqBody`, `Get`,
460`Post` and friends. We can check our understanding by implementing support
461for an `HTML` content-type, so that users of your webservice can access an
462HTML representation of the data they want, ready to be included in any HTML
463document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/),
464simply by adding `Accept: text/html` to their request headers.
466### Case-studies: **servant-blaze** and **servant-lucid**
468These days, most of the haskellers who write their HTML UIs directly from
469Haskell use either [**blaze-html**](http://hackage.haskell.org/package/blaze-html)
470or [**lucid**](http://hackage.haskell.org/package/lucid). The best option for
471**servant** is obviously to support both (and hopefully other templating
472solutions!). We're first going to look at **lucid**:
474``` haskell
475data HTMLLucid
476```
478Once again, the data type is just there as a symbol for the encoding/decoding
479functions, except that this time we will only worry about encoding since
480**lucid** doesn't provide a way to extract data from HTML.
482``` haskell
483instance Accept HTMLLucid where
484 contentType _ = "text" // "html" /: ("charset", "utf-8")
485```
487Note that this instance uses the `(/:)` operator from **http-media** which lets
488us specify additional information about a content-type, like the charset here.
490The rendering instances call similar functions that take
491types with an appropriate instance to an "abstract" HTML representation and
492then write that to a `ByteString`.
494``` haskell
495instance ToHtml a => MimeRender HTMLLucid a where
496 mimeRender _ = renderBS . toHtml
498-- let's also provide an instance for lucid's
499-- 'Html' wrapper.
500instance MimeRender HTMLLucid (Html a) where
501 mimeRender _ = renderBS
502```
504For **blaze-html** everything works very similarly:
506``` haskell
507-- For this tutorial to compile 'HTMLLucid' and 'HTMLBlaze' have to be
508-- distinct. Usually you would stick to one html rendering library and then
509-- you can go with one 'HTML' type.
510data HTMLBlaze
512instance Accept HTMLBlaze where
513 contentType _ = "text" // "html" /: ("charset", "utf-8")
515instance ToMarkup a => MimeRender HTMLBlaze a where
516 mimeRender _ = renderHtml . Text.Blaze.Html.toHtml
518-- while we're at it, just like for lucid we can
519-- provide an instance for rendering blaze's 'Html' type
520instance MimeRender HTMLBlaze Text.Blaze.Html.Html where
521 mimeRender _ = renderHtml
522```
524Both [**servant-blaze**](http://hackage.haskell.org/package/servant-blaze) and
525[**servant-lucid**](http://hackage.haskell.org/package/servant-lucid) let you use
526`HTMLLucid` and `HTMLBlaze` in any content-type list as long as you provide an instance of the
527appropriate class (`ToMarkup` for **blaze-html**, `ToHtml` for **lucid**).
529We can now write a webservice that uses **servant-lucid** to show the `HTMLLucid`
530content-type in action. We will be serving the following API:
532``` haskell
533type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
534```
536where `Person` is defined as follows:
538``` haskell
539data Person = Person
540 { firstName :: String
541 , lastName :: String
542 } deriving Generic -- for the JSON instance
544instance ToJSON Person
545```
547Now, let's teach **lucid** how to render a `Person` as a row in a table, and then
548a list of `Person`s as a table with a row per person.
550``` haskell
551-- HTML serialization of a single person
552instance ToHtml Person where
553 toHtml person =
554 tr_ $ do
555 td_ (toHtml $ firstName person)
556 td_ (toHtml $ lastName person)
558 -- do not worry too much about this
559 toHtmlRaw = toHtml
561-- HTML serialization of a list of persons
562instance ToHtml [Person] where
563 toHtml persons = table_ $ do
564 tr_ $ do
565 th_ "first name"
566 th_ "last name"
568 -- this just calls toHtml on each person of the list
569 -- and concatenates the resulting pieces of HTML together
570 foldMap toHtml persons
572 toHtmlRaw = toHtml
573```
575We create some `Person` values and serve them as a list:
577``` haskell
578people :: [Person]
579people =
580 [ Person "Isaac" "Newton"
581 , Person "Albert" "Einstein"
582 ]
584personAPI :: Proxy PersonAPI
585personAPI = Proxy
587server4 :: Server PersonAPI
588server4 = return people
590app2 :: Application
591app2 = serve personAPI server4
592```
594And we're good to go:
596``` bash
597$ curl http://localhost:8081/persons
598[{"lastName":"Newton","firstName":"Isaac"},{"lastName":"Einstein","firstName":"Albert"}]
599$ curl -H 'Accept: text/html' http://localhost:8081/persons
600<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>
601# or just point your browser to http://localhost:8081/persons
602```
604## The `Handler` monad
606At the heart of the handlers is the monad they run in, namely a newtype `Handler` around `ExceptT ServantErr IO`
607([haddock documentation for `ExceptT`](http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT)).
608One might wonder: why this monad? The answer is that it is the
609simplest monad with the following properties:
611- it lets us both return a successful result (using `return`)
612or "fail" with a descriptive error (using `throwError`);
613- it lets us perform IO, which is absolutely vital since most webservices exist
614as interfaces to databases that we interact with in `IO`.
616Let's recall some definitions.
618``` haskell ignore
619-- from the 'mtl' package at
620newtype ExceptT e m a = ExceptT (m (Either e a))
621```
623In short, this means that a handler of type `Handler a` is simply
624equivalent to a computation of type `IO (Either ServantErr a)`, that is, an IO
625action that either returns an error or a result.
627The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT)
628from which `ExceptT` comes is worth looking at.
629Perhaps most importantly, `ExceptT` and `Handler` are an instances of `MonadError`, so
630`throwError` can be used to return an error from your handler (whereas `return`
631 is enough to return a success).
633Most of what you'll be doing in your handlers is running some IO and,
634depending on the result, you might sometimes want to throw an error of some
635kind and abort early. The next two sections cover how to do just that.
637### Performing IO
639Another important instances from the list above are `MonadIO m => MonadIO
640(ExceptT e m)`, and therefore also `MonadIO Handler` as there is `MonadIO IO` instance..
641[`MonadIO`](http://hackage.haskell.org/package/transformers-0.4.3.0/docs/Control-Monad-IO-Class.html)
642is a class from the **transformers** package defined as:
644``` haskell ignore
645class Monad m => MonadIO m where
646 liftIO :: IO a -> m a
647```
649So if you want to run any kind of
650IO computation in your handlers, just use `liftIO`:
652``` haskell
653type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
655newtype FileContent = FileContent
656 { content :: String }
657 deriving Generic
659instance ToJSON FileContent
661server5 :: Server IOAPI1
662server5 = do
663 filecontent <- liftIO (readFile "myfile.txt")
664 return (FileContent filecontent)
665```
667### Failing, through `ServantErr`
669If you want to explicitly fail at providing the result promised by an endpoint
670using the appropriate HTTP status code (not found, unauthorized, etc) and some
671error message, all you have to do is use the `throwError` function mentioned above
672and provide it with the appropriate value of type `ServantErr`, which is
673defined as:
675``` haskell ignore
676data ServantErr = ServantErr
677 { errHTTPCode :: Int
678 , errReasonPhrase :: String
679 , errBody :: ByteString -- lazy bytestring
680 , errHeaders :: [Header]
681 }
682```
684Many standard values are provided out of the box by the `Servant.Server`
685module. If you want to use these values but add a body or some headers, just
686use record update syntax:
688``` haskell
689failingHandler :: Handler ()
690failingHandler = throwError myerr
692 where myerr :: ServantErr
693 myerr = err503 { errBody = "Sorry dear user." }
694```
696Here's an example where we return a customised 404-Not-Found error message in
697the response body if "myfile.txt" isn't there:
699``` haskell
700server6 :: Server IOAPI1
701server6 = do
702 exists <- liftIO (doesFileExist "myfile.txt")
703 if exists
704 then liftIO (readFile "myfile.txt") >>= return . FileContent
705 else throwError custom404Err
707 where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }
708```
710Here's how that server looks in action:
712``` bash
713$ curl --verbose http://localhost:8081/myfile.txt
714[snip]
715* Connected to localhost (127.0.0.1) port 8081 (#0)
716> GET /myfile.txt HTTP/1.1
717> User-Agent: curl/7.30.0
718> Host: localhost:8081
719> Accept: */*
721< HTTP/1.1 404 Not Found
722[snip]
723myfile.txt just isnt there, please leave this server alone.
725$ echo Hello > myfile.txt
727$ curl --verbose http://localhost:8081/myfile.txt
728[snip]
729* Connected to localhost (127.0.0.1) port 8081 (#0)
730> GET /myfile.txt HTTP/1.1
731> User-Agent: curl/7.30.0
732> Host: localhost:8081
733> Accept: */*
735< HTTP/1.1 200 OK
736[snip]
737< Content-Type: application/json
738[snip]
739{"content":"Hello\n"}
740```
742## Response headers
744To add headers to your response, use
745[addHeader](http://hackage.haskell.org/package/servant/docs/Servant-API-ResponseHeaders.html).
746Note that this changes the type of your API, as we can see in the following example:
748``` haskell
749type MyHandler = Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
751myHandler :: Server MyHandler
752myHandler = return $ addHeader 1797 albert
753```
755Note that the type of `addHeader header x` is different than the type of `x`!
756And if you add more headers, more headers will appear in the header list:
758``` haskell
759type MyHeadfulHandler = Get '[JSON] (Headers '[Header "X-A-Bool" Bool, Header "X-An-Int" Int] User)
761myHeadfulHandler :: Server MyHeadfulHandler
762myHeadfulHandler = return $ addHeader True $ addHeader 1797 albert
763```
765But what if your handler only *sometimes* adds a header? If you declare that
766your handler adds headers, and you don't add one, the return type of your
767handler will be different than expected. To solve this, you have to explicitly
768*not* add a header by using `noHeader`:
770``` haskell
771type MyMaybeHeaderHandler
772 = Capture "withHeader" Bool :> Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
774myMaybeHeaderHandler :: Server MyMaybeHeaderHandler
775myMaybeHeaderHandler x = return $ if x then addHeader 1797 albert
776 else noHeader albert
777```
779## Serving static files
781**servant-server** also provides a way to just serve the content of a directory
782under some path in your web API. As mentioned earlier in this document, the
783`Raw` combinator can be used in your APIs to mean "plug here any WAI
784application". Well, **servant-server** provides a function to get a file and
785directory serving WAI application, namely:
787``` haskell ignore
788-- exported by Servant and Servant.Server
789serveDirectoryWebApp :: FilePath -> Server Raw
790```
792`serveDirectoryWebApp`'s argument must be a path to a valid directory.
794Here's an example API that will serve some static files:
796``` haskell
797type StaticAPI = "static" :> Raw
798```
800And the server:
802``` haskell
803staticAPI :: Proxy StaticAPI
804staticAPI = Proxy
805```
807``` haskell
808server7 :: Server StaticAPI
809server7 = serveDirectoryWebApp "static-files"
811app3 :: Application
812app3 = serve staticAPI server7
813```
815This server will match any request whose path starts with `/static` and will look
816for a file at the path described by the rest of the request path, inside the
817 *static-files/* directory of the path you run the program from.
819In other words: If a client requests `/static/foo.txt`, the server will look for a file at
820`./static-files/foo.txt`. If that file exists it'll succeed and serve the file.
821If it doesn't exist, the handler will fail with a `404` status code.
823`serveDirectoryWebApp` uses some standard settings that fit the use case of
824serving static files for most web apps. You can find out about the other
825options in the documentation of the `Servant.Utils.StaticFiles` module.
827## Nested APIs
829Let's see how you can define APIs in a modular way, while avoiding repetition.
830Consider this simple example:
832``` haskell
833type UserAPI3 = -- view the user with given userid, in JSON
834 Capture "userid" Int :> Get '[JSON] User
836 :<|> -- delete the user with given userid. empty response
837 Capture "userid" Int :> DeleteNoContent '[JSON] NoContent
838```
840We can instead factor out the `userid`:
842``` haskell
843type UserAPI4 = Capture "userid" Int :>
844 ( Get '[JSON] User
845 :<|> DeleteNoContent '[JSON] NoContent
846 )
847```
849However, you have to be aware that this has an effect on the type of the
850corresponding `Server`:
852``` haskell ignore
853Server UserAPI3 = (Int -> Handler User)
854 :<|> (Int -> Handler NoContent)
856Server UserAPI4 = Int -> ( Handler User
857 :<|> Handler NoContent
858 )
859```
861In the first case, each handler receives the *userid* argument. In the latter,
862the whole `Server` takes the *userid* and has handlers that are just
863computations in `Handler`, with no arguments. In other words:
865``` haskell
866server8 :: Server UserAPI3
867server8 = getUser :<|> deleteUser
869 where getUser :: Int -> Handler User
870 getUser _userid = error "..."
872 deleteUser :: Int -> Handler NoContent
873 deleteUser _userid = error "..."
875-- notice how getUser and deleteUser
876-- have a different type! no argument anymore,
877-- the argument directly goes to the whole Server
878server9 :: Server UserAPI4
879server9 userid = getUser userid :<|> deleteUser userid
881 where getUser :: Int -> Handler User
882 getUser = error "..."
884 deleteUser :: Int -> Handler NoContent
885 deleteUser = error "..."
886```
888Note that there's nothing special about `Capture` that lets you "factor it
889out": this can be done with any combinator. Here are a few examples of APIs
890with a combinator factored out for which we can write a perfectly valid
891`Server`.
893``` haskell
894-- we just factor out the "users" path fragment
895type API1 = "users" :>
896 ( Get '[JSON] [User] -- user listing
897 :<|> Capture "userid" Int :> Get '[JSON] User -- view a particular user
898 )
900-- we factor out the Request Body
901type API2 = ReqBody '[JSON] User :>
902 ( Get '[JSON] User -- just display the same user back, don't register it
903 :<|> PostNoContent '[JSON] NoContent -- register the user. empty response
904 )
906-- we factor out a Header
907type API3 = Header "Authorization" Token :>
908 ( Get '[JSON] SecretData -- get some secret data, if authorized
909 :<|> ReqBody '[JSON] SecretData :> PostNoContent '[JSON] NoContent -- add some secret data, if authorized
910 )
912newtype Token = Token ByteString
913newtype SecretData = SecretData ByteString
914```
916This approach lets you define APIs modularly and assemble them all into one big
917API type only at the end.
919``` haskell
920type UsersAPI =
921 Get '[JSON] [User] -- list users
922 :<|> ReqBody '[JSON] User :> PostNoContent '[JSON] NoContent -- add a user
923 :<|> Capture "userid" Int :>
924 ( Get '[JSON] User -- view a user
925 :<|> ReqBody '[JSON] User :> PutNoContent '[JSON] NoContent -- update a user
926 :<|> DeleteNoContent '[JSON] NoContent -- delete a user
927 )
929usersServer :: Server UsersAPI
930usersServer = getUsers :<|> newUser :<|> userOperations
932 where getUsers :: Handler [User]
933 getUsers = error "..."
935 newUser :: User -> Handler NoContent
936 newUser = error "..."
938 userOperations userid =
939 viewUser userid :<|> updateUser userid :<|> deleteUser userid
941 where
942 viewUser :: Int -> Handler User
943 viewUser = error "..."
945 updateUser :: Int -> User -> Handler NoContent
946 updateUser = error "..."
948 deleteUser :: Int -> Handler NoContent
949 deleteUser = error "..."
950```
952``` haskell
953type ProductsAPI =
954 Get '[JSON] [Product] -- list products
955 :<|> ReqBody '[JSON] Product :> PostNoContent '[JSON] NoContent -- add a product
956 :<|> Capture "productid" Int :>
957 ( Get '[JSON] Product -- view a product
958 :<|> ReqBody '[JSON] Product :> PutNoContent '[JSON] NoContent -- update a product
959 :<|> DeleteNoContent '[JSON] NoContent -- delete a product
960 )
962data Product = Product { productId :: Int }
964productsServer :: Server ProductsAPI
965productsServer = getProducts :<|> newProduct :<|> productOperations
967 where getProducts :: Handler [Product]
968 getProducts = error "..."
970 newProduct :: Product -> Handler NoContent
971 newProduct = error "..."
973 productOperations productid =
974 viewProduct productid :<|> updateProduct productid :<|> deleteProduct productid
976 where
977 viewProduct :: Int -> Handler Product
978 viewProduct = error "..."
980 updateProduct :: Int -> Product -> Handler NoContent
981 updateProduct = error "..."
983 deleteProduct :: Int -> Handler NoContent
984 deleteProduct = error "..."
985```
987``` haskell
988type CombinedAPI = "users" :> UsersAPI
989 :<|> "products" :> ProductsAPI
991server10 :: Server CombinedAPI
992server10 = usersServer :<|> productsServer
993```
995Finally, we can realize the user and product APIs are quite similar and
996abstract that away:
998``` haskell
999-- API for values of type 'a'
1000-- indexed by values of type 'i'
1001type APIFor a i =
1002 Get '[JSON] [a] -- list 'a's
1003 :<|> ReqBody '[JSON] a :> PostNoContent '[JSON] NoContent -- add an 'a'
1004 :<|> Capture "id" i :>
1005 ( Get '[JSON] a -- view an 'a' given its "identifier" of type 'i'
1006 :<|> ReqBody '[JSON] a :> PutNoContent '[JSON] NoContent -- update an 'a'
1007 :<|> DeleteNoContent '[JSON] NoContent -- delete an 'a'
1010-- Build the appropriate 'Server'
1011-- given the handlers of the right type.
1012serverFor :: Handler [a] -- handler for listing of 'a's
1013 -> (a -> Handler NoContent) -- handler for adding an 'a'
1014 -> (i -> Handler a) -- handler for viewing an 'a' given its identifier of type 'i'
1015 -> (i -> a -> Handler NoContent) -- updating an 'a' with given id
1016 -> (i -> Handler NoContent) -- deleting an 'a' given its id
1017 -> Server (APIFor a i)
1018serverFor = error "..."
1019-- implementation left as an exercise. contact us on IRC
1020-- or the mailing list if you get stuck!
1021```
1023When your API contains the `EmptyAPI` combinator, you'll want to use
1024`emptyServer` in the corresponding slot for your server, which will simply fail
1025with 404 whenever a request reaches it:
1027``` haskell
1028type CombinedAPI2 = API :<|> "empty" :> EmptyAPI
1030server11 :: Server CombinedAPI2
1031server11 = server3 :<|> emptyServer
1032```
1034## Using another monad for your handlers
1036Remember how `Server` turns combinators for HTTP methods into `Handler`? Well, actually, there's more to that. `Server` is actually a
1037simple type synonym.
1039``` haskell ignore
1040type Server api = ServerT api Handler
1041```
1043`ServerT` is the actual type family that computes the required types for the
1044handlers that's part of the `HasServer` class. It's like `Server` except that
1045it takes another parameter which is the monad you want your handlers to run in,
1046or more generally the return types of your handlers. This third parameter is
1047used for specifying the return type of the handler for an endpoint, e.g when
1048computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be
1049`SomeMonad Person`.
1051The first and main question one might have then is: how do we write handlers
1052that run in another monad? How can we "bring back" the value from a given monad
1053into something **servant** can understand?
1055### Natural transformations
1057If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1058do we have?
1060``` haskell ignore
1061newtype m :~> n = NT { ($$) :: forall a. m a -> n a}
1062```
1064For example:
1066``` haskell
1067listToMaybeNT :: [] :~> Maybe
1068listToMaybeNT = NT listToMaybe -- from Data.Maybe
1069```
1071(`NT` comes from "natural transformation", in case you're wondering.)
1073So if you want to write handlers using another monad/type than `Handler`, say the `Reader String` monad, the first thing you have to
1074prepare is a function:
1076``` haskell ignore
1077readerToHandler :: Reader String :~> Handler
1078```
1080Let's start with `readerToHandler'`. We obviously have to run the `Reader`
1081computation by supplying it with a `String`, like `"hi"`. We get an `a` out
1082from that and can then just `return` it into `Handler`. We can then just wrap
1083that function with the `NT` constructor to make it have the fancier type.
1085``` haskell
1086readerToHandler' :: forall a. Reader String a -> Handler a
1087readerToHandler' r = return (runReader r "hi")
1089readerToHandler :: Reader String :~> Handler
1090readerToHandler = NT readerToHandler'
1091```
1093We can write some simple webservice with the handlers running in `Reader String`.
1095``` haskell
1096type ReaderAPI = "a" :> Get '[JSON] Int
1097 :<|> "b" :> Get '[JSON] String
1099readerAPI :: Proxy ReaderAPI
1100readerAPI = Proxy
1102readerServerT :: ServerT ReaderAPI (Reader String)
1103readerServerT = a :<|> b
1105 where a :: Reader String Int
1106 a = return 1797
1108 b :: Reader String String
1109 b = ask
1110```
1112We unfortunately can't use `readerServerT` as an argument of `serve`, because
1113`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `Handler`. But there's a simple solution to this.
1115### Enter `enter`
1117That's right. We have just written `readerToHandler`, which is exactly what we
1118would need to apply to all handlers to make the handlers have the
1119right type for `serve`. Being cumbersome to do by hand, we provide a function
1120`enter` which takes a natural transformation between two parametrized types `m`
1121and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1123In our case, we can wrap up our little webservice by using `enter
1124readerToHandler` on our handlers.
1126``` haskell
1127readerServer :: Server ReaderAPI
1128readerServer = enter readerToHandler readerServerT
1130app4 :: Application
1131app4 = serve readerAPI readerServer
1132```
1134This is the webservice in action:
1136``` bash
1137$ curl http://localhost:8081/a
11381797
1139$ curl http://localhost:8081/b
1140"hi"
1141```
1143## Conclusion
1145You're now equipped to write webservices/web-applications using
1146**servant**. The rest of this document focuses on **servant-client**,
1147**servant-js** and **servant-docs**.