@@ -0,0 +1,1200 @@
3+Enough chit-chat about type-level combinators and representing an API as a
4+type. Can we have a webservice already?
8+Equipped with some basic knowledge about the way we represent APIs, let's now
9+write our first webservice.
11+The source for this tutorial section is a literate haskell file, so first we
12+need to have some language extensions and imports:
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 #-}
30+import Control.Monad.Except
31+import Control.Monad.Reader
33+import Data.Aeson.Types
34+import Data.Attoparsec.ByteString
35+import Data.ByteString (ByteString)
38+import Data.String.Conversions
39+import Data.Time.Calendar
42+import Network.HTTP.Media ((//), (/:))
44+import Network.Wai.Handler.Warp
46+import System.Directory
48+import Text.Blaze.Html.Renderer.Utf8
49+import Servant.Types.SourceT (source)
50+import qualified Data.Aeson.Parser
51+import qualified Text.Blaze.Html
54+**Important**: the `Servant` module comes from the **servant-server** package,
55+the one that lets us run webservers that implement a particular API type. It
56+reexports all the types from the **servant** package that let you declare API
57+types as well as everything you need to turn your request handlers into a
58+fully-fledged webserver. This means that in your applications, you can just add
59+**servant-server** as a dependency, import `Servant` and not worry about anything
62+We will write a server that will serve the following API.
65+type UserAPI1 = "users" :> Get '[JSON] [User]
68+Here's what we would like to see when making a GET request to `/users`.
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"}
76+Now let's define our `User` data type and write some instances for it.
83+ , registration_date :: Day
84+ } deriving (Eq, Show, Generic)
89+Nothing funny going on here. But we now can define our list of two users.
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)
99+We can now take care of writing the actual webservice that will handle requests
100+to such an API. This one will be very simple, being reduced to just a single
101+endpoint. The type of the web application is determined by the API type,
102+through a *type family* named `Server`. (Type families are just functions that
103+take types as input and return types.) The `Server` type family will compute
104+the right type that a bunch of request handlers should have just from the
105+corresponding API type.
107+The first thing to know about the `Server` type family is that behind the
108+scenes it will drive the routing, letting you focus only on the business
109+logic. The second thing to know is that for each endpoint, your handlers will
110+by default run in the `Handler` monad. This is overridable very
111+easily, as explained near the end of this guide. Third thing, the type of the
112+value returned in that monad must be the same as the second argument of the
113+HTTP method combinator used for the corresponding endpoint. In our case, it
114+means we must provide a handler of type `Handler [User]`. Well,
115+we have a monad, let's just `return` our list:
118+server1 :: Server UserAPI1
119+server1 = return users1
122+That's it. Now we can turn `server` into an actual webserver using
123+[wai](http://hackage.haskell.org/package/wai) and
124+[warp](http://hackage.haskell.org/package/warp):
127+userAPI :: Proxy UserAPI1
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.
134+app1 = serve userAPI server1
137+The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
138+But that's about as much boilerplate as you get.
140+And we're done! Let's run our webservice on the port 8081.
147+You can put this all into a file or just grab [servant's
148+repo](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*.
152+If you run it, you can go to `http://localhost:8081/users` in your browser or
153+query it with curl and you see:
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"}]
162+What if we want more than one endpoint? Let's add `/albert` and `/isaac` to
163+view the corresponding users encoded in JSON.
166+type UserAPI2 = "users" :> Get '[JSON] [User]
167+ :<|> "albert" :> Get '[JSON] User
168+ :<|> "isaac" :> Get '[JSON] User
171+And let's adapt our code a bit.
175+isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
178+albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
181+users2 = [isaac, albert]
184+Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
185+are going to separate the handlers with `:<|>` too! They must be provided in
186+the same order as in the API type.
189+server2 :: Server UserAPI2
190+server2 = return users2
195+And that's it! You can run this example in the same way that we showed for
196+`server1` and check out the data available at `/users`, `/albert` and `/isaac`.
198+## From combinators to handler arguments
200+Fine, we can write trivial webservices easily, but none of the two above use
201+any "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
203+combinators in an endpoint makes the corresponding handler receive an
204+argument of the appropriate type automatically. You don't have to worry about
205+manually looking up URL captures or query string parameters, or
206+decoding/encoding data from/to JSON. Never.
208+We are going to use the following data types and functions to implement a
212+type 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
216+data Position = Position
221+instance ToJSON Position
223+newtype HelloMessage = HelloMessage { msg :: String }
226+instance ToJSON HelloMessage
228+data ClientInfo = ClientInfo
229+ { clientName :: String
230+ , clientEmail :: String
232+ , clientInterestedIn :: [String]
235+instance FromJSON ClientInfo
236+instance ToJSON ClientInfo
245+instance ToJSON Email
247+emailForClient :: ClientInfo -> Email
248+emailForClient c = Email from' to' subject' body'
250+ where from' = "great@company.com"
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!"
260+We can implement handlers for the three endpoints:
263+server3 :: Server API
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)
280+Did you see that? The types for your handlers changed to be just what we
281+needed! 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
285+an endpoint can technically be accessed without specifying any query
286+string parameter, we decided to "force" handlers to be aware that the
287+parameter might not always be there);
289+ - a `ReqBody contentTypeList a` becomes an argument of type `a`;
291+And that's it. Here's the example in action:
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
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"}
304+For 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
315+Wait... How does **servant** know how to decode the `Int`s from the URL? Or how
316+to decode a `ClientInfo` value from the request body? The following three sections will
317+help us answer these questions.
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
321+corresponding (textual) value in the request's "metadata". How types are
322+decoded from headers, captures, and query params is expressed in a class
323+`FromHttpApiData` (from the package
324+[**http-api-data**](http://hackage.haskell.org/package/http-api-data)):
327+class 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
342+As you can see, as long as you provide either `parseUrlPiece` (for `Capture`s)
343+or `parseQueryParam` (for `QueryParam`s), the other methods will be defined in
346+**http-api-data** provides a decent number of instances, helpers for defining new
347+ones, and wonderful documentation.
349+There's not much else to say about these classes. You will need instances for
350+them when using `Capture`, `QueryParam`, `QueryParams`, and `Header` with your
351+types. You will need `FromHttpApiData` instances for server-side request
352+handlers and `ToHttpApiData` instances only when using
353+**servant-client**, as described in the [section about deriving haskell
354+functions to query an API](Client.html).
356+## Using content-types with your data types
358+The same principle was operating when decoding request bodies from JSON, and
359+responses *into* JSON. (JSON is just the running example - you can do this with
362+This section introduces a couple of typeclasses provided by **servant** that make
365+### The truth behind `JSON`
368+What exactly is `JSON` (the type as used in `Get '[JSON] User`)? Like the 3
369+other content-types provided out of the box by **servant**, it's a really dumb
379+Obviously, this is not all there is to `JSON`, otherwise it would be quite
380+pointless. Like most of the data types in **servant**, `JSON` is mostly there as
381+a special *symbol* that's associated with encoding (resp. decoding) to (resp.
382+from) the *JSON* format. The way this association is performed can be
383+decomposed into two steps.
385+The first step is to provide a proper
387+[**http-media**](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html))
388+representation for `JSON`, or for your own content-types. If you look at the
389+haddocks 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
391+use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
392+the `MediaType` is to write an instance for the `Accept` class:
396+class Accept ctype where
397+ contentType :: Proxy ctype -> MediaType
399+instance Accept JSON where
400+ contentType _ = "application" // "json"
403+The second step is centered around the `MimeRender` and `MimeUnrender` classes.
404+These classes just let you specify a way to encode and decode
405+values into or from your content-type's representation.
408+class Accept ctype => MimeRender ctype a where
409+ mimeRender :: Proxy ctype -> a -> ByteString
410+ -- alternatively readable as:
411+ mimeRender :: Proxy ctype -> (a -> ByteString)
414+Given a content-type and some user type, `MimeRender` provides a function that
415+encodes values of type `a` to lazy `ByteString`s.
417+In 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
422+instance ToJSON a => MimeRender JSON a where
423+ mimeRender _ = encode
426+And now the `MimeUnrender` class, which lets us extract values from lazy
427+`ByteString`s, alternatively failing with an error string.
430+class Accept ctype => MimeUnrender ctype a where
431+ mimeUnrender :: Proxy ctype -> ByteString -> Either String a
434+We don't have much work to do there either, `Data.Aeson.eitherDecode` is
435+precisely what we need. However, it only allows arrays and objects as toplevel
436+JSON values and this has proven to get in our way more than help us so we wrote
437+our own little function around **aeson** and **attoparsec** that allows any type of
438+JSON value at the toplevel of a "JSON document". Here's the definition in case
442+eitherDecodeLenient :: FromJSON a => ByteString -> Either String a
443+eitherDecodeLenient input = do
444+ v :: Value <- parseOnly (Data.Aeson.Parser.value <* endOfInput) (cs input)
445+ parseEither parseJSON v
448+This function is exactly what we need for our `MimeUnrender` instance.
451+instance FromJSON a => MimeUnrender JSON a where
452+ mimeUnrender _ = eitherDecodeLenient
455+And 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
457+for an `HTML` content-type, so that users of your webservice can access an
458+HTML representation of the data they want, ready to be included in any HTML
459+document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/),
460+simply by adding `Accept: text/html` to their request headers.
462+### Case-studies: **servant-blaze** and **servant-lucid**
464+These days, most of the haskellers who write their HTML UIs directly from
465+Haskell use either [**blaze-html**](http://hackage.haskell.org/package/blaze-html)
466+or [**lucid**](http://hackage.haskell.org/package/lucid). The best option for
467+**servant** is obviously to support both (and hopefully other templating
468+solutions!). We're first going to look at **lucid**:
474+Once again, the data type is just there as a symbol for the encoding/decoding
475+functions, except that this time we will only worry about encoding since
476+**lucid** doesn't provide a way to extract data from HTML.
479+instance Accept HTMLLucid where
480+ contentType _ = "text" // "html" /: ("charset", "utf-8")
483+Note that this instance uses the `(/:)` operator from **http-media** which lets
484+us specify additional information about a content-type, like the charset here.
486+The rendering instances call similar functions that take
487+types with an appropriate instance to an "abstract" HTML representation and
488+then write that to a `ByteString`.
491+instance ToHtml a => MimeRender HTMLLucid a where
492+ mimeRender _ = renderBS . toHtml
494+-- let's also provide an instance for lucid's
496+instance MimeRender HTMLLucid (Html a) where
497+ mimeRender _ = renderBS
500+For **blaze-html** everything works very similarly:
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.
508+instance Accept HTMLBlaze where
509+ contentType _ = "text" // "html" /: ("charset", "utf-8")
511+instance 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
516+instance MimeRender HTMLBlaze Text.Blaze.Html.Html where
517+ mimeRender _ = renderHtml
520+Both [**servant-blaze**](http://hackage.haskell.org/package/servant-blaze) and
521+[**servant-lucid**](http://hackage.haskell.org/package/servant-lucid) let you use
522+`HTMLLucid` and `HTMLBlaze` in any content-type list as long as you provide an instance of the
523+appropriate class (`ToMarkup` for **blaze-html**, `ToHtml` for **lucid**).
525+We can now write a webservice that uses **servant-lucid** to show the `HTMLLucid`
526+content-type in action. We will be serving the following API:
529+type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
532+where `Person` is defined as follows:
536+ { firstName :: String
537+ , lastName :: String
538+ } deriving Generic -- for the JSON instance
540+instance ToJSON Person
543+Now, let's teach **lucid** how to render a `Person` as a row in a table, and then
544+a list of `Person`s as a table with a row per person.
547+-- HTML serialization of a single person
548+instance ToHtml Person where
551+ td_ (toHtml $ firstName person)
552+ td_ (toHtml $ lastName person)
554+ -- do not worry too much about this
557+-- HTML serialization of a list of persons
558+instance ToHtml [Person] where
559+ toHtml persons = table_ $ do
564+ -- this just calls toHtml on each person of the list
565+ -- and concatenates the resulting pieces of HTML together
566+ foldMap toHtml persons
571+We create some `Person` values and serve them as a list:
576+ [ Person "Isaac" "Newton"
577+ , Person "Albert" "Einstein"
580+personAPI :: Proxy PersonAPI
583+server4 :: Server PersonAPI
584+server4 = return people
587+app2 = serve personAPI server4
590+And we're good to go:
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
600+## The `Handler` monad
602+At the heart of the handlers is the monad they run in, namely a newtype `Handler` around `ExceptT ServerError IO`
603+([haddock documentation for `ExceptT`](http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT)).
604+One might wonder: why this monad? The answer is that it is the
605+simplest monad with the following properties:
607+- it lets us both return a successful result (using `return`)
608+or "fail" with a descriptive error (using `throwError`);
609+- it lets us perform IO, which is absolutely vital since most webservices exist
610+as interfaces to databases that we interact with in `IO`.
612+Let's recall some definitions.
615+-- from the 'mtl' package at
616+newtype ExceptT e m a = ExceptT (m (Either e a))
619+In short, this means that a handler of type `Handler a` is simply
620+equivalent to a computation of type `IO (Either ServerError a)`, that is, an IO
621+action that either returns an error or a result.
623+The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl/docs/Control-Monad-Except.html#t:ExceptT)
624+from which `ExceptT` comes is worth looking at.
625+Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so
626+`throwError` can be used to return an error from your handler (whereas `return`
627+ is enough to return a success).
629+Most of what you'll be doing in your handlers is running some IO and,
630+depending on the result, you might sometimes want to throw an error of some
631+kind and abort early. The next two sections cover how to do just that.
635+Other important instances from the list above are `MonadIO m => MonadIO
636+(ExceptT e m)`, and therefore also `MonadIO Handler` as there is a `MonadIO IO` instance.
637+[`MonadIO`](http://hackage.haskell.org/package/base/docs/Control-Monad-IO-Class.html#t:MonadIO)
638+is a class from the **transformers** package defined as:
641+class Monad m => MonadIO m where
642+ liftIO :: IO a -> m a
645+So if you want to run any kind of
646+IO computation in your handlers, just use `liftIO`:
649+type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
651+newtype FileContent = FileContent
652+ { content :: String }
655+instance ToJSON FileContent
657+server5 :: Server IOAPI1
659+ filecontent <- liftIO (readFile "myfile.txt")
660+ return (FileContent filecontent)
663+### Failing, through `ServerError`
665+If you want to explicitly fail at providing the result promised by an endpoint
666+using the appropriate HTTP status code (not found, unauthorized, etc) and some
667+error message, all you have to do is use the `throwError` function mentioned above
668+and provide it with the appropriate value of type `ServerError`, which is
672+data ServerError = ServerError
673+ { errHTTPCode :: Int
674+ , errReasonPhrase :: String
675+ , errBody :: ByteString -- lazy bytestring
676+ , errHeaders :: [Header]
680+Many standard values are provided out of the box by the `Servant.Server`
681+module. If you want to use these values but add a body or some headers, just
682+use record update syntax:
685+failingHandler :: Handler ()
686+failingHandler = throwError myerr
688+ where myerr :: ServerError
689+ myerr = err503 { errBody = "Sorry dear user." }
692+Here's an example where we return a customised 404-Not-Found error message in
693+the response body if "myfile.txt" isn't there:
696+server6 :: Server IOAPI1
698+ exists <- liftIO (doesFileExist "myfile.txt")
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." }
706+Here's how that server looks in action:
709+$ curl --verbose http://localhost:8081/myfile.txt
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
717+< HTTP/1.1 404 Not Found
719+myfile.txt just isn't there, please leave this server alone.
721+$ echo Hello > myfile.txt
723+$ curl --verbose http://localhost:8081/myfile.txt
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
733+< Content-Type: application/json
735+{"content":"Hello\n"}
740+To add headers to your response, use
741+[addHeader](http://hackage.haskell.org/package/servant/docs/Servant-API-ResponseHeaders.html).
742+Note that this changes the type of your API, as we can see in the following example:
745+type MyHandler = Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
747+myHandler :: Server MyHandler
748+myHandler = return $ addHeader 1797 albert
751+Note that the type of `addHeader header x` is different than the type of `x`!
752+And if you add more headers, more headers will appear in the header list:
755+type MyHeadfulHandler = Get '[JSON] (Headers '[Header "X-A-Bool" Bool, Header "X-An-Int" Int] User)
757+myHeadfulHandler :: Server MyHeadfulHandler
758+myHeadfulHandler = return $ addHeader True $ addHeader 1797 albert
761+But what if your handler only *sometimes* adds a header? If you declare that
762+your handler adds headers, and you don't add one, the return type of your
763+handler will be different than expected. To solve this, you have to explicitly
764+*not* add a header by using `noHeader`:
767+type MyMaybeHeaderHandler
768+ = Capture "withHeader" Bool :> Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
770+myMaybeHeaderHandler :: Server MyMaybeHeaderHandler
771+myMaybeHeaderHandler x = return $ if x then addHeader 1797 albert
772+ else noHeader albert
775+## Serving static files
777+**servant-server** also provides a way to just serve the content of a directory
778+under 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
780+application". Well, **servant-server** provides a function to get a file and
781+directory serving WAI application, namely:
784+-- exported by Servant and Servant.Server
785+serveDirectoryWebApp :: FilePath -> Server Raw
788+`serveDirectoryWebApp`'s argument must be a path to a valid directory.
790+Here's an example API that will serve some static files:
793+type StaticAPI = "static" :> Raw
799+staticAPI :: Proxy StaticAPI
804+server7 :: Server StaticAPI
805+server7 = serveDirectoryWebApp "static-files"
808+app3 = serve staticAPI server7
811+This server will match any request whose path starts with `/static` and will look
812+for a file at the path described by the rest of the request path, inside the
813+ *static-files/* directory of the path you run the program from.
815+In other words: If a client requests `/static/foo.txt`, the server will look for a file at
816+`./static-files/foo.txt`. If that file exists it'll succeed and serve the file.
817+If it doesn't exist, the handler will fail with a `404` status code.
819+`serveDirectoryWebApp` uses some standard settings that fit the use case of
820+serving static files for most web apps. You can find out about the other
821+options in the documentation of the `Servant.Server.StaticFiles` module.
825+Let's see how you can define APIs in a modular way, while avoiding repetition.
826+Consider this simple example:
829+type 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
836+We can instead factor out the `userid`:
839+type UserAPI4 = Capture "userid" Int :>
841+ :<|> DeleteNoContent
845+However, you have to be aware that this has an effect on the type of the
846+corresponding `Server`:
849+Server UserAPI3 = (Int -> Handler User)
850+ :<|> (Int -> Handler NoContent)
852+Server UserAPI4 = Int -> ( Handler User
853+ :<|> Handler NoContent
857+In the first case, each handler receives the *userid* argument. In the latter,
858+the whole `Server` takes the *userid* and has handlers that are just
859+computations in `Handler`, with no arguments. In other words:
862+server8 :: Server UserAPI3
863+server8 = 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
874+server9 :: Server UserAPI4
875+server9 userid = getUser userid :<|> deleteUser userid
877+ where getUser :: Int -> Handler User
878+ getUser = error "..."
880+ deleteUser :: Int -> Handler NoContent
881+ deleteUser = error "..."
884+Note that there's nothing special about `Capture` that lets you "factor it
885+out": this can be done with any combinator. Here are a few examples of APIs
886+with a combinator factored out for which we can write a perfectly valid
890+-- we just factor out the "users" path fragment
891+type API1 = "users" :>
892+ ( Get '[JSON] [User] -- user listing
893+ :<|> Capture "userid" Int :> Get '[JSON] User -- view a particular user
896+-- we factor out the Request Body
897+type 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
902+-- we factor out a Header
903+type 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
908+newtype Token = Token ByteString
909+newtype SecretData = SecretData ByteString
912+This approach lets you define APIs modularly and assemble them all into one big
913+API type only at the end.
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
925+usersServer :: Server UsersAPI
926+usersServer = 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
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 "..."
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
958+data Product = Product { productId :: Int }
960+productsServer :: Server ProductsAPI
961+productsServer = 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
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 "..."
984+type CombinedAPI = "users" :> UsersAPI
985+ :<|> "products" :> ProductsAPI
987+server10 :: Server CombinedAPI
988+server10 = usersServer :<|> productsServer
991+Finally, we can realize the user and product APIs are quite similar and
995+-- API for values of type 'a'
996+-- indexed by values of type '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.
1008+serverFor :: Handler [a] -- handler for listing of 'a's
1009+ -> (a -> Handler NoContent) -- handler for adding an 'a'
1010+ -> (i -> Handler a) -- handler for viewing an 'a' given its identifier of type 'i'
1011+ -> (i -> a -> Handler NoContent) -- updating an 'a' with given id
1012+ -> (i -> Handler NoContent) -- deleting an 'a' given its id
1013+ -> Server (APIFor a i)
1014+serverFor = error "..."
1015+-- implementation left as an exercise. contact us on IRC
1016+-- or the mailing list if you get stuck!
1019+When your API contains the `EmptyAPI` combinator, you'll want to use
1020+`emptyServer` in the corresponding slot for your server, which will simply fail
1021+with 404 whenever a request reaches it:
1024+type CombinedAPI2 = API :<|> "empty" :> EmptyAPI
1026+server11 :: Server CombinedAPI2
1027+server11 = server3 :<|> emptyServer
1030+## Using another monad for your handlers
1032+Remember how `Server` turns combinators for HTTP methods into `Handler`? Well, actually, there's more to that. `Server` is actually a
1033+simple type synonym.
1036+type Server api = ServerT api Handler
1039+`ServerT` is the actual type family that computes the required types for the
1040+handlers that's part of the `HasServer` class. It's like `Server` except that
1041+it takes another parameter which is the monad you want your handlers to run in,
1042+or more generally the return types of your handlers. This third parameter is
1043+used for specifying the return type of the handler for an endpoint, e.g when
1044+computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be
1047+The first and main question one might have then is: how do we write handlers
1048+that run in another monad? How can we "bring back" the value from a given monad
1049+into something **servant** can understand?
1051+### Natural transformations
1053+If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1057+type (~>) m n = forall a. m a -> n a
1063+listToMaybe' :: [] ~> Maybe
1064+listToMaybe' = listToMaybe -- from Data.Maybe
1067+Note that `servant` doesn't declare the `~>` type-alias, as the unfolded
1068+variant isn't much longer to write, as we'll see shortly.
1070+So if you want to write handlers using another monad/type than `Handler`, say the `Reader String` monad, the first thing you have to
1071+prepare is a function:
1074+readerToHandler :: Reader String a -> Handler a
1077+We obviously have to run the `Reader` computation by supplying it with a
1078+`String`, like `"hi"`. We get an `a` out from that and can then just `return`
1082+readerToHandler :: Reader String a -> Handler a
1083+readerToHandler r = return (runReader r "hi")
1086+We can write some simple webservice with the handlers running in `Reader String`.
1089+type ReaderAPI = "a" :> Get '[JSON] Int
1090+ :<|> "b" :> ReqBody '[JSON] Double :> Get '[JSON] Bool
1092+readerAPI :: Proxy ReaderAPI
1095+readerServerT :: ServerT ReaderAPI (Reader String)
1096+readerServerT = a :<|> b where
1097+ a :: Reader String Int
1100+ b :: Double -> Reader String Bool
1101+ b _ = asks (== "hi")
1104+We 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`
1109+That's right. We have just written `readerToHandler`, which is exactly what we
1110+would need to apply to all handlers to make the handlers have the
1111+right 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`
1113+and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1115+In our case, we can wrap up our little webservice by using
1116+`hoistServer readerAPI readerToHandler` on our handlers.
1119+readerServer :: Server ReaderAPI
1120+readerServer = hoistServer readerAPI readerToHandler readerServerT
1123+app4 = serve readerAPI readerServer
1126+This is the webservice in action:
1129+$ curl http://localhost:8081/a
1131+$ curl http://localhost:8081/b -X GET -d '42.0' -H 'Content-Type: application/json'
1135+### An arrow is a reader too.
1137+In previous versions of `servant` we had an `enter` to do what `hoistServer`
1138+does now. `enter` had an ambitious design goals, but was problematic in practice.
1140+One problematic situation was when the source monad was `(->) r`, yet it's
1141+handy in practice, because `(->) r` is isomorphic to `Reader r`.
1143+We can rewrite the previous example without `Reader`:
1146+funServerT :: ServerT ReaderAPI ((->) String)
1147+funServerT = a :<|> b where
1151+ -- unfortunately, we cannot make `String` the first argument.
1152+ b :: Double -> String -> Bool
1155+funToHandler :: (String -> a) -> Handler a
1156+funToHandler f = return (f "hi")
1159+app5 = serve readerAPI (hoistServer readerAPI funToHandler funServerT)
1162+## Streaming endpoints
1164+We can create endpoints that don't just give back a single result, but give
1165+back a *stream* of results, served one at a time. Stream endpoints only provide
1166+a single content type, and also specify what framing strategy is used to
1167+delineate the results. To serve these results, we need to give back a stream
1168+producer. Adapters can be written to *Pipes*, *Conduit* and the like, or
1169+written directly as `SourceIO`s. SourceIO builds upon servant's own `SourceT`
1170+stream type (it's simpler than *Pipes* or *Conduit*).
1171+The API of a streaming endpoint needs to explicitly specify which sort of
1172+generator it produces. Note that the generator itself is returned by a
1173+`Handler` action, so that additional IO may be done in the creation of one.
1176+type StreamAPI = "userStream" :> StreamGet NewlineFraming JSON (SourceIO User)
1177+streamAPI :: Proxy StreamAPI
1180+streamUsers :: SourceIO User
1181+streamUsers = source [isaac, albert, albert]
1184+app6 = serve streamAPI (return streamUsers)
1187+This simple application returns a stream of `User` values encoded in JSON
1188+format, with each value separated by a newline. In this case, the stream will
1189+consist of the value of `isaac`, followed by the value of `albert`, then the
1190+value of `albert` a second time. Importantly, the stream is written back as
1191+results are produced, rather than all at once. This means first that results
1192+are delivered when they are available, and second, that if an exception
1193+interrupts production of the full stream, nonetheless partial results have
1194+already been written back.
1198+You're now equipped to write webservices/web-applications using
1199+**servant**. The rest of this document focuses on **servant-client**,
1200+**servant-js** and **servant-docs**.