Extensible exceptions for servant APIs.
`servant-exceptions` provides a Throw
combinator to declare what types of errors an API might throw. The server implementation catches them and allows for a canonical encoding using servant content-type machinery.
servant-exceptions
Servant servers typically run their handlers in some form of IO
. Either directly in the builtin Handler
monad or a custom monad transformer on top it. When APIs fail, one would typically use the MonadError ServantError
instance via throwError
to create an error response of type ServantErr
.
This approach has two problems:
Handler
(basically beingExceptT ServantErr IO
) is considered an anti-pattern by some, as it suggests to novice users that onlyServantErr
would occur, but inIO
any exception can be raised to abort executionServantErr
values need to be created at the call site ofthrowError
, where the requested content type and/or headers are not available
servant-exceptions
tries to help with both by making it easy to catch specific error types with an instance of Exception
and provide automatic encoding into the requested content-type.
The API combinator Throws e
can be used to annotate what error types e
might be thrown by a server, for example:
type API = "api" :> Throws UsersError :> "users" :> Get '[JSON, PlainText] [User]
The type UsersError
can then be used to describe expected errors and their conversion via type class instances:
data UsersError = UserNotFound
| UserAlreadyExists
| InternalError
deriving (Show)
instance Exception UsersError
instance ToServantErr UsersError where
status UserNotFound = status404
status UserAlreadyExists = status409
status InternalError = status500
instance ToJSON UsersError where
toJSON e = object [ "type" .= show (typeOf e)
, "message" .= message e
]
instance MimeRender PlainText UsersError where
mimeRender ct = mimeRender ct . show
See example for a full, commented example.
Packages
Features
- Declarative conversion of errors into error responses using
ToServantErr
- Respects
Accept
headers and constructs responses accordingly usingMimeRender
- Add headers to error responses, also via
ToServantErr
- Type-driven exception handling in
ServerT
stacks - Convert "backend" errors into "api" errors using
mapException
Planned things
This package lacks at least
servant-client
to rethrow exceptions (usingMonadThrow
and/orMonadError
?)servant-docs
support for automatic error documentation- Documentation, more examples (explain included
ServantException
helper type)
Credit
This package is inspired by servant-checked-exceptions
(Throws combinator) and the generalized error handling in cardano-sl
.