Description

Package goa provides the runtime support for goa microservices.

Code Generation

goa service development begins with writing the *design* of a service. The design is described using the goa language implemented by the github.com/goadesign/goa/design/apidsl package. The `goagen` tool consumes the metadata produced from executing the design language to generate service specific code that glues the underlying HTTP server with action specific code and data structures.

The goa package contains supporting functionality for the generated code including basic request and response state management through the RequestData and ResponseData structs, error handling via error classes, middleware support via the Middleware data structure as well as decoding and encoding algorithms.

Request Context

The RequestData and ResponseData structs provides access to the request and response state. goa request handlers also accept a golang.org/x/net/Context interface as first parameter so that deadlines and cancelation signals may easily be implemented.

The request state exposes the underlying http.Request object as well as the deserialized payload (request body) and parameters (both path and querystring parameters). Generated action specific contexts wrap the context.Context, ResponseData and RequestData data structures. They expose properly typed fields that correspond to the request parameters and body data structure descriptions appearing in the design.

The response state exposes the response status and body length as well as the underlying ResponseWriter. Action contexts provide action specific helper methods that write the responses as described in the design optionally taking an instance of the media type for responses that contain a body.

Here is an example showing an "update" action corresponding to following design (extract):

Resource("bottle", func() {
    DefaultMedia(Bottle)
    Action("update", func() {
        Params(func() {
            Param("bottleID", Integer)
        })
        Payload(UpdateBottlePayload)
        Response(OK)
        Response(NotFound)
    })
})

The action signature generated by goagen is:

type BottleController interface {
    goa.Controller
    Update(*UpdateBottleContext) error
}

where UpdateBottleContext is:

type UpdateBottleContext struct {
    context.Context      // Timeout and deadline support
    *goa.ResponseData    // Response state access
    *goa.RequestData     // Request state access
    Service *goa.Service // Service handling request
    BottleID  int        // Properly typed parameter fields
    Payload   *UpdateBottlePayload // Properly typed payload
}

and implements:

func (ctx *UpdateBottleContext) OK(resp *Bottle) error
func (ctx *UpdateBottleContext) NotFound() error

The definitions of the Bottle and UpdateBottlePayload data structures are ommitted for brievity.

Controllers

There is one controller interface generated per resource defined via the design language. The interface exposes the controller actions. User code must provide data structures that implement these interfaces when mounting a controller onto a service. The controller data structure should include an anonymous field of type *goa.Controller which takes care of implementing the middleware handling.

Middleware

A goa middleware is a function that takes and returns a Handler. A Handler is a the low level function which handles incoming HTTP requests. goagen generates the handlers code so each handler creates the action specific context and calls the controller action with it.

Middleware can be added to a goa service or a specific controller using the corresponding Use methods. goa comes with a few stock middleware that handle common needs such as logging, panic recovery or using the RequestID header to trace requests across multiple services.

Error Handling

The controller action methods generated by goagen such as the Update method of the BottleController interface shown above all return an error value. goa defines an Error struct that action implementations can use to describe the content of the corresponding HTTP response. Errors can be created using error classes which are functions created via NewErrorClass.

The ErrorHandler middleware maps errors to HTTP responses. Errors that are instances of the Error struct are mapped using the struct fields while other types of errors return responses with status code 500 and the error message in the body.

Validation

The goa design language documented in the dsl package makes it possible to attach validations to data structure definitions. One specific type of validation consists of defining the format that a data structure string field must follow. Example of formats include email, data time, hostnames etc. The ValidateFormat function provides the implementation for the format validation invoked from the code generated by goagen.

Encoding

The goa design language makes it possible to specify the encodings supported by the API both as input (Consumes) and output (Produces). goagen uses that information to registed the corresponding packages with the service encoders and decoders via their Register methods. The service exposes the DecodeRequest and EncodeResponse that implement a simple content type negotiation algorithm for picking the right encoder for the "Content-Type" (decoder) or "Accept" (encoder) request header.

Package goa standardizes on structured error responses: a request that fails because of an invalid input or an unexpected condition produces a response that contains a structured error.

The error data structures returned to clients contains five fields: an ID, a code, a status, a detail and metadata. The ID is unique for the occurrence of the error, it helps correlate the content of the response with the content of the service logs. The code defines the class of error (e.g. "invalid_parameter_type") and the status the corresponding HTTP status (e.g. 400). The detail contains a message specific to the error occurrence. The metadata contains key/value pairs that provide contextual information (name of parameters, value of invalid parameter etc.).

Instances of Error can be created via Error Class functions. See http://goa.design/implement/error_handling.html All instance of errors created via a error class implement the ServiceError interface. This interface is leveraged by the error handler middleware to produce the error responses.

The code generated by goagen calls the helper functions exposed in this file when it encounters invalid data (wrong type, validation errors etc.) such as InvalidParamTypeError, InvalidAttributeTypeError etc. These methods return errors that get merged with any previously encountered error via the Error Merge method. The helper functions are error classes stored in global variable. This means your code can override their values to produce arbitrary error responses.

goa includes an error handler middleware that takes care of mapping back any error returned by previously called middleware or action handler into HTTP responses. If the error was created via an error class then the corresponding content including the HTTP status is used otherwise an internal error is returned. Errors that bubble up all the way to the top (i.e. not handled by the error middleware) also generate an internal error response.

goa is referenced in 18 repositories

github.com/goadesign/goa github.com/gopheracademy/congo github.com/goadesign/goa-cellar

...