servant-checked-exceptions-2.2.0.1: Checked exceptions for Servant APIs.
CopyrightDennis Gosnell 2017
LicenseBSD3
MaintainerDennis Gosnell (cdep.illabout@gmail.com)
Stabilityexperimental
Portabilityunknown
Safe HaskellNone
LanguageHaskell2010

Servant.Checked.Exceptions

Description

This module gives you the ability to specify which errors are thrown by a Servant api. This is done with the Throws data type. Here is an example of creating an api that uses Throws:

  type Api =
    "author" :>
    Capture "author-id" AuthorId :>
    Throws CouldNotConnectToDbError :>
    Throws AuthorNotFoundError :>
    Get '[JSON] Author

This api will return an Author for a given AuthorId. Throws is used to indicate that this api will potentially return two different errors: CouldNotConnectToDbError and AuthorNotFoundError.

These two errors might be defined like this:

  data CouldNotConnectToDbError = CouldNotConnectToDbError
    deriving (Eq, Read, Show)

  data AuthorNotFoundError = AuthorNotFoundError
    deriving (Eq, Read, Show)

Writing the server handler for this api will look like the following. Notice how the Envelope type is used:

  getAuthorHandler
    :: AuthorId
    -> Handler (Envelope '[DatabaseError, AuthorNotFoundError] Author)
  getAuthorHandler authorId = do
    eitherAuthor <- getAuthorFromDb authorId
    case eitherAuthor of
      Left NoDb -> pure $ toErrEnvelope CouldNotConnectToDbError
      Left NoAuthor -> pure $ toErrEnvelope AuthorNotFoundError
      Right author -> pure $ toSuccEnvelope author

  getAuthorFromDb :: AuthorId -> Handler (Either DbErr Author)
  getAuthorFromDb = ...

  data DbErr = NoDb | NoAuthor

Envelope '[DatabaseError, AuthorNotFoundError] Author represents a response that will contain an Author on success, or contain either a DatabaseError or a AuthorNotFoundError on error.

Under the hood, Envelope is using an extensible sum-type (OpenUnion) to represent possible errors. Working with an api that returns two possible errors is just as easy as working with an api that returns three possible errors.

Clients will also use the Envelope type:

  getAuthor
    :: AuthorId
    -> ClientM (Envelope '[DatabaseError, AuthorNotFoundError] Author)
  getAuthor = client (Proxy :: Proxy Api)

It is easy to do case analysis (similar to pattern matching) on the Envelope type with the catchesEnvelope function.

Checkout the example in the repository on Github. It includes a fleshed-out example of an api, server, client, and documentation. The README.md shows how to compile and run the examples.

Documentation