haskell-servant / haskell-servant/servant

Deriving an API server by using a "HasHandler" class for each API endpoint

Open
#619 5 comments 0 reactions 0 assignees View on GitHub
Dominant language
Haskell
Stars
2k
Forks
427
Avg merge
2d 23h
Merged PRs (30d)
5

Description

If we have an API like this:

``` haskell
type GetUser = "users" :> Get '[JSON] [User]
type GetAlbert = "albert" :> Get '[JSON] User
type GetIsaac = "isaac" :> Get '[JSON] User

type UserAPI2 = GetUser :<|> GetAlbert :<|> GetIsaac
```

we currently define a server for the API by referencing the position of the endpoint within the API type:

``` haskell
server2 :: Server UserAPI2
server2 = return users2
:<|> return albert
:<|> return isaac
```

How about using a `HasHandler` class instead, for each endpoint within the API, like this:

``` haskell
class HasHandler endpoint where
handle :: Server endpoint

instance HasHandler GetUser where
handle = return users2

instance HasHandler GetAlbert where
handle = return albert

instance HasHandler GetIsaac where
handle = return isaac
```

Now, `server2` can be automatically derived by servant, and if an API endpoint doesn't have a `HasHandler` instance, the user sees a nice error like "no instance for HasHandler ("albert" :> Get '[JSON] User)" rather than the often unintelligible output of eg. plugging in handlers in the wrong order, or missing a handler, when using the `:<|>` combinator.

Is there something I'm missing here, or would this be a superior way to define servers for an API?

I think we could also use a similar class for the client functions, to make it easy get the appropriate client functions, such that we here also reference endpoints within an API, rather than the position of the endpoints within the API. But I'm not sure exactly how this should be structured.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.