{"id":19338019,"url":"https://github.com/scrive/servant-errors","last_synced_at":"2026-05-16T02:40:50.656Z","repository":{"id":66324047,"uuid":"484346483","full_name":"scrive/servant-errors","owner":"scrive","description":null,"archived":false,"fork":false,"pushed_at":"2022-04-22T07:58:10.000Z","size":31,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"dev-ghc902","last_synced_at":"2025-01-06T10:26:56.118Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scrive.png","metadata":{"files":{"readme":"README.lhs","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-22T07:57:51.000Z","updated_at":"2023-11-24T16:58:26.000Z","dependencies_parsed_at":"2023-03-13T20:29:53.178Z","dependency_job_id":null,"html_url":"https://github.com/scrive/servant-errors","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scrive%2Fservant-errors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scrive%2Fservant-errors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scrive%2Fservant-errors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scrive%2Fservant-errors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scrive","download_url":"https://codeload.github.com/scrive/servant-errors/tar.gz/refs/heads/dev-ghc902","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240441951,"owners_count":19801793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T03:16:05.289Z","updated_at":"2026-05-16T02:40:45.622Z","avatar_url":"https://github.com/scrive.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# servant-errors\n\n[![Hackage](https://img.shields.io/hackage/v/servant-errors.svg?logo=haskell)](https://hackage.haskell.org/package/servant-errors)\n[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Build status](https://img.shields.io/travis/epicallan/servant-errors.svg?logo=travis)](https://travis-ci.org/epicallan/servant-errors)\n\n## Intro\n\nThis package implements a wai-middleware targeting servant-server applications with a goal of make it easier to generate custom server error responses.\n\n### Checkout accompanying blogpost for more details.\n\n* [Unifying Servant server error responses](https://lukwagoallan.com/posts/unifying-servant-server-error-responses)\n\n## Motivation\n\nBy 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.\n\nAt 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 when provided within `ServerError`.\n\nWith `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.\n\n```haskell ignore\n-- | A typical servant application is usually of this form\n\nmain :: IO ()\nmain = run 8001 (serve proxyApi handlers)\n\n-- | With 'errorMw' from servant-errors library as an error processing middleware\nmain :: IO ()\nmain = run 8001\n     $ errorMw @JSON @'[\"error\", \"status\"]\n     -- ^ Structures error response as JSON objects\n     -- with 'error' and 'status' strings as error object field keys\n     -- note they can be changed to any other preferred strings.\n     $ serve proxyApi handlers\n\n-- | The implementation above can also be written as below\n-- if you want to output JSON error responses with 'error'\n-- and 'status' as the JSON Object keys\nmain :: IO ()\nmain = run 8001\n     $ errorMwDefJson\n     -- ^ Default implementation of structuring error responses as JSON\n     -- with no customisation option for JSON error object field keys\n     $ serve proxyApi handlers\n```\n\n\n\n## Complete Usage Example\n\n```haskell\n{-# LANGUAGE DataKinds         #-}\n{-# LANGUAGE DeriveGeneric     #-}\n{-# LANGUAGE TypeFamilies      #-}\n{-# LANGUAGE TypeOperators     #-}\n{-# LANGUAGE TypeApplications #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n\nmodule Main where\n\nimport           Data.Aeson (FromJSON, ToJSON)\nimport           Data.Proxy (Proxy(..))\nimport           Data.Text (Text)\nimport           GHC.Generics (Generic)\nimport           Network.Wai (Application)\nimport           Network.Wai.Handler.Warp (run)\nimport           Network.Wai.Middleware.Servant.Errors (errorMw, HasErrorBody(..))\nimport           Servant (ReqBody, (:\u003e), Post, JSON, Accept(..), serve)\n\n-- | A greet message data type for use as Request Body\nnewtype Greet = Greet { msg :: Text }\n  deriving (Generic, Show, FromJSON, ToJSON)\n\n-- servant application\nmain' :: IO ()\nmain' = run 8001\n  $ errorMw @JSON @'[\"error\", \"status\"]\n  -- ^ @JSON specifies content type encoding of errors\n  -- @[\"error\", \"status\"] specifies error and code text label in resulting JSON error response\n  -- when an empty type level list parameter for 'errorMw' is specified\n  -- the 'HasErrorBody' instance defaults it to '@[\"error\", \"status\"]' for JSON and PlainText instances\n  -- hence; errorMw @JSON @'[] == @JSON @[\"error\", \"status\"]\n  $ serve api handler\n  where\n    handler = return . id\n    api = Proxy @(ReqBody '[JSON] Greet :\u003e Post '[JSON] Greet)\n\n-- | Example Below shows the derivation of an alternative 'HasErrorBody' instance\n-- for JSON error responses if you want to implement more customisation\n----------------------------------------------------------------------------------------\n----------------------------------------------------------------------------------------\n\n-- | We need a newtype like data type to avoid orphan instances, 'Ctyp' satisfy's that\n-- Also note that 'HasErrorBody' instance requires an Accept instance for a content-type\n\ndata Ctyp a\n\n{-\n if you are using GHC 8.6 and above you can make use of deriving Via\n for creating the Accept Instance\n\n \u003e\u003e data Ctyp a\n \u003e\u003e   deriving Accept via JSON\n-}\n\ninstance Accept (Ctyp JSON) where\n  contentType _ = contentType (Proxy @JSON)\n\ninstance HasErrorBody (Ctyp JSON) '[] where\n  encodeError = undefined -- write your custom implementation\n\n-- | Example Middleware with a different 'HasErrorBody' instance for JSON\nerrorMwJson :: Application -\u003e Application\nerrorMwJson =  errorMw @(Ctyp JSON) @'[]\n```\n\nIf a user submits a wrong request body during an HTTP request the HTTP error response is of the formats below;\n\nError response body while using this package's error Middleware .\n_________________________________________\n\n``` JSON\n{\n    \"status\": 400,\n    \"error\": \"Error in $: key \\\"msg\\\" not present\"\n}\n# The response is JSON encoded and contains an HTTP content-type header plus a status code.\n```\n\nDefault error response without middleware;\n_________________________________________\n\n```\n \"Error in $: key \\\"msg\\\" not present\"\n\n# The response is plain text, contains an HTTP status code but lacks an HTTP content-type header.\n```\n\n### Documentation\n\nThis 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 :)\n\n```haskell\nmain :: IO ()\nmain = pure ()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscrive%2Fservant-errors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscrive%2Fservant-errors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscrive%2Fservant-errors/lists"}