{"id":28955820,"url":"https://github.com/kodeart/go-problem","last_synced_at":"2025-06-23T20:10:13.952Z","repository":{"id":252205064,"uuid":"839737219","full_name":"kodeart/go-problem","owner":"kodeart","description":"Problem details for HTTP APIs per RFC-9457 standard","archived":false,"fork":false,"pushed_at":"2025-03-25T10:34:42.000Z","size":43,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T11:32:07.641Z","etag":null,"topics":["api-problem","error","go","golang","problem","problem-details","rfc-7807","rfc-9457"],"latest_commit_sha":null,"homepage":"","language":"Go","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/kodeart.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2024-08-08T08:21:06.000Z","updated_at":"2025-03-25T10:33:19.000Z","dependencies_parsed_at":"2025-03-25T11:36:34.904Z","dependency_job_id":null,"html_url":"https://github.com/kodeart/go-problem","commit_stats":null,"previous_names":["kodeart/go-problem"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/kodeart/go-problem","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodeart%2Fgo-problem","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodeart%2Fgo-problem/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodeart%2Fgo-problem/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodeart%2Fgo-problem/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kodeart","download_url":"https://codeload.github.com/kodeart/go-problem/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodeart%2Fgo-problem/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261548741,"owners_count":23175499,"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":["api-problem","error","go","golang","problem","problem-details","rfc-7807","rfc-9457"],"created_at":"2025-06-23T20:10:13.257Z","updated_at":"2025-06-23T20:10:13.927Z","avatar_url":"https://github.com/kodeart.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Problem Details\n\n[![Codecov](https://codecov.io/gh/kodeart/go-problem/branch/master/graph/badge.svg)](https://codecov.io/gh/kodeart/go-problem)\n[![Go Report Card](https://goreportcard.com/badge/github.com/kodeart/go-problem)](https://goreportcard.com/report/github.com/kodeart/go-problem)\n[![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/kodeart/go-problem/blob/master/LICENSE)\n\n## Problem details for HTTP APIs per [RFC-9457][RFC9457] standard.\n\nThis module provides a `Problem` struct which can be used to represent a problem\nin HTTP APIs. It implements the [RFC-9457][RFC9457] standard that creates error responses.\n\n### The benefits of using `go-problem`\n\n- RFC 9457 and 7807 compliant error responses\n- Consistent error format across the API\n- Built-in support for HTTP status codes\n- Extensible error details\n- Clean and readable error handling\n- Built-in JSON and XML marshaling\n\n## Installation\n\n```bash\ngo get github.com/kodeart/go-problem/v2\n```\n\nUpdate\n```\ngo get -u github.com/kodeart/go-problem/v2\ngo mod tidy\n```\n\n### What is it again?\n\nIt's for creating a standardized error responses, like this JSON for example:\n\n```json\n{\n  \"status\": 403,\n  \"type\": \"https://example.com/probs/out-of-credit\",\n  \"title\": \"You do not have enough credit.\",\n  \"detail\": \"Your current balance is 30, but that costs 50.\",\n  \"instance\": \"/account/12345/msgs/abc\",\n  \"balance\": 30,\n  \"accounts\": [\"/account/12345\", \"/account/67890\"]\n}\n```\nor\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cproblem xmlns=\"urn:ietf:rfc:7807\"\u003e\n  \u003ctype\u003ehttps://example.com/probs/out-of-credit\u003c/type\u003e\n  \u003ctitle\u003eYou do not have enough credit.\u003c/title\u003e\n  \u003cdetail\u003eYour current balance is 30, but that costs 50.\u003c/detail\u003e\n  \u003cinstance\u003ehttps://example.net/account/12345/msgs/abc\u003c/instance\u003e\n  \u003cbalance\u003e30\u003c/balance\u003e\n  \u003caccounts\u003e\n    \u003ci\u003ehttps://example.net/account/12345\u003c/i\u003e\n    \u003ci\u003ehttps://example.net/account/67890\u003c/i\u003e\n  \u003c/accounts\u003e\n\u003c/problem\u003e\n```\n\nor even this, **which defeats the whole purpose of the RFC**, but it may be useful for your current/legacy code:\n\n```json\n{\n  \"message\": \"Failed to use the problem details\",\n  \"code\": 500\n}\n```\n\n```xml\n\u003cproblem\u003e\n    \u003cmessage\u003eFailed to use the problem details\u003c/message\u003e\n    \u003ccode\u003e500\u003c/code\u003e\n\u003c/problem\u003e\n```\n\n\n## Usage\n\n`go-problem` module provides an easy way to send the `Problem` struct as a response to the client.\n\n### Example with HTTP handler and middleware\n\n```go\npackage middleware\n\nimport (\n    \"net/http\"\n\n    \"github.com/kodeart/go-problem/v2\"\n)\n\nfunc NotFoundHandler() http.HandlerFunc {\n    return func(w http.ResponseWriter, r *http.Request) {\n        p := problem.Problem{\n            Status:   http.StatusNotFound,\n            Detail:   \"No such API route\",\n            Title:    \"Route Not Found\",\n            Instance: r.URL.Path,\n        }\n        p.JSON(w)\n    }\n}\n\n// or with helper methods\n\nfunc NotFoundHandler(w http.ResponseWriter, r *http.Request) {\n    problem.New().\n        WithStatus(http.StatusNotFound).\n        WithDetail(\"No such API route\").\n        WithTitle(\"Route Not Found\").\n        WithInstance(r.URL.Path).\n        JSON(w)\n}\n```\n\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/go-chi/chi/v5\"\n\t\"github.com/kodeart/go-problem/v2\"\n)\n\nfunc main() {\n    mux := chi.NewRouter()\n    mux.NotFound(middleware.NotFoundHandler)\n\n    // ...\n\n    mux.Get(\"/\", func(w http.ResponseWriter, r *http.Request) {\n        problem.New().\n            WithStatus(http.StatusServiceUnavailable).\n            WithExtension(\"maintenance\", true).\n            WithExtension(\"version\", \"1.0.0\").\n            JSON(w)\n    })\n}\n```\n\n### Helpers\n\nAny key-value pair outide the standard fields can be accessed with\n\n```go\np := problem.New().WithExtension(\"key\", \"value\")\n\nv := p.GetExtension(\"key\")\n\n// if you know the type of the value, assert it\nintVal := p.GetExtension(\"key name\").(int)\n```\nIf there is no such element, `nil` is returned.\n\n### Create a `Problem` with helpers\n\n```go\np := problem.New().\n    WithStatus(http.StatusUnprocessableEntity).\n    WithType(\"https://example.com/probs/out-of-credit\").\n    WithTitle(\"You do not have enough credit.\").\n    WithDetail(\"Your current balance is 30, but that costs 50.\").\n    WithInstance(\"/account/12345/msgs/abc\").\n    WithExtension(\"balance\", 30).\n    WithExtension(\"accounts\", []string{\n        \"/account/12345\",\n        \"/account/67890\",\n    })\n}\n```\n\n\n### Create a `Problem` directly\n\n```go\np := problem.Problem{\n    Status:   http.StatusUnprocessableEntity,\n    Type:     \"https://example.com/probs/out-of-credit\",\n    Title:    \"You do not have enough credit.\",\n    Detail:   \"Your current balance is 30, but that costs 50.\",\n    Instance: \"/account/12345/msgs/abc\",\n    Extensions: map[string]any{\n        \"balance\": 30,\n        \"accounts\": []string{\n            \"/account/12345\",\n            \"/account/67890\",\n\t\t},\n\t},\n}\n```\n\n## Rendering\n\n`Problem` supports serializing and deserializing the data to and from JSON and XML.\n\n### JSON\n\nThe `JSON()` method will render the `Problem` struct to JSON string,\n\n```go\np.JSON(w)\n```\n\nwhile the standard `json.Unmarshal()` method will try to parse a JSON string into a `Problem` struct:\n\n```go\nvar p problem.Problem\njsonString := `{...}` // some error message\n\nerr := json.Unmarshal([]byte(jsonString), \u0026p)\n```\n\n### XML\n\nThe `XML()` method will render the `Problem` struct to XML string,\n\n```go\np.XML(w)\n```\n\n```go\nvar p problem.Problem\nxmlString := `\u003cproblem\u003e...\u003c/problem\u003e`\n\nerr := xml.Unmarshal([]byte(xmlString), \u0026p)\n```\n\n\u003e When unmarshalling the XML into `Problem` struct,\n\u003e the unmarshaller will try it's best to create the extensions.\n\u003e Expect all values to be of a `string` type.\n\n### Content-Type header\n\nThe final response should have a `Content-Type: application/problem+json` or\n`Content-Type: application/problem+xml` header:\n\n\u003cpre\u003e\nHTTP/1.1 404 Not Found\n\u003cb style=\"color:green\"\u003eContent-Type: application/problem+json\u003c/b\u003e\nVary: Origin\nDate: Sat, 01 Jan 1970 17:21:18 GMT\nContent-Length: 87\n\n{\"detail\":\"No such API route\",\"instance\":\"/foo\",\"status\":404,\"title\":\"Route Not Found\"}\n\u003c/pre\u003e\n\n### Cache-Control header\n\nThe response **will not have** a `Cache-Control: no-cache, no-store, must-revalidate` header\naccording to the [RFC-7231][RFC7231] for HTTP/1.1, unless otherwise explicitly set.\n\n### Thread-satey considerations\n\n`UnmarshalJSON` and `UnmarshalXML` modifies the receiver, so the concurrent calls\nwith the same receiver would be unsafe.\n\n\u003e - `Problem` instances should not be modified after creation\n\u003e - Each request should use its own `Problem` instance\n\u003e - `UnmarshalJSON` and `UnmarshalXML` should not be called concurrently on the same instance\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n\n[RFC9457]: https://tools.ietf.org/html/rfc9457\n[RFC7231]: https://tools.ietf.org/html/rfc7231\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodeart%2Fgo-problem","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkodeart%2Fgo-problem","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodeart%2Fgo-problem/lists"}