Tidy up tutorial
This commit is contained in:
parent
a3c5f17749
commit
aa3716b6aa
5 changed files with 35 additions and 15 deletions
|
@ -328,14 +328,25 @@ type ProtectedAPI11
|
||||||
|
|
||||||
### Empty APIs
|
### Empty APIs
|
||||||
|
|
||||||
TODO motivation...
|
Sometimes it is useful to be able to generalise an API over the type of some
|
||||||
|
part of it:
|
||||||
|
|
||||||
``` haskell ignore
|
``` haskell
|
||||||
type UserAPI12
|
type UserAPI12 innerAPI
|
||||||
= UserAPI
|
= UserAPI -- this is the fixed bit of the API
|
||||||
:<|> EmptyAPI -- this adds nothing to the API
|
:<|> "inner" :> innerAPI -- this lets us put various other APIs under /inner
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If there is a case where you do not have anything extra to serve, you can use
|
||||||
|
the `EmptyAPI` combinator to indicate this:
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
type UserAPI12Alone = UserAPI12 EmptyAPI
|
||||||
|
```
|
||||||
|
|
||||||
|
This also works well as a placeholder for unfinished parts of an API while it
|
||||||
|
is under development.
|
||||||
|
|
||||||
### Interoperability with `wai`: `Raw`
|
### Interoperability with `wai`: `Raw`
|
||||||
|
|
||||||
Finally, we also include a combinator named `Raw` that provides an escape hatch
|
Finally, we also include a combinator named `Raw` that provides an escape hatch
|
||||||
|
|
|
@ -62,7 +62,6 @@ Enough chitchat, let's see an example. Consider the following API type from the
|
||||||
type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
|
type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
|
||||||
:<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
|
:<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
|
||||||
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
|
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
|
||||||
:<|> EmptyAPI
|
|
||||||
```
|
```
|
||||||
|
|
||||||
What we are going to get with **servant-client** here is three functions, one to query each endpoint:
|
What we are going to get with **servant-client** here is three functions, one to query each endpoint:
|
||||||
|
@ -89,11 +88,23 @@ the function `client`. It takes one argument:
|
||||||
api :: Proxy API
|
api :: Proxy API
|
||||||
api = Proxy
|
api = Proxy
|
||||||
|
|
||||||
position :<|> hello :<|> marketing :<|> EmptyClient = client api
|
position :<|> hello :<|> marketing = client api
|
||||||
```
|
```
|
||||||
|
|
||||||
`client api` returns client functions for our _entire_ API, combined with `:<|>`, which we can pattern match on as above. You could say `client` "calculates" the correct type and number of client functions for the API type it is given (via a `Proxy`), as well as their implementations.
|
`client api` returns client functions for our _entire_ API, combined with `:<|>`, which we can pattern match on as above. You could say `client` "calculates" the correct type and number of client functions for the API type it is given (via a `Proxy`), as well as their implementations.
|
||||||
|
|
||||||
|
If there is an `EmptyAPI` within your API, this matches the `EmptyClient`
|
||||||
|
constructor:
|
||||||
|
|
||||||
|
``` haskell ignore
|
||||||
|
type API' = API :<|> EmptyAPI
|
||||||
|
|
||||||
|
api' :: Proxy API'
|
||||||
|
api' = Proxy
|
||||||
|
|
||||||
|
(position' :<|> hello' :<|> marketing') :<|> EmptyClient = client api'
|
||||||
|
```
|
||||||
|
|
||||||
``` haskell ignore
|
``` haskell ignore
|
||||||
-- | URI scheme to use
|
-- | URI scheme to use
|
||||||
data Scheme =
|
data Scheme =
|
||||||
|
|
|
@ -38,10 +38,9 @@ Like client function generation, documentation generation amounts to inspecting
|
||||||
This time however, we have to assist **servant**. While it is able to deduce a lot of things about our API, it can't magically come up with descriptions of the various pieces of our APIs that are human-friendly and explain what's going on "at the business-logic level". A good example to study for documentation generation is our webservice with the `/position`, `/hello` and `/marketing` endpoints from earlier:
|
This time however, we have to assist **servant**. While it is able to deduce a lot of things about our API, it can't magically come up with descriptions of the various pieces of our APIs that are human-friendly and explain what's going on "at the business-logic level". A good example to study for documentation generation is our webservice with the `/position`, `/hello` and `/marketing` endpoints from earlier:
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
type ExampleAPI = ("position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
|
type ExampleAPI = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
|
||||||
:<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
|
:<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
|
||||||
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email)
|
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
|
||||||
:<|> EmptyAPI
|
|
||||||
|
|
||||||
exampleAPI :: Proxy ExampleAPI
|
exampleAPI :: Proxy ExampleAPI
|
||||||
exampleAPI = Proxy
|
exampleAPI = Proxy
|
||||||
|
@ -221,7 +220,7 @@ api :: Proxy DocsAPI
|
||||||
api = Proxy
|
api = Proxy
|
||||||
|
|
||||||
server :: Server DocsAPI
|
server :: Server DocsAPI
|
||||||
server = (Server.server3 :<|> emptyServer) :<|> Tagged serveDocs where
|
server = Server.server3 :<|> Tagged serveDocs where
|
||||||
serveDocs _ respond =
|
serveDocs _ respond =
|
||||||
respond $ responseLBS ok200 [plain] docsBS
|
respond $ responseLBS ok200 [plain] docsBS
|
||||||
plain = ("Content-Type", "text/plain")
|
plain = ("Content-Type", "text/plain")
|
||||||
|
|
|
@ -45,7 +45,6 @@ Now let's have the API type(s) and the accompanying datatypes.
|
||||||
``` haskell
|
``` haskell
|
||||||
type API = "point" :> Get '[JSON] Point
|
type API = "point" :> Get '[JSON] Point
|
||||||
:<|> "books" :> QueryParam "q" Text :> Get '[JSON] (Search Book)
|
:<|> "books" :> QueryParam "q" Text :> Get '[JSON] (Search Book)
|
||||||
:<|> EmptyAPI
|
|
||||||
|
|
||||||
type API' = API :<|> Raw
|
type API' = API :<|> Raw
|
||||||
|
|
||||||
|
@ -134,7 +133,6 @@ api' = Proxy
|
||||||
server :: Server API
|
server :: Server API
|
||||||
server = randomPoint
|
server = randomPoint
|
||||||
:<|> searchBook
|
:<|> searchBook
|
||||||
:<|> emptyServer
|
|
||||||
|
|
||||||
server' :: Server API'
|
server' :: Server API'
|
||||||
server' = server
|
server' = server
|
||||||
|
|
|
@ -1020,10 +1020,11 @@ serverFor = error "..."
|
||||||
-- or the mailing list if you get stuck!
|
-- or the mailing list if you get stuck!
|
||||||
```
|
```
|
||||||
|
|
||||||
TODO prose
|
If the API contains the `EmptyAPI` combinator, the corresponding server is
|
||||||
|
called `emptyServer`:
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
type CombinedAPI2 = API :<|> EmptyAPI
|
type CombinedAPI2 = API :<|> "empty" :> EmptyAPI
|
||||||
|
|
||||||
server11 :: Server CombinedAPI2
|
server11 :: Server CombinedAPI2
|
||||||
server11 = server3 :<|> emptyServer
|
server11 = server3 :<|> emptyServer
|
||||||
|
|
Loading…
Add table
Reference in a new issue