git.iko.soy/typeable/servant
on servant-auth-uverb help sign in
1200 lines 40 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 RankNTypes #-}
22{-# LANGUAGE ScopedTypeVariables #-}
23{-# LANGUAGE TypeOperators #-}
25module Server where
27import Prelude ()
28import Prelude.Compat
30import Control.Monad.Except
31import Control.Monad.Reader
32import Data.Aeson
33import Data.Aeson.Types
34import Data.Attoparsec.ByteString
35import Data.ByteString (ByteString)
36import Data.List
37import Data.Maybe
38import Data.String.Conversions
39import Data.Time.Calendar
40import GHC.Generics
41import Lucid
42import Network.HTTP.Media ((//), (/:))
43import Network.Wai
44import Network.Wai.Handler.Warp
45import Servant
46import System.Directory
47import Text.Blaze
48import Text.Blaze.Html.Renderer.Utf8
49import Servant.Types.SourceT (source)
50import qualified Data.Aeson.Parser
51import qualified Text.Blaze.Html
52```
54**Important**: the `Servant` module comes from the **servant-server** package,
55the one that lets us run webservers that implement a particular API type. It
56reexports all the types from the **servant** package that let you declare API
57types as well as everything you need to turn your request handlers into a
58fully-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
60else.
62We will write a server that will serve the following API.
64``` haskell
65type UserAPI1 = "users" :> Get '[JSON] [User]
66```
68Here's what we would like to see when making a GET request to `/users`.
70``` javascript
71[ {"name": "Isaac Newton", "age": 372, "email": "isaac@newton.co.uk", "registration_date": "1683-03-01"}
72, {"name": "Albert Einstein", "age": 136, "email": "ae@mc2.org", "registration_date": "1905-12-01"}
74```
76Now let's define our `User` data type and write some instances for it.
78``` haskell
79data User = User
80 { name :: String
81 , age :: Int
82 , email :: String
83 , registration_date :: Day
84 } deriving (Eq, Show, Generic)
86instance ToJSON User
87```
89Nothing funny going on here. But we now can define our list of two users.
91``` haskell
92users1 :: [User]
93users1 =
94 [ User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
95 , User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
96 ]
97```
99We can now take care of writing the actual webservice that will handle requests
100to such an API. This one will be very simple, being reduced to just a single
101endpoint. The type of the web application is determined by the API type,
102through a *type family* named `Server`. (Type families are just functions that
103take types as input and return types.) The `Server` type family will compute
104the right type that a bunch of request handlers should have just from the
105corresponding API type.
107The first thing to know about the `Server` type family is that behind the
108scenes it will drive the routing, letting you focus only on the business
109logic. The second thing to know is that for each endpoint, your handlers will
110by default run in the `Handler` monad. This is overridable very
111easily, as explained near the end of this guide. Third thing, the type of the
112value returned in that monad must be the same as the second argument of the
113HTTP method combinator used for the corresponding endpoint. In our case, it
114means we must provide a handler of type `Handler [User]`. Well,
115we have a monad, let's just `return` our list:
117``` haskell
118server1 :: Server UserAPI1
119server1 = return users1
120```
122That'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):
126``` haskell
127userAPI :: Proxy UserAPI1
128userAPI = Proxy
130-- 'serve' comes from servant and hands you a WAI Application,
131-- which you can think of as an "abstract" web application,
132-- not yet a webserver.
133app1 :: Application
134app1 = serve userAPI server1
135```
137The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
138But that's about as much boilerplate as you get.
140And we're done! Let's run our webservice on the port 8081.
142``` haskell
143main :: IO ()
144main = run 8081 app1
145```
147You can put this all into a file or just grab [servant's
148repo](http://github.com/haskell-servant/servant) and look at the
149*doc/tutorial* directory. This code (the source of this web page) is in
150*doc/tutorial/Server.lhs*.
152If you run it, you can go to `http://localhost:8081/users` in your browser or
153query it with curl and you see:
155``` bash
156$ curl http://localhost:8081/users
157[{"email":"isaac@newton.co.uk","registration_date":"1683-03-01","age":372,"name":"Isaac Newton"},{"email":"ae@mc2.org","registration_date":"1905-12-01","age":136,"name":"Albert Einstein"}]
158```
160## More endpoints
162What if we want more than one endpoint? Let's add `/albert` and `/isaac` to
163view the corresponding users encoded in JSON.
165``` haskell
166type UserAPI2 = "users" :> Get '[JSON] [User]
167 :<|> "albert" :> Get '[JSON] User
168 :<|> "isaac" :> Get '[JSON] User
169```
171And let's adapt our code a bit.
173``` haskell
174isaac :: User
175isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
177albert :: User
178albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
180users2 :: [User]
181users2 = [isaac, albert]
182```
184Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
185are going to separate the handlers with `:<|>` too! They must be provided in
186the same order as in the API type.
188``` haskell
189server2 :: Server UserAPI2
190server2 = return users2
191 :<|> return albert
192 :<|> return isaac
193```
195And that's it! You can run this example in the same way that we showed for
196`server1` and check out the data available at `/users`, `/albert` and `/isaac`.
198## From combinators to handler arguments
200Fine, we can write trivial webservices easily, but none of the two above use
201any "fancy" combinator from servant. Let's address this and use `QueryParam`,
202`Capture` and `ReqBody` right away. You'll see how each occurrence of these
203combinators in an endpoint makes the corresponding handler receive an
204argument of the appropriate type automatically. You don't have to worry about
205manually looking up URL captures or query string parameters, or
206decoding/encoding data from/to JSON. Never.
208We are going to use the following data types and functions to implement a
209server for `API`.
211``` haskell
212type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
213 :<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
214 :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
216data Position = Position
217 { xCoord :: Int
218 , yCoord :: Int
219 } deriving Generic
221instance ToJSON Position
223newtype HelloMessage = HelloMessage { msg :: String }
224 deriving Generic
226instance ToJSON HelloMessage
228data ClientInfo = ClientInfo
229 { clientName :: String
230 , clientEmail :: String
231 , clientAge :: Int
232 , clientInterestedIn :: [String]
233 } deriving Generic
235instance FromJSON ClientInfo
236instance ToJSON ClientInfo
238data Email = Email
239 { from :: String
240 , to :: String
241 , subject :: String
242 , body :: String
243 } deriving Generic
245instance ToJSON Email
247emailForClient :: ClientInfo -> Email
248emailForClient c = Email from' to' subject' body'
250 where from' = "great@company.com"
251 to' = clientEmail c
252 subject' = "Hey " ++ clientName c ++ ", we miss you!"
253 body' = "Hi " ++ clientName c ++ ",\n\n"
254 ++ "Since you've recently turned " ++ show (clientAge c)
255 ++ ", have you checked out our latest "
256 ++ intercalate ", " (clientInterestedIn c)
257 ++ " products? Give us a visit!"
258```
260We can implement handlers for the three endpoints:
262``` haskell
263server3 :: Server API
264server3 = position
265 :<|> hello
266 :<|> marketing
268 where position :: Int -> Int -> Handler Position
269 position x y = return (Position x y)
271 hello :: Maybe String -> Handler HelloMessage
272 hello mname = return . HelloMessage $ case mname of
273 Nothing -> "Hello, anonymous coward"
274 Just n -> "Hello, " ++ n
276 marketing :: ClientInfo -> Handler Email
277 marketing clientinfo = return (emailForClient clientinfo)
278```
280Did you see that? The types for your handlers changed to be just what we
281needed! In particular:
283 - a `Capture "something" a` becomes an argument of type `a` (for `position`);
284 - a `QueryParam "something" a` becomes an argument of type `Maybe a` (because
285an endpoint can technically be accessed without specifying any query
286string parameter, we decided to "force" handlers to be aware that the
287parameter might not always be there);
289 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
291And that's it. Here's the example in action:
293``` bash
294$ curl http://localhost:8081/position/1/2
295{"xCoord":1,"yCoord":2}
296$ curl http://localhost:8081/hello
297{"msg":"Hello, anonymous coward"}
298$ curl http://localhost:8081/hello?name=Alp
299{"msg":"Hello, Alp"}
300$ curl -X POST -d '{"clientName":"Alp Mestanogullari", "clientEmail" : "alp@foo.com", "clientAge": 25, "clientInterestedIn": ["haskell", "mathematics"]}' -H 'Accept: application/json' -H 'Content-type: application/json' http://localhost:8081/marketing
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"}
302```
304For reference, here's a list of some combinators from **servant**:
306 > - `Delete`, `Get`, `Patch`, `Post`, `Put`: these do not become arguments. They provide the return type of handlers, which usually is `Handler <something>`.
307 > - `Capture "something" a` becomes an argument of type `a`.
308 > - `QueryParam "something" a`, `Header "something" a` all become arguments of type `Maybe a`, because there might be no value at all specified by the client for these.
309 > - `QueryFlag "something"` gets turned into an argument of type `Bool`.
310 > - `QueryParams "something" a` gets turned into an argument of type `[a]`.
311 > - `ReqBody contentTypes a` gets turned into an argument of type `a`.
313## The `FromHttpApiData`/`ToHttpApiData` classes
315Wait... How does **servant** know how to decode the `Int`s from the URL? Or how
316to decode a `ClientInfo` value from the request body? The following three sections will
317help us answer these questions.
319`Capture`s and `QueryParam`s are represented by some textual value in URLs.
320`Header`s are similarly represented by a pair of a header name and a
321corresponding (textual) value in the request's "metadata". How types are
322decoded from headers, captures, and query params is expressed in a class
323`FromHttpApiData` (from the package
324[**http-api-data**](http://hackage.haskell.org/package/http-api-data)):
326``` haskell ignore
327class FromHttpApiData a where
328 {-# MINIMAL parseUrlPiece | parseQueryParam #-}
329 -- | Parse URL path piece.
330 parseUrlPiece :: Text -> Either Text a
331 parseUrlPiece = parseQueryParam
333 -- | Parse HTTP header value.
334 parseHeader :: ByteString -> Either Text a
335 parseHeader = parseUrlPiece . decodeUtf8
337 -- | Parse query param value.
338 parseQueryParam :: Text -> Either Text a
339 parseQueryParam = parseUrlPiece
340```
342As you can see, as long as you provide either `parseUrlPiece` (for `Capture`s)
343or `parseQueryParam` (for `QueryParam`s), the other methods will be defined in
344terms of this.
346**http-api-data** provides a decent number of instances, helpers for defining new
347ones, and wonderful documentation.
349There's not much else to say about these classes. You will need instances for
350them when using `Capture`, `QueryParam`, `QueryParams`, and `Header` with your
351types. You will need `FromHttpApiData` instances for server-side request
352handlers and `ToHttpApiData` instances only when using
353**servant-client**, as described in the [section about deriving haskell
354functions to query an API](Client.html).
356## Using content-types with your data types
358The same principle was operating when decoding request bodies from JSON, and
359responses *into* JSON. (JSON is just the running example - you can do this with
360any content-type.)
362This section introduces a couple of typeclasses provided by **servant** that make
363all of this work.
365### The truth behind `JSON`
368What exactly is `JSON` (the type as used in `Get '[JSON] User`)? Like the 3
369other content-types provided out of the box by **servant**, it's a really dumb
370data type.
372``` haskell ignore
373data JSON
374data PlainText
375data FormUrlEncoded
376data OctetStream
377```
379Obviously, this is not all there is to `JSON`, otherwise it would be quite
380pointless. Like most of the data types in **servant**, `JSON` is mostly there as
381a special *symbol* that's associated with encoding (resp. decoding) to (resp.
382from) the *JSON* format. The way this association is performed can be
383decomposed into two steps.
385The first step is to provide a proper
386`MediaType` (from
387[**http-media**](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html))
388representation for `JSON`, or for your own content-types. If you look at the
389haddocks from this link, you can see that we just have to specify
390`application/json` using the appropriate functions. In our case, we can just
391use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
392the `MediaType` is to write an instance for the `Accept` class:
394``` haskell ignore
395-- for reference:
396class Accept ctype where
397 contentType :: Proxy ctype -> MediaType
399instance Accept JSON where
400 contentType _ = "application" // "json"
401```
403The second step is centered around the `MimeRender` and `MimeUnrender` classes.
404These classes just let you specify a way to encode and decode
405values into or from your content-type's representation.
407``` haskell ignore
408class Accept ctype => MimeRender ctype a where
409 mimeRender :: Proxy ctype -> a -> ByteString
410 -- alternatively readable as:
411 mimeRender :: Proxy ctype -> (a -> ByteString)
412```
414Given a content-type and some user type, `MimeRender` provides a function that
415encodes values of type `a` to lazy `ByteString`s.
417In the case of `JSON`, this is easily dealt with! For any type `a` with a
418`ToJSON` instance, we can render values of that type to JSON using
419`Data.Aeson.encode`.
421``` haskell ignore
422instance ToJSON a => MimeRender JSON a where
423 mimeRender _ = encode
424```
426And now the `MimeUnrender` class, which lets us extract values from lazy
427`ByteString`s, alternatively failing with an error string.
429``` haskell ignore
430class Accept ctype => MimeUnrender ctype a where
431 mimeUnrender :: Proxy ctype -> ByteString -> Either String a
432```
434We don't have much work to do there either, `Data.Aeson.eitherDecode` is
435precisely what we need. However, it only allows arrays and objects as toplevel
436JSON values and this has proven to get in our way more than help us so we wrote
437our own little function around **aeson** and **attoparsec** that allows any type of
438JSON value at the toplevel of a "JSON document". Here's the definition in case
439you are curious.
441``` haskell
442eitherDecodeLenient :: FromJSON a => ByteString -> Either String a
443eitherDecodeLenient input = do
444 v :: Value <- parseOnly (Data.Aeson.Parser.value <* endOfInput) (cs input)
445 parseEither parseJSON v
446```
448This function is exactly what we need for our `MimeUnrender` instance.
450``` haskell ignore
451instance FromJSON a => MimeUnrender JSON a where
452 mimeUnrender _ = eitherDecodeLenient
453```
455And this is all the code that lets you use `JSON` with `ReqBody`, `Get`,
456`Post` and friends. We can check our understanding by implementing support
457for an `HTML` content-type, so that users of your webservice can access an
458HTML representation of the data they want, ready to be included in any HTML
459document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/),
460simply by adding `Accept: text/html` to their request headers.
462### Case-studies: **servant-blaze** and **servant-lucid**
464These days, most of the haskellers who write their HTML UIs directly from
465Haskell use either [**blaze-html**](http://hackage.haskell.org/package/blaze-html)
466or [**lucid**](http://hackage.haskell.org/package/lucid). The best option for
467**servant** is obviously to support both (and hopefully other templating
468solutions!). We're first going to look at **lucid**:
470``` haskell
471data HTMLLucid
472```
474Once again, the data type is just there as a symbol for the encoding/decoding
475functions, except that this time we will only worry about encoding since
476**lucid** doesn't provide a way to extract data from HTML.
478``` haskell
479instance Accept HTMLLucid where
480 contentType _ = "text" // "html" /: ("charset", "utf-8")
481```
483Note that this instance uses the `(/:)` operator from **http-media** which lets
484us specify additional information about a content-type, like the charset here.
486The rendering instances call similar functions that take
487types with an appropriate instance to an "abstract" HTML representation and
488then write that to a `ByteString`.
490``` haskell
491instance ToHtml a => MimeRender HTMLLucid a where
492 mimeRender _ = renderBS . toHtml
494-- let's also provide an instance for lucid's
495-- 'Html' wrapper.
496instance MimeRender HTMLLucid (Html a) where
497 mimeRender _ = renderBS
498```
500For **blaze-html** everything works very similarly:
502``` haskell
503-- For this tutorial to compile 'HTMLLucid' and 'HTMLBlaze' have to be
504-- distinct. Usually you would stick to one html rendering library and then
505-- you can go with one 'HTML' type.
506data HTMLBlaze
508instance Accept HTMLBlaze where
509 contentType _ = "text" // "html" /: ("charset", "utf-8")
511instance ToMarkup a => MimeRender HTMLBlaze a where
512 mimeRender _ = renderHtml . Text.Blaze.Html.toHtml
514-- while we're at it, just like for lucid we can
515-- provide an instance for rendering blaze's 'Html' type
516instance MimeRender HTMLBlaze Text.Blaze.Html.Html where
517 mimeRender _ = renderHtml
518```
520Both [**servant-blaze**](http://hackage.haskell.org/package/servant-blaze) and
521[**servant-lucid**](http://hackage.haskell.org/package/servant-lucid) let you use
522`HTMLLucid` and `HTMLBlaze` in any content-type list as long as you provide an instance of the
523appropriate class (`ToMarkup` for **blaze-html**, `ToHtml` for **lucid**).
525We can now write a webservice that uses **servant-lucid** to show the `HTMLLucid`
526content-type in action. We will be serving the following API:
528``` haskell
529type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
530```
532where `Person` is defined as follows:
534``` haskell
535data Person = Person
536 { firstName :: String
537 , lastName :: String
538 } deriving Generic -- for the JSON instance
540instance ToJSON Person
541```
543Now, let's teach **lucid** how to render a `Person` as a row in a table, and then
544a list of `Person`s as a table with a row per person.
546``` haskell
547-- HTML serialization of a single person
548instance ToHtml Person where
549 toHtml person =
550 tr_ $ do
551 td_ (toHtml $ firstName person)
552 td_ (toHtml $ lastName person)
554 -- do not worry too much about this
555 toHtmlRaw = toHtml
557-- HTML serialization of a list of persons
558instance ToHtml [Person] where
559 toHtml persons = table_ $ do
560 tr_ $ do
561 th_ "first name"
562 th_ "last name"
564 -- this just calls toHtml on each person of the list
565 -- and concatenates the resulting pieces of HTML together
566 foldMap toHtml persons
568 toHtmlRaw = toHtml
569```
571We create some `Person` values and serve them as a list:
573``` haskell
574people :: [Person]
575people =
576 [ Person "Isaac" "Newton"
577 , Person "Albert" "Einstein"
578 ]
580personAPI :: Proxy PersonAPI
581personAPI = Proxy
583server4 :: Server PersonAPI
584server4 = return people
586app2 :: Application
587app2 = serve personAPI server4
588```
590And we're good to go:
592``` bash
593$ curl http://localhost:8081/persons
594[{"lastName":"Newton","firstName":"Isaac"},{"lastName":"Einstein","firstName":"Albert"}]
595$ curl -H 'Accept: text/html' http://localhost:8081/persons
596<table><tr><td>first name</td><td>last name</td></tr><tr><td>Isaac</td><td>Newton</td></tr><tr><td>Albert</td><td>Einstein</td></tr></table>
597# or just point your browser to http://localhost:8081/persons
598```
600## The `Handler` monad
602At 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)).
604One might wonder: why this monad? The answer is that it is the
605simplest monad with the following properties:
607- it lets us both return a successful result (using `return`)
608or "fail" with a descriptive error (using `throwError`);
609- it lets us perform IO, which is absolutely vital since most webservices exist
610as interfaces to databases that we interact with in `IO`.
612Let's recall some definitions.
614``` haskell ignore
615-- from the 'mtl' package at
616newtype ExceptT e m a = ExceptT (m (Either e a))
617```
619In short, this means that a handler of type `Handler a` is simply
620equivalent to a computation of type `IO (Either ServerError a)`, that is, an IO
621action that either returns an error or a result.
623The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl/docs/Control-Monad-Except.html#t:ExceptT)
624from which `ExceptT` comes is worth looking at.
625Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so
626`throwError` can be used to return an error from your handler (whereas `return`
627 is enough to return a success).
629Most of what you'll be doing in your handlers is running some IO and,
630depending on the result, you might sometimes want to throw an error of some
631kind and abort early. The next two sections cover how to do just that.
633### Performing IO
635Other important instances from the list above are `MonadIO m => MonadIO
636(ExceptT e m)`, and therefore also `MonadIO Handler` as there is a `MonadIO IO` instance.
637[`MonadIO`](http://hackage.haskell.org/package/base/docs/Control-Monad-IO-Class.html#t:MonadIO)
638is a class from the **transformers** package defined as:
640``` haskell ignore
641class Monad m => MonadIO m where
642 liftIO :: IO a -> m a
643```
645So if you want to run any kind of
646IO computation in your handlers, just use `liftIO`:
648``` haskell
649type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
651newtype FileContent = FileContent
652 { content :: String }
653 deriving Generic
655instance ToJSON FileContent
657server5 :: Server IOAPI1
658server5 = do
659 filecontent <- liftIO (readFile "myfile.txt")
660 return (FileContent filecontent)
661```
663### Failing, through `ServerError`
665If you want to explicitly fail at providing the result promised by an endpoint
666using the appropriate HTTP status code (not found, unauthorized, etc) and some
667error message, all you have to do is use the `throwError` function mentioned above
668and provide it with the appropriate value of type `ServerError`, which is
669defined as:
671``` haskell ignore
672data ServerError = ServerError
673 { errHTTPCode :: Int
674 , errReasonPhrase :: String
675 , errBody :: ByteString -- lazy bytestring
676 , errHeaders :: [Header]
677 }
678```
680Many standard values are provided out of the box by the `Servant.Server`
681module. If you want to use these values but add a body or some headers, just
682use record update syntax:
684``` haskell
685failingHandler :: Handler ()
686failingHandler = throwError myerr
688 where myerr :: ServerError
689 myerr = err503 { errBody = "Sorry dear user." }
690```
692Here's an example where we return a customised 404-Not-Found error message in
693the response body if "myfile.txt" isn't there:
695``` haskell
696server6 :: Server IOAPI1
697server6 = do
698 exists <- liftIO (doesFileExist "myfile.txt")
699 if exists
700 then liftIO (readFile "myfile.txt") >>= return . FileContent
701 else throwError custom404Err
703 where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }
704```
706Here's how that server looks in action:
708``` bash
709$ curl --verbose http://localhost:8081/myfile.txt
710[snip]
711* Connected to localhost (127.0.0.1) port 8081 (#0)
712> GET /myfile.txt HTTP/1.1
713> User-Agent: curl/7.30.0
714> Host: localhost:8081
715> Accept: */*
717< HTTP/1.1 404 Not Found
718[snip]
719myfile.txt just isn't there, please leave this server alone.
721$ echo Hello > myfile.txt
723$ curl --verbose http://localhost:8081/myfile.txt
724[snip]
725* Connected to localhost (127.0.0.1) port 8081 (#0)
726> GET /myfile.txt HTTP/1.1
727> User-Agent: curl/7.30.0
728> Host: localhost:8081
729> Accept: */*
731< HTTP/1.1 200 OK
732[snip]
733< Content-Type: application/json
734[snip]
735{"content":"Hello\n"}
736```
738## Response headers
740To add headers to your response, use
741[addHeader](http://hackage.haskell.org/package/servant/docs/Servant-API-ResponseHeaders.html).
742Note that this changes the type of your API, as we can see in the following example:
744``` haskell
745type MyHandler = Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
747myHandler :: Server MyHandler
748myHandler = return $ addHeader 1797 albert
749```
751Note that the type of `addHeader header x` is different than the type of `x`!
752And if you add more headers, more headers will appear in the header list:
754``` haskell
755type MyHeadfulHandler = Get '[JSON] (Headers '[Header "X-A-Bool" Bool, Header "X-An-Int" Int] User)
757myHeadfulHandler :: Server MyHeadfulHandler
758myHeadfulHandler = return $ addHeader True $ addHeader 1797 albert
759```
761But what if your handler only *sometimes* adds a header? If you declare that
762your handler adds headers, and you don't add one, the return type of your
763handler will be different than expected. To solve this, you have to explicitly
764*not* add a header by using `noHeader`:
766``` haskell
767type MyMaybeHeaderHandler
768 = Capture "withHeader" Bool :> Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
770myMaybeHeaderHandler :: Server MyMaybeHeaderHandler
771myMaybeHeaderHandler x = return $ if x then addHeader 1797 albert
772 else noHeader albert
773```
775## Serving static files
777**servant-server** also provides a way to just serve the content of a directory
778under some path in your web API. As mentioned earlier in this document, the
779`Raw` combinator can be used in your APIs to mean "plug here any WAI
780application". Well, **servant-server** provides a function to get a file and
781directory serving WAI application, namely:
783``` haskell ignore
784-- exported by Servant and Servant.Server
785serveDirectoryWebApp :: FilePath -> Server Raw
786```
788`serveDirectoryWebApp`'s argument must be a path to a valid directory.
790Here's an example API that will serve some static files:
792``` haskell
793type StaticAPI = "static" :> Raw
794```
796And the server:
798``` haskell
799staticAPI :: Proxy StaticAPI
800staticAPI = Proxy
801```
803``` haskell
804server7 :: Server StaticAPI
805server7 = serveDirectoryWebApp "static-files"
807app3 :: Application
808app3 = serve staticAPI server7
809```
811This server will match any request whose path starts with `/static` and will look
812for a file at the path described by the rest of the request path, inside the
813 *static-files/* directory of the path you run the program from.
815In 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.
817If it doesn't exist, the handler will fail with a `404` status code.
819`serveDirectoryWebApp` uses some standard settings that fit the use case of
820serving static files for most web apps. You can find out about the other
821options in the documentation of the `Servant.Server.StaticFiles` module.
823## Nested APIs
825Let's see how you can define APIs in a modular way, while avoiding repetition.
826Consider this simple example:
828``` haskell
829type UserAPI3 = -- view the user with given userid, in JSON
830 Capture "userid" Int :> Get '[JSON] User
832 :<|> -- delete the user with given userid. empty response
833 Capture "userid" Int :> DeleteNoContent
834```
836We can instead factor out the `userid`:
838``` haskell
839type UserAPI4 = Capture "userid" Int :>
840 ( Get '[JSON] User
841 :<|> DeleteNoContent
842 )
843```
845However, you have to be aware that this has an effect on the type of the
846corresponding `Server`:
848``` haskell ignore
849Server UserAPI3 = (Int -> Handler User)
850 :<|> (Int -> Handler NoContent)
852Server UserAPI4 = Int -> ( Handler User
853 :<|> Handler NoContent
854 )
855```
857In the first case, each handler receives the *userid* argument. In the latter,
858the whole `Server` takes the *userid* and has handlers that are just
859computations in `Handler`, with no arguments. In other words:
861``` haskell
862server8 :: Server UserAPI3
863server8 = getUser :<|> deleteUser
865 where getUser :: Int -> Handler User
866 getUser _userid = error "..."
868 deleteUser :: Int -> Handler NoContent
869 deleteUser _userid = error "..."
871-- notice how getUser and deleteUser
872-- have a different type! no argument anymore,
873-- the argument directly goes to the whole Server
874server9 :: Server UserAPI4
875server9 userid = getUser userid :<|> deleteUser userid
877 where getUser :: Int -> Handler User
878 getUser = error "..."
880 deleteUser :: Int -> Handler NoContent
881 deleteUser = error "..."
882```
884Note that there's nothing special about `Capture` that lets you "factor it
885out": this can be done with any combinator. Here are a few examples of APIs
886with a combinator factored out for which we can write a perfectly valid
887`Server`.
889``` haskell
890-- we just factor out the "users" path fragment
891type API1 = "users" :>
892 ( Get '[JSON] [User] -- user listing
893 :<|> Capture "userid" Int :> Get '[JSON] User -- view a particular user
894 )
896-- we factor out the Request Body
897type API2 = ReqBody '[JSON] User :>
898 ( Get '[JSON] User -- just display the same user back, don't register it
899 :<|> PostNoContent -- register the user. empty response
900 )
902-- we factor out a Header
903type API3 = Header "Authorization" Token :>
904 ( Get '[JSON] SecretData -- get some secret data, if authorized
905 :<|> ReqBody '[JSON] SecretData :> PostNoContent -- add some secret data, if authorized
906 )
908newtype Token = Token ByteString
909newtype SecretData = SecretData ByteString
910```
912This approach lets you define APIs modularly and assemble them all into one big
913API type only at the end.
915``` haskell
916type UsersAPI =
917 Get '[JSON] [User] -- list users
918 :<|> ReqBody '[JSON] User :> PostNoContent -- add a user
919 :<|> Capture "userid" Int :>
920 ( Get '[JSON] User -- view a user
921 :<|> ReqBody '[JSON] User :> PutNoContent -- update a user
922 :<|> DeleteNoContent -- delete a user
923 )
925usersServer :: Server UsersAPI
926usersServer = getUsers :<|> newUser :<|> userOperations
928 where getUsers :: Handler [User]
929 getUsers = error "..."
931 newUser :: User -> Handler NoContent
932 newUser = error "..."
934 userOperations userid =
935 viewUser userid :<|> updateUser userid :<|> deleteUser userid
937 where
938 viewUser :: Int -> Handler User
939 viewUser = error "..."
941 updateUser :: Int -> User -> Handler NoContent
942 updateUser = error "..."
944 deleteUser :: Int -> Handler NoContent
945 deleteUser = error "..."
946```
948``` haskell
949type ProductsAPI =
950 Get '[JSON] [Product] -- list products
951 :<|> ReqBody '[JSON] Product :> PostNoContent -- add a product
952 :<|> Capture "productid" Int :>
953 ( Get '[JSON] Product -- view a product
954 :<|> ReqBody '[JSON] Product :> PutNoContent -- update a product
955 :<|> DeleteNoContent -- delete a product
956 )
958data Product = Product { productId :: Int }
960productsServer :: Server ProductsAPI
961productsServer = getProducts :<|> newProduct :<|> productOperations
963 where getProducts :: Handler [Product]
964 getProducts = error "..."
966 newProduct :: Product -> Handler NoContent
967 newProduct = error "..."
969 productOperations productid =
970 viewProduct productid :<|> updateProduct productid :<|> deleteProduct productid
972 where
973 viewProduct :: Int -> Handler Product
974 viewProduct = error "..."
976 updateProduct :: Int -> Product -> Handler NoContent
977 updateProduct = error "..."
979 deleteProduct :: Int -> Handler NoContent
980 deleteProduct = error "..."
981```
983``` haskell
984type CombinedAPI = "users" :> UsersAPI
985 :<|> "products" :> ProductsAPI
987server10 :: Server CombinedAPI
988server10 = usersServer :<|> productsServer
989```
991Finally, we can realize the user and product APIs are quite similar and
992abstract that away:
994``` haskell
995-- API for values of type 'a'
996-- indexed by values of type 'i'
997type APIFor a i =
998 Get '[JSON] [a] -- list 'a's
999 :<|> ReqBody '[JSON] a :> PostNoContent -- add an 'a'
1000 :<|> Capture "id" i :>
1001 ( Get '[JSON] a -- view an 'a' given its "identifier" of type 'i'
1002 :<|> ReqBody '[JSON] a :> PutNoContent -- update an 'a'
1003 :<|> DeleteNoContent -- delete an 'a'
1006-- Build the appropriate 'Server'
1007-- given the handlers of the right type.
1008serverFor :: Handler [a] -- handler for listing of 'a's
1009 -> (a -> Handler NoContent) -- handler for adding an 'a'
1010 -> (i -> Handler a) -- handler for viewing an 'a' given its identifier of type 'i'
1011 -> (i -> a -> Handler NoContent) -- updating an 'a' with given id
1012 -> (i -> Handler NoContent) -- deleting an 'a' given its id
1013 -> Server (APIFor a i)
1014serverFor = error "..."
1015-- implementation left as an exercise. contact us on IRC
1016-- or the mailing list if you get stuck!
1017```
1019When your API contains the `EmptyAPI` combinator, you'll want to use
1020`emptyServer` in the corresponding slot for your server, which will simply fail
1021with 404 whenever a request reaches it:
1023``` haskell
1024type CombinedAPI2 = API :<|> "empty" :> EmptyAPI
1026server11 :: Server CombinedAPI2
1027server11 = server3 :<|> emptyServer
1028```
1030## Using another monad for your handlers
1032Remember how `Server` turns combinators for HTTP methods into `Handler`? Well, actually, there's more to that. `Server` is actually a
1033simple type synonym.
1035``` haskell ignore
1036type Server api = ServerT api Handler
1037```
1039`ServerT` is the actual type family that computes the required types for the
1040handlers that's part of the `HasServer` class. It's like `Server` except that
1041it takes another parameter which is the monad you want your handlers to run in,
1042or more generally the return types of your handlers. This third parameter is
1043used for specifying the return type of the handler for an endpoint, e.g when
1044computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be
1045`SomeMonad Person`.
1047The first and main question one might have then is: how do we write handlers
1048that run in another monad? How can we "bring back" the value from a given monad
1049into something **servant** can understand?
1051### Natural transformations
1053If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1054do we have?
1056``` haskell
1057type (~>) m n = forall a. m a -> n a
1058```
1060For example:
1062``` haskell
1063listToMaybe' :: [] ~> Maybe
1064listToMaybe' = listToMaybe -- from Data.Maybe
1065```
1067Note that `servant` doesn't declare the `~>` type-alias, as the unfolded
1068variant isn't much longer to write, as we'll see shortly.
1070So if you want to write handlers using another monad/type than `Handler`, say the `Reader String` monad, the first thing you have to
1071prepare is a function:
1073``` haskell ignore
1074readerToHandler :: Reader String a -> Handler a
1075```
1077We obviously have to run the `Reader` computation by supplying it with a
1078`String`, like `"hi"`. We get an `a` out from that and can then just `return`
1079it into `Handler`.
1081``` haskell
1082readerToHandler :: Reader String a -> Handler a
1083readerToHandler r = return (runReader r "hi")
1084```
1086We can write some simple webservice with the handlers running in `Reader String`.
1088``` haskell
1089type ReaderAPI = "a" :> Get '[JSON] Int
1090 :<|> "b" :> ReqBody '[JSON] Double :> Get '[JSON] Bool
1092readerAPI :: Proxy ReaderAPI
1093readerAPI = Proxy
1095readerServerT :: ServerT ReaderAPI (Reader String)
1096readerServerT = a :<|> b where
1097 a :: Reader String Int
1098 a = return 1797
1100 b :: Double -> Reader String Bool
1101 b _ = asks (== "hi")
1102```
1104We unfortunately can't use `readerServerT` as an argument of `serve`, because
1105`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `Handler`. But there's a simple solution to this.
1107### Welcome `hoistServer`
1109That's right. We have just written `readerToHandler`, which is exactly what we
1110would need to apply to all handlers to make the handlers have the
1111right type for `serve`. Being cumbersome to do by hand, we provide a function
1112`hoistServer` which takes a natural transformation between two parameterized types `m`
1113and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1115In our case, we can wrap up our little webservice by using
1116`hoistServer readerAPI readerToHandler` on our handlers.
1118``` haskell
1119readerServer :: Server ReaderAPI
1120readerServer = hoistServer readerAPI readerToHandler readerServerT
1122app4 :: Application
1123app4 = serve readerAPI readerServer
1124```
1126This is the webservice in action:
1128``` bash
1129$ curl http://localhost:8081/a
11301797
1131$ curl http://localhost:8081/b -X GET -d '42.0' -H 'Content-Type: application/json'
1132true
1133```
1135### An arrow is a reader too.
1137In previous versions of `servant` we had an `enter` to do what `hoistServer`
1138does now. `enter` had an ambitious design goals, but was problematic in practice.
1140One problematic situation was when the source monad was `(->) r`, yet it's
1141handy in practice, because `(->) r` is isomorphic to `Reader r`.
1143We can rewrite the previous example without `Reader`:
1145```haskell
1146funServerT :: ServerT ReaderAPI ((->) String)
1147funServerT = a :<|> b where
1148 a :: String -> Int
1149 a _ = 1797
1151 -- unfortunately, we cannot make `String` the first argument.
1152 b :: Double -> String -> Bool
1153 b _ s = s == "hi"
1155funToHandler :: (String -> a) -> Handler a
1156funToHandler f = return (f "hi")
1158app5 :: Application
1159app5 = serve readerAPI (hoistServer readerAPI funToHandler funServerT)
1160```
1162## Streaming endpoints
1164We can create endpoints that don't just give back a single result, but give
1165back a *stream* of results, served one at a time. Stream endpoints only provide
1166a single content type, and also specify what framing strategy is used to
1167delineate the results. To serve these results, we need to give back a stream
1168producer. Adapters can be written to *Pipes*, *Conduit* and the like, or
1169written directly as `SourceIO`s. SourceIO builds upon servant's own `SourceT`
1170stream type (it's simpler than *Pipes* or *Conduit*).
1171The API of a streaming endpoint needs to explicitly specify which sort of
1172generator 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.
1175``` haskell
1176type StreamAPI = "userStream" :> StreamGet NewlineFraming JSON (SourceIO User)
1177streamAPI :: Proxy StreamAPI
1178streamAPI = Proxy
1180streamUsers :: SourceIO User
1181streamUsers = source [isaac, albert, albert]
1183app6 :: Application
1184app6 = serve streamAPI (return streamUsers)
1185```
1187This simple application returns a stream of `User` values encoded in JSON
1188format, with each value separated by a newline. In this case, the stream will
1189consist of the value of `isaac`, followed by the value of `albert`, then the
1190value of `albert` a second time. Importantly, the stream is written back as
1191results are produced, rather than all at once. This means first that results
1192are delivered when they are available, and second, that if an exception
1193interrupts production of the full stream, nonetheless partial results have
1194already been written back.
1196## Conclusion
1198You're now equipped to write webservices/web-applications using
1199**servant**. The rest of this document focuses on **servant-client**,
1200**servant-js** and **servant-docs**.