@@ -0,0 +1,1115 @@
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 ScopedTypeVariables #-}
22+{-# LANGUAGE TypeOperators #-}
29+import Control.Monad.Except
30+import Control.Monad.Reader
31+import Data.Aeson.Compat
32+import Data.Aeson.Types
33+import Data.Attoparsec.ByteString
34+import Data.ByteString (ByteString)
37+import Data.String.Conversions
38+import Data.Time.Calendar
41+import Network.HTTP.Media ((//), (/:))
43+import Network.Wai.Handler.Warp
45+import System.Directory
47+import Text.Blaze.Html.Renderer.Utf8
48+import qualified Data.Aeson.Parser
49+import qualified Text.Blaze.Html
52+**Important**: the `Servant` module comes from the **servant-server** package,
53+the one that lets us run webservers that implement a particular API type. It
54+reexports all the types from the **servant** package that let you declare API
55+types as well as everything you need to turn your request handlers into a
56+fully-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
60+We will write a server that will serve the following API.
63+type UserAPI1 = "users" :> Get '[JSON] [User]
66+Here's what we would like to see when making a GET request to `/users`.
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"}
74+Now let's define our `User` data type and write some instances for it.
81+ , registration_date :: Day
82+ } deriving (Eq, Show, Generic)
87+Nothing funny going on here. But we now can define our list of two users.
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)
97+Let's also write our API type.
100+type UserAPI1 = "users" :> Get '[JSON] [User]
103+We can now take care of writing the actual webservice that will handle requests
104+to such an API. This one will be very simple, being reduced to just a single
105+endpoint. The type of the web application is determined by the API type,
106+through a *type family* named `Server`. (Type families are just functions that
107+take types as input and return types.) The `Server` type family will compute
108+the right type that a bunch of request handlers should have just from the
109+corresponding API type.
111+The first thing to know about the `Server` type family is that behind the
112+scenes it will drive the routing, letting you focus only on the business
113+logic. The second thing to know is that for each endpoint, your handlers will
114+by default run in the `ExceptT ServantErr IO` monad. This is overridable very
115+easily, as explained near the end of this guide. Third thing, the type of the
116+value returned in that monad must be the same as the second argument of the
117+HTTP method combinator used for the corresponding endpoint. In our case, it
118+means we must provide a handler of type `ExceptT ServantErr IO [User]`. Well,
119+we have a monad, let's just `return` our list:
122+server1 :: Server UserAPI1
123+server1 = return users1
126+That'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):
131+userAPI :: Proxy UserAPI1
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.
138+app1 = serve userAPI server1
141+The `userAPI` bit is, alas, boilerplate (we need it to guide type inference).
142+But that's about as much boilerplate as you get.
144+And we're done! Let's run our webservice on the port 8081.
151+You can put this all into a file or just grab [servant's
152+repo](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*.
156+If you run it, you can go to `http://localhost:8081/users` in your browser or
157+query it with curl and you see:
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"}]
166+What if we want more than one endpoint? Let's add `/albert` and `/isaac` to
167+view the corresponding users encoded in JSON.
170+type UserAPI2 = "users" :> Get '[JSON] [User]
171+ :<|> "albert" :> Get '[JSON] User
172+ :<|> "isaac" :> Get '[JSON] User
175+And let's adapt our code a bit.
179+isaac = User "Isaac Newton" 372 "isaac@newton.co.uk" (fromGregorian 1683 3 1)
182+albert = User "Albert Einstein" 136 "ae@mc2.org" (fromGregorian 1905 12 1)
185+users2 = [isaac, albert]
188+Now, just like we separate the various endpoints in `UserAPI` with `:<|>`, we
189+are going to separate the handlers with `:<|>` too! They must be provided in
190+the same order as in in the API type.
193+server2 :: Server UserAPI2
194+server2 = return users2
199+And 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
204+Fine, we can write trivial webservices easily, but none of the two above use
205+any "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
207+combinators in an endpoint makes the corresponding handler receive an
208+argument of the appropriate type automatically. You don't have to worry about
209+manually looking up URL captures or query string parameters, or
210+decoding/encoding data from/to JSON. Never.
212+We are going to use the following data types and functions to implement a
216+type 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
220+data Position = Position
225+instance ToJSON Position
227+newtype HelloMessage = HelloMessage { msg :: String }
230+instance ToJSON HelloMessage
232+data ClientInfo = ClientInfo
233+ { clientName :: String
234+ , clientEmail :: String
236+ , clientInterestedIn :: [String]
239+instance FromJSON ClientInfo
240+instance ToJSON ClientInfo
249+instance ToJSON Email
251+emailForClient :: ClientInfo -> Email
252+emailForClient c = Email from' to' subject' body'
254+ where from' = "great@company.com"
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!"
264+We can implement handlers for the three endpoints:
267+server3 :: Server API
272+ where position :: Int -> Int -> ExceptT ServantErr IO Position
273+ position x y = return (Position x y)
275+ hello :: Maybe String -> ExceptT ServantErr IO HelloMessage
276+ hello mname = return . HelloMessage $ case mname of
277+ Nothing -> "Hello, anonymous coward"
278+ Just n -> "Hello, " ++ n
280+ marketing :: ClientInfo -> ExceptT ServantErr IO Email
281+ marketing clientinfo = return (emailForClient clientinfo)
284+Did you see that? The types for your handlers changed to be just what we
285+needed! 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
289+an endpoint can technically be accessed without specifying any query
290+string parameter, we decided to "force" handlers to be aware that the
291+parameter might not always be there);
293+ - a `ReqBody contentTypeList a` becomes an argument of type `a`;
295+And that's it. Here's the example in action:
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
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"}
308+For 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 `ExceptT ServantErr IO <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
319+Wait... How does **servant** know how to decode the `Int`s from the URL? Or how
320+to decode a `ClientInfo` value from the request body? This is what this and the
321+following 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
325+corresponding (textual) value in the request's "metadata". How types are
326+decoded 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)):
331+class 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
346+As you can see, as long as you provide either `parseUrlPiece` (for `Capture`s)
347+or `parseQueryParam` (for `QueryParam`s), the other methods will be defined in
350+**http-api-data** provides a decent number of instances, helpers for defining new
351+ones, and wonderful documentation.
353+There's not much else to say about these classes. You will need instances for
354+them when using `Capture`, `QueryParam`, `QueryParams`, and `Header` with your
355+types. You will need `FromHttpApiData` instances for server-side request
356+handlers and `ToHttpApiData` instances only when using
357+**servant-client**, as described in the [section about deriving haskell
358+functions to query an API](Client.html).
360+## Using content-types with your data types
362+The same principle was operating when decoding request bodies from JSON, and
363+responses *into* JSON. (JSON is just the running example - you can do this with
366+This section introduces a couple of typeclasses provided by **servant** that make
369+### The truth behind `JSON`
372+What exactly is `JSON` (the type as used in `Get '[JSON] User`)? Like the 3
373+other content-types provided out of the box by **servant**, it's a really dumb
383+Obviously, this is not all there is to `JSON`, otherwise it would be quite
384+pointless. Like most of the data types in **servant**, `JSON` is mostly there as
385+a special *symbol* that's associated with encoding (resp. decoding) to (resp.
386+from) the *JSON* format. The way this association is performed can be
387+decomposed into two steps.
389+The first step is to provide a proper
391+[**http-media**](https://hackage.haskell.org/package/http-media-0.6.2/docs/Network-HTTP-Media.html))
392+representation for `JSON`, or for your own content-types. If you look at the
393+haddocks 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
395+use `(//) :: ByteString -> ByteString -> MediaType`. The precise way to specify
396+the `MediaType` is to write an instance for the `Accept` class:
400+class Accept ctype where
401+ contentType :: Proxy ctype -> MediaType
403+instance Accept JSON where
404+ contentType _ = "application" // "json"
407+The second step is centered around the `MimeRender` and `MimeUnrender` classes.
408+These classes just let you specify a way to encode and decode
409+values into or from your content-type's representation.
412+class Accept ctype => MimeRender ctype a where
413+ mimeRender :: Proxy ctype -> a -> ByteString
414+ -- alternatively readable as:
415+ mimeRender :: Proxy ctype -> (a -> ByteString)
418+Given a content-type and some user type, `MimeRender` provides a function that
419+encodes values of type `a` to lazy `ByteString`s.
421+In 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
426+instance ToJSON a => MimeRender JSON a where
427+ mimeRender _ = encode
430+And now the `MimeUnrender` class, which lets us extract values from lazy
431+`ByteString`s, alternatively failing with an error string.
434+class Accept ctype => MimeUnrender ctype a where
435+ mimeUnrender :: Proxy ctype -> ByteString -> Either String a
438+We don't have much work to do there either, `Data.Aeson.eitherDecode` is
439+precisely what we need. However, it only allows arrays and objects as toplevel
440+JSON values and this has proven to get in our way more than help us so we wrote
441+our own little function around **aeson** and **attoparsec** that allows any type of
442+JSON value at the toplevel of a "JSON document". Here's the definition in case
446+eitherDecodeLenient :: FromJSON a => ByteString -> Either String a
447+eitherDecodeLenient input = do
448+ v :: Value <- parseOnly (Data.Aeson.Parser.value <* endOfInput) (cs input)
449+ parseEither parseJSON v
452+This function is exactly what we need for our `MimeUnrender` instance.
455+instance FromJSON a => MimeUnrender JSON a where
456+ mimeUnrender _ = eitherDecodeLenient
459+And 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
461+for an `HTML` content-type, so that users of your webservice can access an
462+HTML representation of the data they want, ready to be included in any HTML
463+document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/),
464+simply by adding `Accept: text/html` to their request headers.
466+### Case-studies: **servant-blaze** and **servant-lucid**
468+These days, most of the haskellers who write their HTML UIs directly from
469+Haskell use either [**blaze-html**](http://hackage.haskell.org/package/blaze-html)
470+or [**lucid**](http://hackage.haskell.org/package/lucid). The best option for
471+**servant** is obviously to support both (and hopefully other templating
472+solutions!). We're first going to look at **lucid**:
478+Once again, the data type is just there as a symbol for the encoding/decoding
479+functions, except that this time we will only worry about encoding since
480+**lucid** doesn't provide a way to extract data from HTML.
483+instance Accept HTMLLucid where
484+ contentType _ = "text" // "html" /: ("charset", "utf-8")
487+Note that this instance uses the `(/:)` operator from **http-media** which lets
488+us specify additional information about a content-type, like the charset here.
490+The rendering instances call similar functions that take
491+types with an appropriate instance to an "abstract" HTML representation and
492+then write that to a `ByteString`.
495+instance ToHtml a => MimeRender HTMLLucid a where
496+ mimeRender _ = renderBS . toHtml
498+-- let's also provide an instance for lucid's
500+instance MimeRender HTMLLucid (Html a) where
501+ mimeRender _ = renderBS
504+For **blaze-html** everything works very similarly:
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.
512+instance Accept HTMLBlaze where
513+ contentType _ = "text" // "html" /: ("charset", "utf-8")
515+instance 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
520+instance MimeRender HTMLBlaze Text.Blaze.Html.Html where
521+ mimeRender _ = renderHtml
524+Both [**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
527+appropriate class (`ToMarkup` for **blaze-html**, `ToHtml` for **lucid**).
529+We can now write a webservice that uses **servant-lucid** to show the `HTMLLucid`
530+content-type in action. We will be serving the following API:
533+type PersonAPI = "persons" :> Get '[JSON, HTMLLucid] [Person]
536+where `Person` is defined as follows:
540+ { firstName :: String
541+ , lastName :: String
542+ } deriving Generic -- for the JSON instance
544+instance ToJSON Person
547+Now, let's teach **lucid** how to render a `Person` as a row in a table, and then
548+a list of `Person`s as a table with a row per person.
551+-- HTML serialization of a single person
552+instance ToHtml Person where
555+ td_ (toHtml $ firstName person)
556+ td_ (toHtml $ lastName person)
558+ -- do not worry too much about this
561+-- HTML serialization of a list of persons
562+instance ToHtml [Person] where
563+ toHtml persons = table_ $ do
568+ -- this just calls toHtml on each person of the list
569+ -- and concatenates the resulting pieces of HTML together
570+ foldMap toHtml persons
575+We create some `Person` values and serve them as a list:
580+ [ Person "Isaac" "Newton"
581+ , Person "Albert" "Einstein"
584+personAPI :: Proxy PersonAPI
587+server4 :: Server PersonAPI
588+server4 = return people
591+app2 = serve personAPI server4
594+And we're good to go:
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
604+## The `ExceptT ServantErr IO` monad
606+At the heart of the handlers is the monad they run in, namely `ExceptT
608+([haddock documentation for `ExceptT`](http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT)).
609+One might wonder: why this monad? The answer is that it is the
610+simplest monad with the following properties:
612+- it lets us both return a successful result (using `return`)
613+or "fail" with a descriptive error (using `throwError`);
614+- it lets us perform IO, which is absolutely vital since most webservices exist
615+as interfaces to databases that we interact with in `IO`.
617+Let's recall some definitions.
620+-- from the 'mtl' package at
621+newtype ExceptT e m a = ExceptT (m (Either e a))
624+In short, this means that a handler of type `ExceptT ServantErr IO a` is simply
625+equivalent to a computation of type `IO (Either ServantErr a)`, that is, an IO
626+action that either returns an error or a result.
628+The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT)
629+from which `ExceptT` comes is worth looking at.
630+Perhaps most importantly, `ExceptT` is an instance of `MonadError`, so
631+`throwError` can be used to return an error from your handler (whereas `return`
632+ is enough to return a success).
634+Most of what you'll be doing in your handlers is running some IO and,
635+depending on the result, you might sometimes want to throw an error of some
636+kind and abort early. The next two sections cover how to do just that.
640+Another important instance from the list above is `MonadIO m => MonadIO
642+[`MonadIO`](http://hackage.haskell.org/package/transformers-0.4.3.0/docs/Control-Monad-IO-Class.html)
643+is a class from the **transformers** package defined as:
646+class Monad m => MonadIO m where
647+ liftIO :: IO a -> m a
650+The `IO` monad provides a `MonadIO` instance. Hence for any type
651+`e`, `ExceptT e IO` has a `MonadIO` instance. So if you want to run any kind of
652+IO computation in your handlers, just use `liftIO`:
655+type IOAPI1 = "myfile.txt" :> Get '[JSON] FileContent
657+newtype FileContent = FileContent
658+ { content :: String }
661+instance ToJSON FileContent
663+server5 :: Server IOAPI1
665+ filecontent <- liftIO (readFile "myfile.txt")
666+ return (FileContent filecontent)
669+### Failing, through `ServantErr`
671+If you want to explicitly fail at providing the result promised by an endpoint
672+using the appropriate HTTP status code (not found, unauthorized, etc) and some
673+error message, all you have to do is use the `throwError` function mentioned above
674+and provide it with the appropriate value of type `ServantErr`, which is
678+data ServantErr = ServantErr
679+ { errHTTPCode :: Int
680+ , errReasonPhrase :: String
681+ , errBody :: ByteString -- lazy bytestring
682+ , errHeaders :: [Header]
686+Many standard values are provided out of the box by the `Servant.Server`
687+module. If you want to use these values but add a body or some headers, just
688+use record update syntax:
691+failingHandler :: ExceptT ServantErr IO ()
692+failingHandler = throwError myerr
694+ where myerr :: ServantErr
695+ myerr = err503 { errBody = "Sorry dear user." }
698+Here's an example where we return a customised 404-Not-Found error message in
699+the response body if "myfile.txt" isn't there:
702+server6 :: Server IOAPI1
704+ exists <- liftIO (doesFileExist "myfile.txt")
706+ then liftIO (readFile "myfile.txt") >>= return . FileContent
707+ else throwError custom404Err
709+ where custom404Err = err404 { errBody = "myfile.txt just isn't there, please leave this server alone." }
712+Here's how that server looks in action:
715+$ curl --verbose http://localhost:8081/myfile.txt
717+* Connected to localhost (127.0.0.1) port 8081 (#0)
718+> GET /myfile.txt HTTP/1.1
719+> User-Agent: curl/7.30.0
720+> Host: localhost:8081
723+< HTTP/1.1 404 Not Found
725+myfile.txt just isnt there, please leave this server alone.
727+$ echo Hello > myfile.txt
729+$ curl --verbose http://localhost:8081/myfile.txt
731+* Connected to localhost (127.0.0.1) port 8081 (#0)
732+> GET /myfile.txt HTTP/1.1
733+> User-Agent: curl/7.30.0
734+> Host: localhost:8081
739+< Content-Type: application/json
741+{"content":"Hello\n"}
746+To add headers to your response, use
747+[addHeader](http://hackage.haskell.org/package/servant/docs/Servant-API-ResponseHeaders.html).
748+Note that this changes the type of your API, as we can see in the following example:
751+type MyHandler = Get '[JSON] (Headers '[Header "X-An-Int" Int] User)
753+myHandler :: Server MyHandler
754+myHandler = return $ addHeader 1797 albert
757+Note that the type of `addHeader x` is different than the type of `x`!
759+## Serving static files
761+**servant-server** also provides a way to just serve the content of a directory
762+under some path in your web API. As mentioned earlier in this document, the
763+`Raw` combinator can be used in your APIs to mean "plug here any WAI
764+application". Well, **servant-server** provides a function to get a file and
765+directory serving WAI application, namely:
768+-- exported by Servant and Servant.Server
769+serveDirectory :: FilePath -> Server Raw
772+`serveDirectory`'s argument must be a path to a valid directory.
774+Here's an example API that will serve some static files:
777+type StaticAPI = "static" :> Raw
783+staticAPI :: Proxy StaticAPI
788+server7 :: Server StaticAPI
789+server7 = serveDirectory "static-files"
792+app3 = serve staticAPI server7
795+This server will match any request whose path starts with `/static` and will look
796+for a file at the path described by the rest of the request path, inside the
797+ *static-files/* directory of the path you run the program from.
799+In other words: If a client requests `/static/foo.txt`, the server will look for a file at
800+`./static-files/foo.txt`. If that file exists it'll succeed and serve the file.
801+If it doesn't exist, the handler will fail with a `404` status code.
805+Let's see how you can define APIs in a modular way, while avoiding repetition.
806+Consider this simple example:
809+type UserAPI3 = -- view the user with given userid, in JSON
810+ Capture "userid" Int :> Get '[JSON] User
812+ :<|> -- delete the user with given userid. empty response
813+ Capture "userid" Int :> Delete '[] ()
816+We can instead factor out the `userid`:
819+type UserAPI4 = Capture "userid" Int :>
825+However, you have to be aware that this has an effect on the type of the
826+corresponding `Server`:
829+Server UserAPI3 = (Int -> ExceptT ServantErr IO User)
830+ :<|> (Int -> ExceptT ServantErr IO ())
832+Server UserAPI4 = Int -> ( ExceptT ServantErr IO User
833+ :<|> ExceptT ServantErr IO ()
837+In the first case, each handler receives the *userid* argument. In the latter,
838+the whole `Server` takes the *userid* and has handlers that are just
839+computations in `ExceptT`, with no arguments. In other words:
842+server8 :: Server UserAPI3
843+server8 = getUser :<|> deleteUser
845+ where getUser :: Int -> ExceptT ServantErr IO User
846+ getUser _userid = error "..."
848+ deleteUser :: Int -> ExceptT ServantErr IO ()
849+ deleteUser _userid = error "..."
851+-- notice how getUser and deleteUser
852+-- have a different type! no argument anymore,
853+-- the argument directly goes to the whole Server
854+server9 :: Server UserAPI4
855+server9 userid = getUser userid :<|> deleteUser userid
857+ where getUser :: Int -> ExceptT ServantErr IO User
858+ getUser = error "..."
860+ deleteUser :: Int -> ExceptT ServantErr IO ()
861+ deleteUser = error "..."
864+Note that there's nothing special about `Capture` that lets you "factor it
865+out": this can be done with any combinator. Here are a few examples of APIs
866+with a combinator factored out for which we can write a perfectly valid
870+-- we just factor out the "users" path fragment
871+type API1 = "users" :>
872+ ( Get '[JSON] [User] -- user listing
873+ :<|> Capture "userid" Int :> Get '[JSON] User -- view a particular user
876+-- we factor out the Request Body
877+type API2 = ReqBody '[JSON] User :>
878+ ( Get '[JSON] User -- just display the same user back, don't register it
879+ :<|> Post '[JSON] () -- register the user. empty response
882+-- we factor out a Header
883+type API3 = Header "Authorization" Token :>
884+ ( Get '[JSON] SecretData -- get some secret data, if authorized
885+ :<|> ReqBody '[JSON] SecretData :> Post '[] () -- add some secret data, if authorized
888+newtype Token = Token ByteString
889+newtype SecretData = SecretData ByteString
892+This approach lets you define APIs modularly and assemble them all into one big
893+API type only at the end.
897+ Get '[JSON] [User] -- list users
898+ :<|> ReqBody '[JSON] User :> Post '[] () -- add a user
899+ :<|> Capture "userid" Int :>
900+ ( Get '[JSON] User -- view a user
901+ :<|> ReqBody '[JSON] User :> Put '[] () -- update a user
902+ :<|> Delete '[] () -- delete a user
905+usersServer :: Server UsersAPI
906+usersServer = getUsers :<|> newUser :<|> userOperations
908+ where getUsers :: ExceptT ServantErr IO [User]
909+ getUsers = error "..."
911+ newUser :: User -> ExceptT ServantErr IO ()
912+ newUser = error "..."
914+ userOperations userid =
915+ viewUser userid :<|> updateUser userid :<|> deleteUser userid
918+ viewUser :: Int -> ExceptT ServantErr IO User
919+ viewUser = error "..."
921+ updateUser :: Int -> User -> ExceptT ServantErr IO ()
922+ updateUser = error "..."
924+ deleteUser :: Int -> ExceptT ServantErr IO ()
925+ deleteUser = error "..."
930+ Get '[JSON] [Product] -- list products
931+ :<|> ReqBody '[JSON] Product :> Post '[] () -- add a product
932+ :<|> Capture "productid" Int :>
933+ ( Get '[JSON] Product -- view a product
934+ :<|> ReqBody '[JSON] Product :> Put '[] () -- update a product
935+ :<|> Delete '[] () -- delete a product
938+data Product = Product { productId :: Int }
940+productsServer :: Server ProductsAPI
941+productsServer = getProducts :<|> newProduct :<|> productOperations
943+ where getProducts :: ExceptT ServantErr IO [Product]
944+ getProducts = error "..."
946+ newProduct :: Product -> ExceptT ServantErr IO ()
947+ newProduct = error "..."
949+ productOperations productid =
950+ viewProduct productid :<|> updateProduct productid :<|> deleteProduct productid
953+ viewProduct :: Int -> ExceptT ServantErr IO Product
954+ viewProduct = error "..."
956+ updateProduct :: Int -> Product -> ExceptT ServantErr IO ()
957+ updateProduct = error "..."
959+ deleteProduct :: Int -> ExceptT ServantErr IO ()
960+ deleteProduct = error "..."
964+type CombinedAPI = "users" :> UsersAPI
965+ :<|> "products" :> ProductsAPI
967+server10 :: Server CombinedAPI
968+server10 = usersServer :<|> productsServer
971+Finally, we can realize the user and product APIs are quite similar and
975+-- API for values of type 'a'
976+-- indexed by values of type 'i'
978+ Get '[JSON] [a] -- list 'a's
979+ :<|> ReqBody '[JSON] a :> Post '[] () -- add an 'a'
980+ :<|> Capture "id" i :>
981+ ( Get '[JSON] a -- view an 'a' given its "identifier" of type 'i'
982+ :<|> ReqBody '[JSON] a :> Put '[] () -- update an 'a'
983+ :<|> Delete '[] () -- delete an 'a'
986+-- Build the appropriate 'Server'
987+-- given the handlers of the right type.
988+serverFor :: ExceptT ServantErr IO [a] -- handler for listing of 'a's
989+ -> (a -> ExceptT ServantErr IO ()) -- handler for adding an 'a'
990+ -> (i -> ExceptT ServantErr IO a) -- handler for viewing an 'a' given its identifier of type 'i'
991+ -> (i -> a -> ExceptT ServantErr IO ()) -- updating an 'a' with given id
992+ -> (i -> ExceptT ServantErr IO ()) -- deleting an 'a' given its id
993+ -> Server (APIFor a i)
994+serverFor = error "..."
995+-- implementation left as an exercise. contact us on IRC
996+-- or the mailing list if you get stuck!
999+## Using another monad for your handlers
1001+Remember how `Server` turns combinators for HTTP methods into `ExceptT
1002+ServantErr IO`? Well, actually, there's more to that. `Server` is actually a
1003+simple type synonym.
1006+type Server api = ServerT api (ExceptT ServantErr IO)
1009+`ServerT` is the actual type family that computes the required types for the
1010+handlers that's part of the `HasServer` class. It's like `Server` except that
1011+it takes another parameter which is the monad you want your handlers to run in,
1012+or more generally the return types of your handlers. This third parameter is
1013+used for specifying the return type of the handler for an endpoint, e.g when
1014+computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be
1017+The first and main question one might have then is: how do we write handlers
1018+that run in another monad? How can we "bring back" the value from a given monad
1019+into something **servant** can understand?
1021+### Natural transformations
1023+If we have a function that gets us from an `m a` to an `n a`, for any `a`, what
1027+newtype m :~> n = Nat { unNat :: forall a. m a -> n a}
1033+listToMaybeNat :: [] :~> Maybe
1034+listToMaybeNat = Nat listToMaybe -- from Data.Maybe
1037+(`Nat` comes from "natural transformation", in case you're wondering.)
1039+So if you want to write handlers using another monad/type than `ExceptT
1040+ServantErr IO`, say the `Reader String` monad, the first thing you have to
1041+prepare is a function:
1044+readerToHandler :: Reader String :~> ExceptT ServantErr IO
1047+Let's start with `readerToHandler'`. We obviously have to run the `Reader`
1048+computation by supplying it with a `String`, like `"hi"`. We get an `a` out
1049+from that and can then just `return` it into `ExceptT`. We can then just wrap
1050+that function with the `Nat` constructor to make it have the fancier type.
1053+readerToHandler' :: forall a. Reader String a -> ExceptT ServantErr IO a
1054+readerToHandler' r = return (runReader r "hi")
1056+readerToHandler :: Reader String :~> ExceptT ServantErr IO
1057+readerToHandler = Nat readerToHandler'
1060+We can write some simple webservice with the handlers running in `Reader String`.
1063+type ReaderAPI = "a" :> Get '[JSON] Int
1064+ :<|> "b" :> Get '[JSON] String
1066+readerAPI :: Proxy ReaderAPI
1069+readerServerT :: ServerT ReaderAPI (Reader String)
1070+readerServerT = a :<|> b
1072+ where a :: Reader String Int
1075+ b :: Reader String String
1079+We unfortunately can't use `readerServerT` as an argument of `serve`, because
1080+`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `ExceptT
1081+ServantErr IO`. But there's a simple solution to this.
1085+That's right. We have just written `readerToHandler`, which is exactly what we
1086+would need to apply to all handlers to make the handlers have the
1087+right type for `serve`. Being cumbersome to do by hand, we provide a function
1088+`enter` which takes a natural transformation between two parametrized types `m`
1089+and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1091+In our case, we can wrap up our little webservice by using `enter
1092+readerToHandler` on our handlers.
1095+readerServer :: Server ReaderAPI
1096+readerServer = enter readerToHandler readerServerT
1099+app4 = serve readerAPI readerServer
1102+This is the webservice in action:
1105+$ curl http://localhost:8081/a
1107+$ curl http://localhost:8081/b
1113+You're now equipped to write webservices/web-applications using
1114+**servant**. The rest of this document focuses on **servant-client**,
1115+**servant-js** and **servant-docs**.