Yokai module for JSON API, based on google/jsonapi.
This module provides to your Yokai application a Processor, that you can inject
in your HTTP handlers to process JSON API requests and responses.
It also provides automatic error handling, compliant with the JSON API specifications.
Install the module:
go get github.com/ankorstore/yokai-contrib/fxjsonapi
Then activate it in your application bootstrapper:
// internal/bootstrap.go
package internal
import (
"github.com/ankorstore/yokai-contrib/fxjsonapi"
"github.com/ankorstore/yokai/fxcore"
)
var Bootstrapper = fxcore.NewBootstrapper().WithOptions(
// load modules
fxjsonapi.FxJSONAPIModule,
// ...
)
Configuration reference:
# ./configs/config.yaml
modules:
jsonapi:
log:
enabled: true # to automatically log JSON API processing, disabled by default
trace:
enabled: true # to automatically trace JSON API processing, disabled by default
JSON API request & response processing are driven by the jsonapi
tag that you can set on your structs for un/marshalling operations.
You can find more information about this in the underlying google/jsonapi library documentation.
You can use the provided Processor to automatically process a JSON API request:
package handler
import (
"net/http"
"github.com/ankorstore/yokai-contrib/fxjsonapi"
"github.com/google/jsonapi"
"github.com/labstack/echo/v4"
)
type Foo struct {
ID int `jsonapi:"primary,foo"`
Name string `jsonapi:"attr,name"`
Bar *Bar `jsonapi:"relation,bar"`
}
func (f Foo) JSONAPIMeta() *jsonapi.Meta {
return &jsonapi.Meta{
"some": "foo meta",
}
}
type Bar struct {
ID int `jsonapi:"primary,bar"`
Name string `jsonapi:"attr,name"`
}
func (b Bar) JSONAPIMeta() *jsonapi.Meta {
return &jsonapi.Meta{
"some": "bar meta",
}
}
type JSONAPIHandler struct {
processor fxjsonapi.Processor
}
func NewJSONAPIHandler(processor fxjsonapi.Processor) *JSONAPIHandler {
return &JSONAPIHandler{
processor: processor,
}
}
func (h *JSONAPIHandler) Handle() echo.HandlerFunc {
return func(c echo.Context) error {
foo := Foo{}
// unmarshall JSON API request payload in foo
err := h.processor.ProcessRequest(
// echo context
c,
// pointer to the struct to unmarshall
&foo,
// optionally override module config for logging
fxjsonapi.WithLog(true),
// optionally override module config for tracing
fxjsonapi.WithTrace(true),
)
if err != nil {
return err
}
return c.JSON(http.StatusOK, foo)
}
}
Notes about ProcessRequest()
:
- if the request payload does not respect the JSON API specifications, a
400
error will be automatically returned - if the request
Content-Type
header is notapplication/vnd.api+json
, a415
error will be automatically returned
You can use the provided Processor to automatically process a JSON API response:
package handler
import (
"net/http"
"github.com/ankorstore/yokai-contrib/fxjsonapi"
"github.com/ankorstore/yokai-contrib/fxjsonapi/testdata/model"
"github.com/labstack/echo/v4"
)
type Foo struct {
ID int `jsonapi:"primary,foo"`
Name string `jsonapi:"attr,name"`
Bar *Bar `jsonapi:"relation,bar"`
}
func (f Foo) JSONAPIMeta() *jsonapi.Meta {
return &jsonapi.Meta{
"some": "foo meta",
}
}
type Bar struct {
ID int `jsonapi:"primary,bar"`
Name string `jsonapi:"attr,name"`
}
func (b Bar) JSONAPIMeta() *jsonapi.Meta {
return &jsonapi.Meta{
"some": "bar meta",
}
}
type JSONAPIHandler struct {
processor fxjsonapi.Processor
}
func NewJSONAPIHandler(processor fxjsonapi.Processor) *JSONAPIHandler {
return &JSONAPIHandler{
processor: processor,
}
}
func (h *JSONAPIHandler) Handle() echo.HandlerFunc {
return func(c echo.Context) error {
foo := Foo{
ID: 123,
Name: "foo",
Bar: &Bar{
ID: 456,
Name: "bar",
},
}
return h.processor.ProcessResponse(
// echo context
c,
// HTTP status code
http.StatusOK,
// pointer to the struct to marshall
&foo,
// optionally pass metadata to the JSON API response
fxjsonapi.WithMetadata(map[string]interface{}{
"some": "response meta",
}),
// optionally remove the included from the JSON API response (enabled by default)
fxjsonapi.WithIncluded(false),
// optionally override module config for logging
fxjsonapi.WithLog(true),
// optionally override module config for tracing
fxjsonapi.WithTrace(true),
)
}
}
Notes about ProcessResponse()
:
- you can pass a
pointer
or aslice of pointers
to marshall as JSON API application/vnd.api+json
will be automatically added to the responseContent-Type
header
This module automatically enables the ErrorHandler, to convert errors bubbling up in JSON API format.
It handles:
- JSON API errors: automatically sets the
status code of the error
- validation errors: automatically sets a
400
status code - HTTP errors: automatically sets the
status code of the error
- or any generic error: automatically sets a
500
status code
You can optionally obfuscate
the errors details (ex: for production) in the HTTP server configuration:
# ./configs/config.yaml
modules:
http:
server:
errors:
obfuscate: true # disabled by default
This module provides a ProcessorMock for mocking Processor, see usage example.