{"id":20230635,"url":"https://github.com/cdepillabout/servant-checked-exceptions","last_synced_at":"2025-04-03T02:11:51.558Z","repository":{"id":49009503,"uuid":"88938834","full_name":"cdepillabout/servant-checked-exceptions","owner":"cdepillabout","description":"type-level errors for Servant APIs.","archived":false,"fork":false,"pushed_at":"2023-06-27T03:55:57.000Z","size":249,"stargazers_count":71,"open_issues_count":12,"forks_count":14,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-04-26T02:21:26.445Z","etag":null,"topics":["client","docs","errors","exceptions","hacktoberfest","haskell","json","servant","server"],"latest_commit_sha":null,"homepage":"https://hackage.haskell.org/package/servant-checked-exceptions","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cdepillabout.png","metadata":{"files":{"readme":"README.md","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":"2017-04-21T04:05:56.000Z","updated_at":"2024-04-14T17:47:33.000Z","dependencies_parsed_at":"2024-11-14T07:43:21.619Z","dependency_job_id":"33203490-f76e-4ed1-9d58-6194d5094a5f","html_url":"https://github.com/cdepillabout/servant-checked-exceptions","commit_stats":{"total_commits":205,"total_committers":10,"mean_commits":20.5,"dds":0.06341463414634141,"last_synced_commit":"00ebc9a3dae5afd49fe0751d01ba379db7685a04"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdepillabout%2Fservant-checked-exceptions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdepillabout%2Fservant-checked-exceptions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdepillabout%2Fservant-checked-exceptions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdepillabout%2Fservant-checked-exceptions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cdepillabout","download_url":"https://codeload.github.com/cdepillabout/servant-checked-exceptions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246922248,"owners_count":20855345,"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":["client","docs","errors","exceptions","hacktoberfest","haskell","json","servant","server"],"created_at":"2024-11-14T07:43:15.117Z","updated_at":"2025-04-03T02:11:51.532Z","avatar_url":"https://github.com/cdepillabout.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nServant.Checked.Exceptions\n==========================\n\n[![Build Status](https://github.com/cdepillabout/servant-checked-exceptions/workflows/CI/badge.svg)](https://github.com/cdepillabout/servant-checked-exceptions)\n[![Hackage](https://img.shields.io/hackage/v/servant-checked-exceptions.svg)](https://hackage.haskell.org/package/servant-checked-exceptions)\n[![Stackage LTS](http://stackage.org/package/servant-checked-exceptions/badge/lts)](http://stackage.org/lts/package/servant-checked-exceptions)\n[![Stackage Nightly](http://stackage.org/package/servant-checked-exceptions/badge/nightly)](http://stackage.org/nightly/package/servant-checked-exceptions)\n![BSD3 license](https://img.shields.io/badge/license-BSD3-blue.svg)\n\n`servant-checked-exceptions` provides a way to specify errors thrown by a\nServant api on the type level.  It allows easy composition between different\nerror types.\n\n`servant-checked-exceptions` provides the\n[`Throws`](https://hackage.haskell.org/package/servant-checked-exceptions/docs/Servant-Checked-Exceptions.html#t:Throws)\ndata type to signify which errors can be thrown by an api.  For instance,\nimagine a `getAuthor` api that returns an `Author` based on an `AuthorId`:\n\n```haskell\n-- This is a servant-compatible type describing our api.\ntype Api =\n  \"author\" :\u003e\n  Capture \"author-id\" AuthorId :\u003e\n  Throws DatabaseError :\u003e\n  Throws AuthorNotFoundError :\u003e\n  Get '[JSON] Author\n\n-- These are the two errors that can be thrown:\ndata DatabaseError = DatabaseError\ndata AuthorNotFoundError = AuthorNotFoundError\n```\n\nThe corresponding handler function uses the\n[`Envelope`](https://hackage.haskell.org/package/servant-checked-exceptions/docs/Servant-Checked-Exceptions.html#t:Envelope)\ndata type to model the possibility of returning an `Author` successfully, or\neither `DatabaseError` or `AuthorNotFoundError` unsuccessfully.\nInternally, `Envelope` is using an open sum-type to easily represent multiple\ndifferent errors:\n\n```haskell\ngetAuthorHandler\n  :: AuthorId\n  -\u003e Handler (Envelope '[DatabaseError, AuthorNotFoundError] Author)\ngetAuthorHandler authorId = ...\n```\n\nFor more documentation and usage examples, see the\n[documentation](https://hackage.haskell.org/package/servant-checked-exceptions) on Hackage.\n\n## Why would I want to use this?\n\nUsing `Envelope` with its open sum-type to represent errors gives us an easy\nway to reuse errors on multiple routes.\n\nFor instance, imagine that we had another api for updating an author's name,\ngiven the author's ID.  Using `Throws` and `Envelope`, it might look like this:\n\n```haskell\ntype Api =\n  \"update-author-name\" :\u003e\n  Capture \"author-id\" AuthorId :\u003e\n  Capture \"author-name\" AuthorName :\u003e\n  Throws DatabaseError :\u003e\n  Throws AuthorNotFoundError :\u003e\n  Throws AuthorNameTooShort :\u003e\n  Post '[JSON] Author\n\ndata AuthorNameTooShort = AuthorNameTooShort\n\npostChangeAuthorName\n  :: AuthorId\n  -\u003e AuthorName\n  -\u003e Handler (Envelope '[DatabaseError, AuthorNotFoundError, AuthorNameTooShort] Author)\npostChangeAuthorName authorId newAuthorName = ...\n```\n\nWe are able to reuse the `DatabaseError` and `AuthorNotFoundError`.  If we try\nto return an error that is not declared using `Throws`, GHC will give us an\nerror.  We get flexiblity and type-safety.\n\nWhen using [servant-docs](https://hackage.haskell.org/package/servant-docs) to\ncreate documentation, only one instance of `ToSample` needs to be created for\neach error (`DatabaseError`, `AuthorNotFoundError`, and `AuthorNameTooShort`).\nMultiple instances of `ToSample` do not need to be created for _every_\ndifferent `Envelope` used in a handler.\n\n## Example\n\nThis repository contains an [example](servant-checked-exceptions/example/) of using\n`servant-checked-exceptions`.  This includes an [api](servant-checked-exceptions/example/Api.hs),\n[server](servant-checked-exceptions/example/Server.hs), [client](servant-checked-exceptions/example/Client.hs), and\n[documentation](servant-checked-exceptions-core/example/Docs.hs).\n\nBelow I show how to compile and run these examples.\n\n### Compile\n\nThe examples can be compiled by using the `buildexample` flag:\n\n```sh\n$ stack build --flag servant-checked-exceptions-core:buildexample --flag servant-checked-exceptions:buildexample\n```\n\nThis creates three executables.  A server, a client, and a documentaiton\ngenerator.\n\n### Run the server\n\nThe server is a small example that will take search queries and return results.\nThe server can be run with the following command:\n\n```sh\n$ stack exec -- servant-checked-exceptions-example-server\n```\n\nThis runs the server on port 8201.  Here is an example of using `curl` to\naccess the server.  This will send the query `hello`:\n\n```sh\n$ curl \\\n    --request POST \\\n    --header 'Accept: application/json' \\\n    'http://localhost:8201/lax-search/hello'\n{\"data\":\"good\"}\n```\n\nIf you try to send a query that is not `hello`, the server will return an error:\n\n```sh\n$ curl \\\n    --request POST \\\n    --header 'Accept: application/json' \\\n    'http://localhost:8201/lax-search/goodbye'\n{\"err\":\"BadSearchTermErr\"}\n```\n\nThere is also a strict api, that requires `hello` to be capitalized like `Hello`:\n\n```sh\n$ curl \\\n    --request POST \\\n    --header 'Accept: application/json' \\\n    'http://localhost:8201/strict-search/hello'\n{\"err\":\"IncorrectCapitalization\"}\n$ curl \\\n    --request POST \\\n    --header 'Accept: application/json' \\\n    'http://localhost:8201/strict-search/Hello'\n{\"data\":\"good\"}\n```\n\n### Run the client\n\nThe client provides a small command line application to query the server.  In\norder to use the client, the server must be running.\n\nUse the client to access the lax search api:\n\n```sh\n$ stack exec -- servant-checked-exceptions-example-client foobar\nthe search term was not \"Hello\"\n$ stack exec -- servant-checked-exceptions-example-client hello\nSuccess: good\n```\n\nUse the client to access the strict search api:\n\n```sh\n$ stack exec -- servant-checked-exceptions-example-client --strict hello\nthe search term was not capitalized correctly\n$ stack exec -- servant-checked-exceptions-example-client --strict Hello\nSuccess: good\n```\n\n### Run the documentation generator\n\nThe documentation generator will generate documentation for the api in Markdown:\n\n```sh\n$ stack exec -- servant-checked-exceptions-core-example-docs\n```\n\nHere is a small example of the documentation that will be generated for the lax\nsearch api:\n\n```markdown\n## POST /lax-search/:query\n\n#### Captures:\n\n- *query*: a search string like \"hello\" or \"bye\"\n\n#### Response:\n\n- Status code 200\n- Headers: []\n\n- Supported content types are:\n\n    - `application/json`\n\n- This is a successful response.\n\n{\"data\":\"good\"}\n\n- a completely incorrect search term was used\n\n{\"err\":\"BadSearchTermErr\"}\n```\n\nYou can see that both the success and error responses are documented.\n\n## Packaging the core types\n\n[`servant-checked-exceptions-core`](https://hackage.haskell.org/package/servant-checked-exceptions-core)\nexports the core types need for building an API with checked exceptions,\nallowing you to avoid depending on server-side libraries like `warp`, `Glob`\nand `servant-server`. This can be useful if you are writing an API meant to be\nshared with ghcjs and run in a browser, where these dependencies aren't\navailable.\n\n## Limitations\n\nCurrently, `servant-client` only treats HTTP responses as successful if they\nhave a status code of 2XX.  This means that any non-2XX errors thrown by\n`servant-checked-exceptions` don't get parsed into a typed `Envelope` as\nexpected, but raised as a Servant `ClientError`.  For more information, see\n[issue #27](https://github.com/cdepillabout/servant-checked-exceptions/issues/27).\n\n## Maintainers\n\n- [![Maintainer: cdepillabout](https://img.shields.io/badge/maintainer-cdepillabout-lightgrey.svg)](http://github.com/cdepillabout)\n- [![Maintainer: imalsogreg](https://img.shields.io/badge/maintainer-imalsogreg-lightgrey.svg)](http://github.com/imalsogreg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdepillabout%2Fservant-checked-exceptions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcdepillabout%2Fservant-checked-exceptions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdepillabout%2Fservant-checked-exceptions/lists"}