servant-errors: Servant Errors wai-middlware

[ library, mit, network, servant ] [ Propose Tags ]

A Wai middleware that uniformly structures errors with in a servant application. The library assumes all HTTP responses with status code greater than 200 and without an HTTP content type are error responses. This assumption is derived from servant server error handling implementation.

[Skip to Readme]
Versions [faq],,,,,,,,,,, (info)
Change log
Dependencies aeson (==1.3.*), base (==4.11.*), bytestring (>= && <0.11), http-api-data (==0.3.*), http-media (==0.7.*), http-types (==0.12.*), scientific (==0.3.*), servant (==0.14.*), string-conversions (==0.4.*), text (==1.2.*), unordered-containers (==0.2.*), wai (==3.2.*) [details]
License MIT
Copyright 2019 Lukwago Allan
Author Lukwago Allan
Category Network, Servant
Home page
Bug tracker
Source repo head: git clone
Uploaded by epicallan at 2019-09-26T17:04:44Z
Distributions NixOS:
Downloads 1605 total (31 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs uploaded by user [build log]
All reported builds failed as of 2019-09-26 [all 2 reports]


[Index] [Quick Jump]


Maintainer's Corner

For package maintainers and hackage trustees

Readme for servant-errors-

[back to package description]


Hackage MIT license Build status


This package implements a wai-middleware targeting servant-server applications with a goal of make it easier to generate custom server error responses.


By default, when your servant server application experiences an internal exception during endpoint route resolution, e.g. request body decode errors. The server response is just plain text with a status code in the HTTP headers.

At the same time, if you don't write custom code to customise error responses for errors thrown within servant route handlers the default response is plain text with an HTTP content-type if set within ServerError.

With servant-errors library, you get a single interface to structure and encode your error responses in one place as JSON error response or any other preferred form.

-- | A typical servant application is usually of this form

main :: IO ()
main = run 8001 (serve proxyApi handlers)

-- | With servant-errors as error processing middleware
main :: IO ()
main = run 8001
     $ errorMw @JSON @'["error", "status"]
     -- ^ Structures error response as JSON objects
     -- with 'error' and 'status' strings as error object field keys
     -- note they can be changed to any other preferred strings.
     $ serve proxyApi handlers

-- | The implementation above can also be written as below
-- if you want to output JSON error responses with 'error'
-- and 'status' as the JSON Object keys
main :: IO ()
main = run 8001
     $ errorMwDefJson
     -- ^ Default implementation of structuring error responses as JSON
     -- with no customisation option for JSON error object field keys
     $ serve proxyApi handlers

This blog post explains in more details.

Complete Usage Example

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE TypeFamilies      #-}
{-# LANGUAGE TypeOperators     #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DerivingVia #-}

module Main where

import           Data.Aeson (FromJSON, ToJSON)
import           Data.Proxy (Proxy(..))
import           Data.Text (Text)
import           GHC.Generics (Generic)
import           Network.Wai (Application)
import           Network.Wai.Handler.Warp (run)
import           Network.Wai.Middleware.Servant.Errors (errorMw, HasErrorBody(..))
import           Servant (ReqBody, (:>), Post, JSON, Accept(..), serve)

-- | A greet message data type for use as Request Body
newtype Greet = Greet { msg :: Text }
  deriving (Generic, Show, FromJSON, ToJSON)

-- servant application
main' :: IO ()
main' = run 8001
  $ errorMw @JSON @'["error", "status"]
  -- ^ @JSON specifies content type encoding of errors
  -- @["error", "status"] specifies error and code text label in resulting JSON error response
  -- when an empty type level list parameter for 'errorMw' is specified
  -- the 'HasErrorBody' instance defaults it to '@["error", "status"]' for JSON and PlainText instances
  -- hence; errorMw @JSON @'[] == @JSON @["error", "status"]
  $ serve api handler
    handler = return . id
    api = Proxy @(ReqBody '[JSON] Greet :> Post '[JSON] Greet)

-- | Example Below shows the derivation of an alternative 'HasErrorBody' instance
-- for JSON error responses if you want to implement more customisation

-- | We need a newtype like data type to avoid orphan instances, 'Ctyp' satisfy's that
-- We also make use of deriving via to get an Accept instance easily.
-- Note: 'HasErrorBody' instance requires an Accept instance for a content-type

data Ctyp a
  deriving Accept via JSON

instance HasErrorBody (Ctyp JSON) '[] where
  encodeError = undefined -- write your custom implementation

-- | Example Middleware with a different 'HasErrorBody' instance for JSON
errorMwJson :: Application -> Application
errorMwJson =  errorMw @(Ctyp JSON) @'[]

If a user submits a wrong request body during an HTTP request the HTTP error response is of the formats below;

Error response body while using this package's error Middleware .

    "status": 400,
    "error": "Error in $: key \"msg\" not present"
# The response is JSON encoded and contains an HTTP content-type header plus a status code.

Default error response without middleware;

 "Error in $: key \"msg\" not present"

# The response is plain text, contains an HTTP status code but lacks an HTTP content-type header.


This README is tested by markdown-unlit to make sure the code builds. To keep that happy, we do need a main in this file, so ignore the following :)

main :: IO ()
main = pure ()