MyNixOS website logo
Description

Typed error wrapper for Servant.

Typed error wrapper using UVerb for Servant

servant-typed-error

This is a small wrapper for the exception approach detailed here: https://docs.servant.dev/en/stable/cookbook/uverb/UVerb.html?highlight=exceptions

This library allows sending and receiving Typed errors via servant. For example, we can define the following API:

data APIError = Whoops | Bad Int
  deriving (Generic, ToJSON, FromJSON)

type API =
  "foo" :> Capture "number" Int :> GetTypedError '[JSON] Bool APIError
    :<|> "whoops" :> GetTypedError '[JSON] Int APIError

Here we use GetTypedError instead of the usual Get to indicate the API can return an ApiError, which must have a ToJSON/FromJSON instance.

There are two ways of writing a server for this API. We can either use the TypedHandler monad, which has throwTypedError and throwServantError functions allowing us to either throw our custom typed error or a generic ServantError. Another approach using a generic mtl style definition of a monad with a MonadError e m constraint; we can then use liftTypedError to lift it into servant's Handler monad:

alwaysWhoops :: MonadError APIError m => m Int
alwaysWhoops = throwError Whoops

server :: Server API
server =
  ( \i ->
      runTypedHandler $ case i of
        42 -> throwTypedError $ Bad i
        -1 -> throwServantError err500
        x -> pure $ x `mod` 2 == 0
  )
    :<|> liftTypedError alwaysWhoops

Finally, we can recover the errors on the client side via a special TypedClientM e a monad. We use the typedClient to convert the servant-client API to use our TypedClientM:

foo :: Int -> TypedClientM APIError Bool
whoops :: TypedClientM APIError Int
foo :<|> whoops = typedClient $ client $ Proxy @API

Then, using runTypedClientM we can obtain an Either (Either ClientError APIError) a and pattern match on both the generic servant-client error or the user defined APIError.

Differences with other servant typed error/exception libs

  • servant-checked-exceptions This library wraps the response in a custom type, so it would be a bit awkward to use with the frontend. The approach here sends the same 200 response as vanilla servant would

  • servant-exceptions Not investigated much since it's missing the client implementation.

Metadata

Version

0.1.2.0

License

Platforms (77)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-windows
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows