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 execution -
ServantErr
values need to be created at the call site ofthrowError
, where the requested content type and/or headers are not available
servant-exception
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 catch exceptions of type e
in the 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
TODO
This package is still a work-in-progress and lacks at least
- Documentation, more examples
-
servant-client
support -
servant-docs
support
Credit
This package is inspired by servant-checked-exceptions
(Throws combinator) and
the generalized error handling in cardano-sl
.