Skip to content

Package problem implements the RFC 9457 problem details specification in Go.

License

Notifications You must be signed in to change notification settings

nussjustin/problem

Repository files navigation

RFC 9457 Problem Details API for Go

Go Reference Lint Test

This module provides an API for the RFC 9457 problem details specification in Go.

Warning

This module depends on the experimental github.com/go-json-experiment/json package. This package is planned to become part of the Go standard library in form of a future json/v2 package. Once that happens this module will be updated to use the new json/v2 package from the standard library instead.

Examples

Using problem details

Problem details are represented by the problem.Details type and can be constructed either directly via a struct literal or using the global New function.

Example using a struct literal:

var OutOfCreditProblemType = &problem.Type{
    Type:     "https://example.com/probs/out-of-credit",
    Title:    "You do not have enough credit.",
    Status:   http.StatusForbidden,
    Detail:   "Your current balance is 30, but that costs 50.",
    Instance: "/account/12345/msgs/abc",
    Extensions: map[string]any{
        "balance":  30,
        "accounts": []string{"/account/12345", "/account/67890"},
    },
}

All fields are optional, but at least the Type, Title and Status fields should always be set.

When marshaled as JSON the Details type will result in a single object containing all fields that do not have a zero value as well as all added extensions.

Similarly, when unmarshalling, values for known fields are stored into the existing struct fields, with unknown fields being added into the Extensions map.

The Details object implements the http.Handler interface making it possible to directly write a problem as a response to an HTTP request.

Example:

type (s *MyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // ...
    if outOfCredit {
        &problem.Type{
            Type:     "https://example.com/probs/out-of-credit",
            Title:    "You do not have enough credit.",
            Status:   http.StatusForbidden,
        }.ServeHTTP(w, r)
        return
    }
    // ...
}

Defining and using reusable types

It is also possible to pre-defines specific problem types that can be used to create new Details instances.

The main use case is as package-level variables that can than be used across different types and functions. These types can reduce boilerplate and serve as part of the documentation

Example:

var OutOfCreditProblemType = &problem.Type{
    URI: "https://example.com/probs/out-of-credit",
    Title: "You do not have enough credit.",
    Status: http.StatusForbidden,
}

The Details method returns a new Details value that inherits the values from the type, while also making it possible to add case specific details data and other relevant data using functional options.

Example:

if outOfCredit {
    OutOfCreditProblemType.Details(
        problem.WithDetail("Your current balance is 30, but that costs 50."),
        problem.WithInstance("/account/12345/msgs/abc"),
        problem.WithExtension("balance", 30),
        problem.WithExtension("accounts", []string{"/account/12345", "/account/67890"}),
    ).ServeHTTP(w, r)
}

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

MIT

About

Package problem implements the RFC 9457 problem details specification in Go.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published