{"id":15705306,"url":"https://github.com/brahmlower/public-private-api-errors-example","last_synced_at":"2026-07-06T20:31:20.838Z","repository":{"id":86081629,"uuid":"152822919","full_name":"brahmlower/public-private-api-errors-example","owner":"brahmlower","description":null,"archived":false,"fork":false,"pushed_at":"2018-10-16T18:04:00.000Z","size":6,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-05T17:12:17.650Z","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":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brahmlower.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":"2018-10-13T01:11:57.000Z","updated_at":"2018-10-16T18:04:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"241c18e6-30d0-4732-9634-dbed587ba417","html_url":"https://github.com/brahmlower/public-private-api-errors-example","commit_stats":{"total_commits":2,"total_committers":1,"mean_commits":2.0,"dds":0.0,"last_synced_commit":"d5a8ea239e477a1c18188f796944d15c3b6c7e78"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahmlower%2Fpublic-private-api-errors-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahmlower%2Fpublic-private-api-errors-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahmlower%2Fpublic-private-api-errors-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahmlower%2Fpublic-private-api-errors-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brahmlower","download_url":"https://codeload.github.com/brahmlower/public-private-api-errors-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246336617,"owners_count":20761084,"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-10-03T20:15:22.657Z","updated_at":"2025-10-26T02:13:06.081Z","avatar_url":"https://github.com/brahmlower.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Public/Private Errors\n\n## Description\n\nThis is an example of an error structure to enforce public and private error\ntyping, which is useful to prevent errors containing sensative information from\nleaking out of an API.\n\n## Representation\n\nThis is a contrived example, but aims to represent a more complicated\narchitecture. Since so much of this code is representative, the following is a\ndetailed description of each module and where you might find it in a larger\napplication.\n\n| Module | Description |\n|--------|-------------|\n| Main.main | this is calling our two \"endpoint\" functions, representing an incoming HTTP request\n| Backend.Api | represents functions for a servant endpoints\n| Backend.Api.throwAppError | \"throws\" a servant error, resulting in a non-success HTTP status code. In the case of this example, it just prints the error to show us what was returned to the \"client\".\n| Backend.Domain | functions handling core business logic. nearly one-to-one with api layer functions, but these have no concept of the API or the existence of the public errors.\n| Backend.Errors | Backend error types. The root error type defines a method of converting its subtypes to a public error (via the instance of `SE.ToAppError`), and all subtypes define translations the corresponding public error type. Notice some sensative backend errors are converted to non-sensative varients, like `UnhandledSQLError Text` is converted to `SE.QueryFailure`.\n| Shared.Errors | This module represents an external common package shared between the backend and the frontend, allowing models, errors and routes to be used in the frontend and backend, providing compiletime garantees that the frontend/backend work together. Errors here are public, and information within them is visible to users and potential bad actors. Sensative information should never be contained or represented in these errors. This includes but is not limited to: user account status (disabled, missing, locked), raw SQL errors, raw shell command errors/output\n\n\n## Running\n\nRun with cabal. It was tested using GHC 7.10.3, but should work with newer\nversions as well (there's nothing particularly new-age in this).\n\n```\n[vagrant@localhost perrors]$ cabal run\nPreprocessing executable 'perrors' for perrors-0.1.0.0..\nBuilding executable 'perrors' for perrors-0.1.0.0..\nRunning perrors...\nErrorAuth BadCredentials\nErrorDb QueryFailure\n```\n\nHere we see the final form of the errors that the application is throwing from\nthe domain logic. What's most important to note is that in the depths of the\ndomain logic, we returned `AccountDisabled` and `FailedConstraint`, but what the\n\"API\" presented to us were safe error messages defined not in the backend, but\nin the shared error module.\n\nBetter yet, we didn't have to expicitly handle this error conversion in the\ndomain logic (it was done on the class interface by the error definition). Plus,\nwe didn't have to spend much energy dealing with converting our `BackendError`\nto the public errors- it's all packaged in a single case statement in the API\nerror handling layer, and constrained to prevent unsafe errors from slipping\nthrough.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrahmlower%2Fpublic-private-api-errors-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrahmlower%2Fpublic-private-api-errors-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrahmlower%2Fpublic-private-api-errors-example/lists"}