Herodot is a lightweight SDK for writing RESTful responses. It is comparable to render but provides easier error handling. The error model implements the well established Google API Design Guide. Herodot currently supports only JSON responses but can be extended easily.
Herodot is used by ORY Hydra and serves millions of requests already.
Herodot is versioned using go modules and works best with pkg/errors. To install it, run
go get -u github.com/ory/herodot
The v2 directory contains the module github.com/ory/herodot/v2. It
identifies errors by a domain and a reason from a proto catalog and renders them
in the AIP-193 shape. This module bridges to it:
herodot.Legacy(outer, inner)wraps a v2 error in theDefaultErrorthat was returned before, so that legacy responses stay byte-equal during the migration.DefaultErrorimplementsherodotv2.Classifier, soherodotv2.Fromclassifies it under thelegacy.ory.comdomain.JSONWriterrenders v2 errors that aren't wrapped in aDefaultErrorin the legacy shape, andJSONWriter.Recorderand theRecording*Interceptorfunctions record each error response with its AIP shadow rendering.
Tips on upgrading can be found in UPGRADE.md
Using Herodot is straightforward. The examples below will help you get started.
Herodot supplies an interface, allowing to return errors in many data formats like XML and others. Currently, it supports only JSON.
var hd = herodot.NewJSONWriter(nil)
func GetHandler(rw http.ResponseWriter, r *http.Request) {
// run your business logic here
hd.Write(rw, r, map[string]interface{}{
"key": "value"
})
}
type MyStruct struct {
Key string `json:"key"`
}
func GetHandlerWithStruct(rw http.ResponseWriter, r *http.Request) {
// business logic
hd.Write(rw, r, &MyStruct{Key: "value"})
}
func PostHandlerWithStruct(rw http.ResponseWriter, r *http.Request) {
// business logic
hd.WriteCreated(rw, r, "/path/to/the/resource/that/was/created", &MyStruct{Key: "value"})
}
func SomeHandlerWithArbitraryStatusCode(rw http.ResponseWriter, r *http.Request) {
// business logic
hd.WriteCode(rw, r, http.StatusAccepted, &MyStruct{Key: "value"})
}
func SomeHandlerWithArbitraryStatusCode(rw http.ResponseWriter, r *http.Request) {
// business logic
hd.WriteCode(rw, r, http.StatusAccepted, &MyStruct{Key: "value"})
}var writer = herodot.NewJSONWriter(nil)
func GetHandlerWithError(rw http.ResponseWriter, r *http.Request) {
if err := someFunctionThatReturnsAnError(); err != nil {
hd.WriteError(w, r, err)
return
}
// ...
}
func GetHandlerWithErrorCode(rw http.ResponseWriter, r *http.Request) {
if err := someFunctionThatReturnsAnError(); err != nil {
hd.WriteErrorCode(w, r, http.StatusBadRequest, err)
return
}
// ...
}Herodot implements the error model of the well established
Google API Design Guide.
Additionally, it makes the fields request and reason available. A complete
Herodot error response looks like this:
{
"error": {
"code": 404,
"status": "some-status",
"request": "foo",
"reason": "some-reason",
"details": [{ "foo": "bar" }],
"message": "foo"
}
}To add context to your errors, implement herodot.ErrorContextCarrier. If you
only want to set the status code of errors implement
herodot.StatusCodeCarrier.