@@ -3,29 +3,10 @@
3Enough chit-chat about type-level combinators and representing an API as a
3Enough chit-chat about type-level combinators and representing an API as a
4type. Can we have a webservice already?
4type. Can we have a webservice already?
6−If you want to follow along with the code and run the examples while you read this guide:
9−cabal get servant-examples
10−cd servant-examples-<VERSION>
12−cabal install --dependencies-only
13−cabal configure && cabal build
16−This will produce a `tutorial` executable in the
17−`dist/build/tutorial` directory that just runs the example corresponding
18−to the number specified as a command line argument:
21−$ dist/build/tutorial/tutorial
23− where N is the number of the example you want to run.
26## A first example
6## A first example
28−Equipped with some basic knowledge about the way we represent API, let's now write our first webservice.
8+Equipped with some basic knowledge about the way we represent API, let's now
9+write our first webservice.
30The source for this tutorial section is a literate haskell file, so first we
11The source for this tutorial section is a literate haskell file, so first we
31need to have some language extensions and imports:
12need to have some language extensions and imports:
@@ -72,7 +53,13 @@ import qualified Text.Blaze.Html
72{-# LANGUAGE TypeFamilies #-}
53{-# LANGUAGE TypeFamilies #-}
75−**Important**: the `Servant` module comes from the *servant-server* package, the one that lets us run webservers that implement a particular API type. It reexports all the types from the *servant* package that let you declare API types as well as everything you need to turn your request handlers into a fully-fledged webserver. This means that in your applications, you can just add *servant-server* as a dependency, import `Servant` and not worry about anything else.
56+**Important**: the `Servant` module comes from the *servant-server* package,
57+the one that lets us run webservers that implement a particular API type. It
58+reexports all the types from the *servant* package that let you declare API
59+types as well as everything you need to turn your request handlers into a
60+fully-fledged webserver. This means that in your applications, you can just add
61+*servant-server* as a dependency, import `Servant` and not worry about anything
77We will write a server that will serve the following API.
64We will write a server that will serve the following API.
@@ -95,7 +82,7 @@ data User = User
95 { name :: String
82 { name :: String
96 , age :: Int
83 , age :: Int
97 , email :: String
84 , email :: String
98− , registrationDate :: Day
85+ , registration_date :: Day
99 } deriving (Eq, Show, Generic)
86 } deriving (Eq, Show, Generic)
101instance ToJSON User
88instance ToJSON User
@@ -140,7 +127,9 @@ server1 :: Server UserAPI1
140server1 = return users1
127server1 = return users1
143−That's it. Now we can turn `server` into an actual webserver using [wai](http://hackage.haskell.org/package/wai) and [warp](http://hackage.haskell.org/package/warp):
130+That's it. Now we can turn `server` into an actual webserver using
131+[wai](http://hackage.haskell.org/package/wai) and
132+[warp](http://hackage.haskell.org/package/warp):
145``` haskell
134``` haskell
146userAPI :: Proxy UserAPI1
135userAPI :: Proxy UserAPI1
@@ -179,7 +168,8 @@ $ curl http://localhost:8081/users
180## More endpoints
169## More endpoints
182−What if we want more than one endpoint? Let's add `/albert` and `/isaac` to view the corresponding users encoded in JSON.
171+What if we want more than one endpoint? Let's add `/albert` and `/isaac` to
172+view the corresponding users encoded in JSON.
184``` haskell
174``` haskell
185type UserAPI2 = "users" :> Get '[JSON] [User]
175type UserAPI2 = "users" :> Get '[JSON] [User]
@@ -225,7 +215,8 @@ argument of the appropriate type automatically. You don't have to worry about
225manually looking up URL captures or query string parameters, or
215manually looking up URL captures or query string parameters, or
226decoding/encoding data from/to JSON. Never.
216decoding/encoding data from/to JSON. Never.
228−We are going to use the following data types and functions to implement a server for `API`.
218+We are going to use the following data types and functions to implement a
230``` haskell
221``` haskell
231type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
222type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
@@ -307,7 +298,8 @@ parameter might not always be there);
308 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
299 - a `ReqBody contentTypeList a` becomes an argument of type `a`;
310−And that's it. You can see this example in action by running `dist/build/tutorial/tutorial 3`.
301+And that's it. You can see this example in action by running
302+`dist/build/tutorial/tutorial 3`.
313$ curl http://localhost:8081/position/1/2
305$ curl http://localhost:8081/position/1/2
@@ -474,8 +466,8 @@ And this is all the code that lets you use `JSON` for with `ReqBody`, `Get`,
474`Post` and friends. We can check our understanding by implementing support
466`Post` and friends. We can check our understanding by implementing support
475for an `HTML` content type, so that users of your webservice can access an
467for an `HTML` content type, so that users of your webservice can access an
476HTML representation of the data they want, ready to be included in any HTML
468HTML representation of the data they want, ready to be included in any HTML
477−document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/), simply by adding `Accept:
469+document, e.g. using [jQuery's `load` function](https://api.jquery.com/load/),
478−text/html` to their request headers.
470+simply by adding `Accept: text/html` to their request headers.
480### Case-studies: *servant-blaze* and *servant-lucid*
472### Case-studies: *servant-blaze* and *servant-lucid*
@@ -766,7 +758,8 @@ query it, first without the file and then with the file.
767## Response headers
759## Response headers
761+To add headers to your response, use
769−To add headers to your response, use [addHeader](http://hackage.haskell.org/package/servant-0.4.4/docs/Servant-API-ResponseHeaders.html).
762+[addHeader](http://hackage.haskell.org/package/servant/docs/Servant-API-ResponseHeaders.html).
770Note that this changes the type of your API, as we can see in the following example:
763Note that this changes the type of your API, as we can see in the following example:
772``` haskell
765``` haskell
@@ -824,9 +817,12 @@ for a file at the path described by the rest of the request path, inside the
825In other words:
818In other words:
827−- If a client requests `/code/foo.txt`, the server will look for a file at `./tutorial/foo.txt` (and fail)
820+- If a client requests `/code/foo.txt`, the server will look for a file at
821+ `./tutorial/foo.txt` (and fail)
828−- If a client requests `/code/T1.hs`, the server will look for a file at `./tutorial/T1.hs` (and succeed)
822+- If a client requests `/code/T1.hs`, the server will look for a file at
823+ `./tutorial/T1.hs` (and succeed)
829−- If a client requests `/code/foo/bar/baz/movie.mp4`, the server will look for a file at `./tutorial/foo/bar/baz/movie.mp4` (and fail)
824+- If a client requests `/code/foo/bar/baz/movie.mp4`, the server will look for
825+ a file at `./tutorial/foo/bar/baz/movie.mp4` (and fail)
831Here is our little server in action.
827Here is our little server in action.
@@ -921,7 +917,8 @@ not found
922## Nested APIs
918## Nested APIs
924−Let's see how you can define APIs in a modular way, while avoiding repetition. Consider this simple example:
920+Let's see how you can define APIs in a modular way, while avoiding repetition.
921+Consider this simple example:
926``` haskell
923``` haskell
927type UserAPI3 = -- view the user with given userid, in JSON
924type UserAPI3 = -- view the user with given userid, in JSON
@@ -940,7 +937,8 @@ type UserAPI4 = Capture "userid" Int :>
943−However, you have to be aware that this has an effect on the type of the corresponding `Server`:
940+However, you have to be aware that this has an effect on the type of the
941+corresponding `Server`:
945``` haskell ignore
943``` haskell ignore
946Server UserAPI3 = (Int -> ExceptT ServantErr IO User)
944Server UserAPI3 = (Int -> ExceptT ServantErr IO User)
@@ -952,7 +950,8 @@ Server UserAPI4 = Int -> ( ExceptT ServantErr IO User
954In the first case, each handler receives the *userid* argument. In the latter,
952In the first case, each handler receives the *userid* argument. In the latter,
955−the whole `Server` takes the *userid* and has handlers that are just computations in `ExceptT`, with no arguments. In other words:
953+the whole `Server` takes the *userid* and has handlers that are just
954+computations in `ExceptT`, with no arguments. In other words:
957``` haskell
956``` haskell
958server8 :: Server UserAPI3
957server8 :: Server UserAPI3
@@ -977,7 +976,10 @@ server9 userid = getUser userid :<|> deleteUser userid
977 deleteUser = error "..."
976 deleteUser = error "..."
980−Note that there's nothing special about `Capture` that lets you "factor it out": this can be done with any combinator. Here are a few examples of APIs with a combinator factored out for which we can write a perfectly valid `Server`.
979+Note that there's nothing special about `Capture` that lets you "factor it
980+out": this can be done with any combinator. Here are a few examples of APIs
981+with a combinator factored out for which we can write a perfectly valid
982``` haskell
984``` haskell
983-- we just factor out the "users" path fragment
985-- we just factor out the "users" path fragment
@@ -1002,7 +1004,8 @@ newtype Token = Token ByteString
1002newtype SecretData = SecretData ByteString
1004newtype SecretData = SecretData ByteString
1005−This approach lets you define APIs modularly and assemble them all into one big API type only at the end.
1007+This approach lets you define APIs modularly and assemble them all into one big
1008+API type only at the end.
1007``` haskell
1010``` haskell
1008type UsersAPI =
1011type UsersAPI =
@@ -1080,7 +1083,8 @@ server10 :: Server CombinedAPI
1080server10 = usersServer :<|> productsServer
1083server10 = usersServer :<|> productsServer
1083−Finally, we can realize the user and product APIs are quite similar and abstract that away:
1086+Finally, we can realize the user and product APIs are quite similar and
1085``` haskell
1089``` haskell
1086-- API for values of type 'a'
1090-- API for values of type 'a'
@@ -1109,15 +1113,25 @@ serverFor = error "..."
1110## Using another monad for your handlers
1114## Using another monad for your handlers
1112−Remember how `Server` turns combinators for HTTP methods into `ExceptT ServantErr IO`? Well, actually, there's more to that. `Server` is actually a simple type synonym.
1116+Remember how `Server` turns combinators for HTTP methods into `ExceptT
1117+ServantErr IO`? Well, actually, there's more to that. `Server` is actually a
1118+simple type synonym.
1114``` haskell ignore
1120``` haskell ignore
1115type Server api = ServerT api (ExceptT ServantErr IO)
1121type Server api = ServerT api (ExceptT ServantErr IO)
1118−`ServerT` is the actual type family that computes the required types for the handlers that's part of the `HasServer` class. It's like `Server` except that it takes a third parameter which is the monad you want your handlers to run in, or more generally the return types of your handlers. This third parameter is used for specifying the return type of the handler for an endpoint, e.g when computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be `SomeMonad Person`.
1124+`ServerT` is the actual type family that computes the required types for the
1125+handlers that's part of the `HasServer` class. It's like `Server` except that
1126+it takes a third parameter which is the monad you want your handlers to run in,
1127+or more generally the return types of your handlers. This third parameter is
1128+used for specifying the return type of the handler for an endpoint, e.g when
1129+computing `ServerT (Get '[JSON] Person) SomeMonad`. The result would be
1120−The first and main question one might have then is: how do we write handlers that run in another monad? How can we "bring back" the value from a given monad into something *servant* can understand?
1132+The first and main question one might have then is: how do we write handlers
1133+that run in another monad? How can we "bring back" the value from a given monad
1134+into something *servant* can understand?
1122### Natural transformations
1136### Natural transformations
@@ -1185,7 +1199,8 @@ right type for `serve`. Being cumbersome to do by hand, we provide a function
1185`enter` which takes a natural transformation between two parametrized types `m`
1199`enter` which takes a natural transformation between two parametrized types `m`
1186and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1200and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1188−In our case, we can wrap up our little webservice by using `enter readerToEither` on our handlers.
1202+In our case, we can wrap up our little webservice by using `enter
1203+readerToEither` on our handlers.
1190``` haskell
1205``` haskell
1191readerServer :: Server ReaderAPI
1206readerServer :: Server ReaderAPI
@@ -1195,7 +1210,8 @@ app4 :: Application
1195app4 = serve readerAPI EmptyConfig readerServer
1210app4 = serve readerAPI EmptyConfig readerServer
1198−And we can indeed see this webservice in action by running `dist/build/tutorial/tutorial 7`.
1213+And we can indeed see this webservice in action by running
1214+`dist/build/tutorial/tutorial 7`.
1201$ curl http://localhost:8081/a
1217$ curl http://localhost:8081/a
@@ -1206,9 +1222,7 @@ $ curl http://localhost:8081/b
1207## Conclusion
1223## Conclusion
1209−You're now equipped to write any kind of webservice/web-application using *servant*. One thing not covered here is how to incorporate your own combinators and will be the topic of a page on the website. The rest of this document focuses on *servant-client*, *servant-jquery* and *servant-docs*.
1225+You're now equipped to write any kind of webservice/web-application using
1210−
1226+*servant*. One thing not covered here is how to incorporate your own
1211−<div style="text-align: center;">
1227+combinators and will be the topic of a page on the website. The rest of this
1212− <p><a href="/tutorial/api-type.html">Previous page: A web API as a type</a></p>
1228+document focuses on *servant-client*, *servant-jquery* and *servant-docs*.
1213− <p><a href="/tutorial/client.html">Next page: Deriving Haskell functions to query an API</a></p>