@@ -1,7 +1,4 @@
2−title: A web API as a type
1+# A web API as a type
6The source for this tutorial section is a literate haskell file, so first we
3The source for this tutorial section is a literate haskell file, so first we
7need to have some language extensions and imports:
4need to have some language extensions and imports:
@@ -25,8 +22,7 @@ Consider the following informal specification of an API:
26You *should* be able to formalize that. And then use the formalized version to
23You *should* be able to formalize that. And then use the formalized version to
27get you much of the way towards writing a web app. And all the way towards
24get you much of the way towards writing a web app. And all the way towards
28−getting some client libraries, and documentation (and in the future, who knows
25+getting some client libraries, and documentation, and more.
29−- tests, HATEOAS, ...).
31How would we describe it with servant? As mentioned earlier, an endpoint
27How would we describe it with servant? As mentioned earlier, an endpoint
32description is a good old Haskell **type**:
28description is a good old Haskell **type**:
@@ -45,22 +41,22 @@ data User = User {
45Let's break that down:
41Let's break that down:
47- `"users"` says that our endpoint will be accessible under `/users`;
43- `"users"` says that our endpoint will be accessible under `/users`;
48−- `QueryParam "sortby" SortBy`, where `SortBy` is defined by `data SortBy = Age
44+- `QueryParam "sortby" SortBy`, where `SortBy` is defined by `data SortBy = Age | Name`,
49−| Name`, says that the endpoint has a query string parameter named `sortby`
45+ says that the endpoint has a query string parameter named `sortby`
50−whose value will be extracted as a value of type `SortBy`.
46+ whose value will be extracted as a value of type `SortBy`.
51- `Get '[JSON] [User]` says that the endpoint will be accessible through HTTP
47- `Get '[JSON] [User]` says that the endpoint will be accessible through HTTP
52−GET requests, returning a list of users encoded as JSON. You will see
48+ GET requests, returning a list of users encoded as JSON. You will see
53−later how you can make use of this to make your data available under different
49+ later how you can make use of this to make your data available under different
54−formats, the choice being made depending on the [Accept
50+ formats, the choice being made depending on the [Accept
55−header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) specified in
51+ header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) specified in
56−the client's request.
52+ the client's request.
57−- the `:>` operator that separates the various "combinators" just lets you
53+- The `:>` operator that separates the various "combinators" just lets you
58−sequence static path fragments, URL captures and other combinators. The
54+ sequence static path fragments, URL captures and other combinators. The
59−ordering only matters for static path fragments and URL captures. `"users" :>
55+ ordering only matters for static path fragments and URL captures. `"users" :>
60−"list-all" :> Get '[JSON] [User]`, equivalent to `/users/list-all`, is
56+ "list-all" :> Get '[JSON] [User]`, equivalent to `/users/list-all`, is
61−obviously not the same as `"list-all" :> "users" :> Get '[JSON] [User]`, which
57+ obviously not the same as `"list-all" :> "users" :> Get '[JSON] [User]`, which
62−is equivalent to `/list-all/users`. This means that sometimes `:>` is somehow
58+ is equivalent to `/list-all/users`. This means that sometimes `:>` is somehow
63−equivalent to `/`, but sometimes it just lets you chain another combinator.
59+ equivalent to `/`, but sometimes it just lets you chain another combinator.
65We can also describe APIs with multiple endpoints by using the `:<|>`
61We can also describe APIs with multiple endpoints by using the `:<|>`
66combinators. Here's an example:
62combinators. Here's an example:
@@ -74,11 +70,10 @@ type UserAPI2 = "users" :> "list-all" :> Get '[JSON] [User]
74always write your own when you need it. Here's a quick overview of all the
70always write your own when you need it. Here's a quick overview of all the
75combinators that servant comes with.
71combinators that servant comes with.
77−Combinators
73+## Combinators
83As you've already seen, you can use type-level strings (enabled with the
78As you've already seen, you can use type-level strings (enabled with the
84`DataKinds` language extension) for static path fragments. Chaining
79`DataKinds` language extension) for static path fragments. Chaining
@@ -90,8 +85,8 @@ type UserAPI3 = "users" :> "list-all" :> "now" :> Get '[JSON] [User]
90 -- /users/list-all/now
85 -- /users/list-all/now
93−`Delete`, `Get`, `Patch`, `Post` and `Put`
88+### `Delete`, `Get`, `Patch`, `Post` and `Put`
94−------------------------------------------
89+
96These 5 combinators are very similar except that they each describe a
91These 5 combinators are very similar except that they each describe a
97different HTTP method. This is how they're declared
92different HTTP method. This is how they're declared
@@ -112,8 +107,8 @@ type UserAPI4 = "users" :> Get '[JSON] [User]
112 :<|> "admins" :> Get '[JSON] [User]
107 :<|> "admins" :> Get '[JSON] [User]
115−`Capture`
110+### `Capture`
118URL captures are parts of the URL that are variable and whose actual value is
113URL captures are parts of the URL that are variable and whose actual value is
119captured and passed to the request handlers. In many web frameworks, you'll see
114captured and passed to the request handlers. In many web frameworks, you'll see
@@ -147,8 +142,7 @@ type UserAPI5 = "user" :> Capture "userid" Integer :> Get '[JSON] User
147 -- equivalent to 'DELETE /user/:userid'
142 -- equivalent to 'DELETE /user/:userid'
150−`QueryParam`, `QueryParams`, `QueryFlag`, `MatrixParam`, `MatrixParams` and `MatrixFlag`
145+### `QueryParam`, `QueryParams`, `QueryFlag`, `MatrixParam`, `MatrixParams` and `MatrixFlag`
151−----------------------------------------------------------------------------------------
153`QueryParam`, `QueryParams` and `QueryFlag` are about query string
147`QueryParam`, `QueryParams` and `QueryFlag` are about query string
154parameters, i.e., those parameters that come after the question mark
148parameters, i.e., those parameters that come after the question mark
@@ -202,8 +196,7 @@ type UserAPI6 = "users" :> QueryParam "sortby" SortBy :> Get '[JSON] [User]
202Again, your handlers don't have to deserialize these things (into, for example,
196Again, your handlers don't have to deserialize these things (into, for example,
203a `SortBy`). *servant* takes care of it.
197a `SortBy`). *servant* takes care of it.
205−`ReqBody`
199+### `ReqBody`
208Each HTTP request can carry some additional data that the server can use in its
201Each HTTP request can carry some additional data that the server can use in its
209*body*, and this data can be encoded in any format -- as long as the server
202*body*, and this data can be encoded in any format -- as long as the server
@@ -240,8 +233,8 @@ type UserAPI7 = "users" :> ReqBody '[JSON] User :> Post '[JSON] User
240 -- - returns a User encoded in JSON
233 -- - returns a User encoded in JSON
243−Request `Header`s
236+### Request `Header`s
244−-----------------
237+
246Request headers are used for various purposes, from caching to carrying
239Request headers are used for various purposes, from caching to carrying
247auth-related data. They consist of a header name and an associated value. An
240auth-related data. They consist of a header name and an associated value. An
@@ -263,8 +256,7 @@ the client to send the request.
263type UserAPI8 = "users" :> Header "User-Agent" Text :> Get '[JSON] [User]
256type UserAPI8 = "users" :> Header "User-Agent" Text :> Get '[JSON] [User]
266−Content types
259+### Content types
269So far, whenever we have used a combinator that carries a list of content
261So far, whenever we have used a combinator that carries a list of content
270types, we've always specified `'[JSON]`. However, *servant* lets you use several
262types, we've always specified `'[JSON]`. However, *servant* lets you use several
@@ -286,8 +278,7 @@ that everyone uses, we decided to release 2 packages, *servant-lucid* and
286We will further explain how these content types and your data types can play
278We will further explain how these content types and your data types can play
287together in the [section about serving an API](/tutorial/server.html).
279together in the [section about serving an API](/tutorial/server.html).
289−Response `Headers`
281+### Response `Headers`
292Just like an HTTP request, the response generated by a webserver can carry
283Just like an HTTP request, the response generated by a webserver can carry
293headers too. *servant* provides a `Headers` combinator that carries a list of
284headers too. *servant* provides a `Headers` combinator that carries a list of
@@ -305,8 +296,7 @@ response, you could write it as below:
305type UserAPI10 = "users" :> Get '[JSON] (Headers '[Header "User-Count" Integer] [User])
296type UserAPI10 = "users" :> Get '[JSON] (Headers '[Header "User-Count" Integer] [User])
308−Interoperability with other WAI `Application`s: `Raw`
299+### Interoperability with other WAI `Application`s: `Raw`
309−-----------------------------------------------------
311Finally, we also include a combinator named `Raw` that can be used for two reasons:
301Finally, we also include a combinator named `Raw` that can be used for two reasons: