Categorygithub.com/goadesign/goa
modulepackage
1.4.3
Repository: https://github.com/goadesign/goa.git
Documentation: pkg.go.dev

# README

goa is a framework for building micro-services and REST APIs in Go using a unique design-first approach.

Build Status Windows Build status License Godoc Slack

Why goa?

goa takes a different approach to building micro-services. Instead of focusing solely on helping with implementation, goa makes it possible to describe the design of an API using a simple DSL. goa then uses that description to provide specialized helper code to the implementation and to generate documentation, API clients, tests, even custom artifacts.

If DSLs are not your thing then consider this: you need to document your APIs so that clients (be it internal e.g. other services or external e.g. UIs) may consume them. Typically this requires maintaining a completely separate document (for example an OpenAPI specification). Making sure that the document stays up-to-date takes a lot of effort and at the end of the day you have to write that document - why not use a simple and clear Go DSL to do that instead?

Another aspect to consider is the need for properly designing APIs and making sure that the design choices remain consistent across the endpoints or even across multiple APIs. If the source code is the only place where design decisions are kept then not only is it impossible to maintain consistency it's also difficult to think about the design in the first place. The goa DSL makes it possible to think about the design explicitly and - since it's code - to re-use design elements for consistency.

The goa DSL allows writing self-explanatory code that describes the resources exposed by the API and for each resource the properties and actions. goa comes with the goagen tool which runs the DSL and generates various types of artifacts from the resulting data structures.

One of the goagen output is glue code that binds your code with the underlying HTTP server. This code is specific to your API so that for example there is no need to cast or "bind" any handler argument prior to using them. Each generated handler has a signature that is specific to the corresponding resource action. It's not just the parameters though, each handler also has access to specific helper methods that generate the possible responses for that action. The DSL can also define validations in which case the generated code takes care of validating the incoming request parameters and payload prior to invoking the handler.

The end result is controller code that is terse and clean, the boilerplate is all gone. Another big benefit is the clean separation of concern between design and implementation: on bigger projects it's often the case that API design changes require careful review, being able to generate a new version of the documentation without having to write a single line of implementation is a big boon.

This idea of separating design and implementation is not new, the excellent Praxis framework from RightScale follows the same pattern and was an inspiration to goa.

Installation

Goa v1 can be used with Go modules:

export GO111MODULE=on
go mod init <my project>
go get github.com/goadesign/goa/...@v1

Or without Go modules by cloning the repo first:

cd $GOPATH/src
mkdir -p github.com/goadesign
cd github.com/goadesign
git clone https://github.com/goadesign/goa
cd goa; git checkout v1
go get -v github.com/goadesign/goa/...

Stable Versions

goa follows Semantic Versioning which is a fancy way of saying it publishes releases with version numbers of the form vX.Y.Z and makes sure that your code can upgrade to new versions with the same X component without having to make changes.

Releases are tagged with the corresponding version number. There is also a branch for each major version (only v1 at the moment). The recommended practice is to vendor the stable branch.

Current Release: v1.4.3 Stable Branch: v1

Teaser

1. Design

Create the file $GOPATH/src/goa-adder/design/design.go with the following content:

package design

import (
        . "github.com/goadesign/goa/design"
        . "github.com/goadesign/goa/design/apidsl"
)

var _ = API("adder", func() {
        Title("The adder API")
        Description("A teaser for goa")
        Host("localhost:8080")
        Scheme("http")
})

var _ = Resource("operands", func() {
        Action("add", func() {
                Routing(GET("add/:left/:right"))
                Description("add returns the sum of the left and right parameters in the response body")
                Params(func() {
                        Param("left", Integer, "Left operand")
                        Param("right", Integer, "Right operand")
                })
                Response(OK, "text/plain")
        })

})

This file contains the design for an adder API which accepts HTTP GET requests to /add/:x/:y where :x and :y are placeholders for integer values. The API returns the sum of x and y in its body.

2. Implement

Now that the design is done, let's run goagen on the design package:

cd $GOPATH/src/goa-adder
goagen bootstrap -d goa-adder/design

This produces the following outputs:

  • main.go and operands.go contain scaffolding code to help bootstrap the implementation. running goagen again does not recreate them so that it's safe to edit their content.
  • an app package which contains glue code that binds the low level HTTP server to your implementation.
  • a client package with a Client struct that implements a AddOperands function which calls the API with the given arguments and returns the http.Response.
  • a tool directory that contains the complete source for a client CLI tool.
  • a swagger package with implements the GET /swagger.json API endpoint. The response contains the full Swagger specificiation of the API.

3. Run

First let's implement the API - edit the file operands.go and replace the content of the Add function with:

// Add import for strconv
import "strconv"

// Add runs the add action.
func (c *OperandsController) Add(ctx *app.AddOperandsContext) error {
        sum := ctx.Left + ctx.Right
        return ctx.OK([]byte(strconv.Itoa(sum)))
}

Now let's compile and run the service:

cd $GOPATH/src/goa-adder
go build
./goa-adder
2016/04/05 20:39:10 [INFO] mount ctrl=Operands action=Add route=GET /add/:left/:right
2016/04/05 20:39:10 [INFO] listen transport=http addr=:8080

Open a new console and compile the generated CLI tool:

cd $GOPATH/src/goa-adder/tool/adder-cli
go build

The tool includes contextual help:

./adder-cli --help
CLI client for the adder service

Usage:
  adder-cli [command]

Available Commands:
  add         add returns the sum of the left and right parameters in the response body

Flags:
      --dump               Dump HTTP request and response.
  -H, --host string        API hostname (default "localhost:8080")
  -s, --scheme string      Set the requests scheme
  -t, --timeout duration   Set the request timeout (default 20s)

Use "adder-cli [command] --help" for more information about a command.

To get information on how to call a specific API use:

./adder-cli add operands --help
Usage:
  adder-cli add operands [/add/LEFT/RIGHT] [flags]

Flags:
      --left int    Left operand
      --pp          Pretty print response body
      --right int   Right operand

Global Flags:
      --dump               Dump HTTP request and response.
  -H, --host string        API hostname (default "localhost:8080")
  -s, --scheme string      Set the requests scheme
  -t, --timeout duration   Set the request timeout (default 20s)

Now let's run it:

./adder-cli add operands /add/1/2
2016/04/05 20:43:18 [INFO] started id=HffVaGiH GET=http://localhost:8080/add/1/2
2016/04/05 20:43:18 [INFO] completed id=HffVaGiH status=200 time=1.028827ms
3⏎

This also works:

$ ./adder-cli add operands --left=1 --right=2
2016/04/25 00:08:59 [INFO] started id=ouKmwdWp GET=http://localhost:8080/add/1/2
2016/04/25 00:08:59 [INFO] completed id=ouKmwdWp status=200 time=1.097749ms
3⏎

The console running the service shows the request that was just handled:

2016/06/06 10:23:03 [INFO] started req_id=rLAtsSThLD-1 GET=/add/1/2 from=::1 ctrl=OperandsController action=Add
2016/06/06 10:23:03 [INFO] params req_id=rLAtsSThLD-1 right=2 left=1
2016/06/06 10:23:03 [INFO] completed req_id=rLAtsSThLD-1 status=200 bytes=1 time=66.25µs

Now let's see how robust our service is and try to use non integer values:

./adder-cli add operands add/1/d
2016/06/06 10:24:22 [INFO] started id=Q2u/lPUc GET=http://localhost:8080/add/1/d
2016/06/06 10:24:22 [INFO] completed id=Q2u/lPUc status=400 time=1.301083ms
error: 400: {"code":"invalid_request","status":400,"detail":"invalid value \"d\" for parameter \"right\", must be a integer"}

As you can see the generated code validated the incoming request against the types defined in the design.

4. Document

The swagger directory contains the API Swagger (OpenAPI) specification in both YAML and JSON format.

For open source projects hosted on github swagger.goa.design provides a free service that renders the Swagger representation dynamically from goa design packages. Simply set the url query string with the import path to the design package. For example displaying the docs for github.com/goadesign/goa-cellar/design is done by browsing to:

http://swagger.goa.design/?url=goadesign%2Fgoa-cellar%2Fdesign

Note that the above generates the swagger spec dynamically and does not require it to be present in the Github repo.

The Swagger JSON can also easily be served from the documented service itself using a simple Files definition in the design. Edit the file design/design.go and add:

var _ = Resource("swagger", func() {
        Origin("*", func() {
               Methods("GET") // Allow all origins to retrieve the Swagger JSON (CORS)
        })
        Files("/swagger.json", "swagger/swagger.json")
})

Re-run goagen bootstrap -d goa-adder/design and note the new file swagger.go containing the implementation for a controller that serves the swagger.json file.

Mount the newly generated controller by adding the following two lines to the main function in main.go:

cs := NewSwaggerController(service)
app.MountSwaggerController(service, cs)

Recompile and restart the service:

^C
go build
./goa-adder
2016/06/06 10:31:14 [INFO] mount ctrl=Operands action=Add route=GET /add/:left/:right
2016/06/06 10:31:14 [INFO] mount ctrl=Swagger files=swagger/swagger.json route=GET /swagger.json
2016/06/06 10:31:14 [INFO] listen transport=http addr=:8080

Note the new route /swagger.json. Requests made to it return the Swagger specification. The generated controller also takes care of adding the proper CORS headers so that the JSON may be retrieved from browsers using JavaScript served from a different origin (e.g. via Swagger UI). The client also has a new download action:

cd tool/adder-cli
go build
./adder-cli download --help
Download file with given path

Usage:
  adder-cli download [PATH] [flags]

Flags:
      --out string   Output file

Global Flags:
      --dump               Dump HTTP request and response.
  -H, --host string        API hostname (default "localhost:8080")
  -s, --scheme string      Set the requests scheme
  -t, --timeout duration   Set the request timeout (default 20s)

Which can be used like this to download the file swagger.json in the current directory:

./adder-cli download swagger.json
2016/06/06 10:36:24 [INFO] started file=swagger.json id=ciHL2VLt GET=http://localhost:8080/swagger.json
2016/06/06 10:36:24 [INFO] completed file=swagger.json id=ciHL2VLt status=200 time=1.013307ms

We now have a self-documenting API and best of all the documentation is automatically updated as the API design changes.

Resources

Consult the following resources to learn more about goa.

goa.design

goa.design contains further information on goa including a getting started guide, detailed DSL documentation as well as information on how to implement a goa service.

Examples

The examples repo contains simple examples illustrating basic concepts.

The goa-cellar repo contains the implementation for a goa service which demonstrates many aspects of the design language. It is kept up-to-date and provides a reference for testing functionality.

Contributing

Did you fix a bug? write docs or additional tests? or implement some new awesome functionality? You're a rock star!! Just make sure that make succeeds (or that TravisCI is green) and send a PR over.

# Packages

No description provided by the author
Package cors provides the means for implementing the server side of CORS, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS.
Package design defines types which describe the data types used by action controllers.
No description provided by the author
Package encoding provide goa adapters to many different encoders.
No description provided by the author
No description provided by the author
Package logging contains logger adapters that make it possible for goa to log messages to various logger backends.
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

AddSample adds a sample to an aggregated metric reporting count, min, max, mean, and std deviation Usage: AddSample([]string{"my","namespace","key"}, 15.0).
ContextAction extracts the action name from the given context.
ContextController extracts the controller name from the given context.
ContextError extracts the error from the given context.
ContextLogger extracts the logger from the given context.
ContextRequest extracts the request data from the given context.
ContextRequiredScopes extracts the security scopes from the given context.
ContextResponse extracts the response data from the given context.
EmitKey emits a key/value pair Usage: EmitKey([]string{"my","namespace","key"}, 15.0).
GetMetrics returns goa's metrics collector adapter interface.
IncrCounter increments the counter named by `key` Usage: IncrCounter([]key{"my","namespace","counter"}, 1.0).
InvalidAttributeTypeError is the error produced when the type of payload field does not match the type defined in the design.
InvalidEnumValueError is the error produced when the value of a parameter or payload field does not match one the values defined in the design Enum validation.
InvalidFormatError is the error produced when the value of a parameter or payload field does not match the format validation defined in the design.
InvalidLengthError is the error produced when the value of a parameter or payload field does not match the length validation defined in the design.
InvalidParamTypeError is the error produced when the type of a parameter does not match the type defined in the design.
InvalidPatternError is the error produced when the value of a parameter or payload field does not match the pattern validation defined in the design.
InvalidRangeError is the error produced when the value of a parameter or payload field does not match the range validation defined in the design.
LogError extracts the logger from the given context and calls Error on it.
Logger returns the logger stored in the context if any, nil otherwise.
LogInfo extracts the logger from the given context and calls Info on it.
MeasureSince creates a timing metric that records the duration of elapsed time since `start` Usage: MeasureSince([]string{"my","namespace","action}, time.Now()) Frequently used in a defer: defer MeasureSince([]string{"my","namespace","action}, time.Now()).
MergeErrors updates an error by merging another into it.
MethodNotAllowedError is the error produced to requests that match the path of a registered handler but not the HTTP method.
MissingAttributeError is the error produced when a request payload is missing a required field.
MissingHeaderError is the error produced when a request is missing a required header.
MissingParamError is the error produced for requests that are missing path or querystring parameters.
MissingPayloadError is the error produced when a request is missing a required payload.
New instantiates a service with the given name.
NewContext builds a new goa request context.
NewErrorClass creates a new error class.
NewGobDecoder is an adapter for the encoding package gob decoder.
NewGobEncoder is an adapter for the encoding package gob encoder.
NewHTTPDecoder creates a decoder that maps HTTP content types to low level decoders.
NewHTTPEncoder creates an encoder that maps HTTP content types to low level encoders.
NewJSONDecoder is an adapter for the encoding package JSON decoder.
NewJSONEncoder is an adapter for the encoding package JSON encoder.
NewLogger returns a goa log adpater backed by a log logger.
NewMetrics initializes goa's metrics instance with the supplied configuration and metrics sink This method is deprecated and SetMetrics should be used instead.
NewMiddleware creates a middleware from the given argument.
NewMux returns a Mux.
NewNoOpCollector returns a Collector that does no collection.
NewNoOpSink returns a NOOP sink.
NewXMLDecoder is an adapter for the encoding package XML decoder.
NewXMLEncoder is an adapter for the encoding package XML encoder.
NoAuthMiddleware is the error produced when goa is unable to lookup a auth middleware for a security scheme defined in the design.
SetGauge sets the named gauge to the specified value Usage: SetGauge([]string{"my","namespace"}, 2.0).
SetMetrics initializes goa's metrics instance with the supplied metrics adapter interface.
ValidateFormat validates a string against a standard format.
ValidatePattern returns an error if val does not match the regular expression p.
WithAction creates a context with the given action name.
WithError creates a context with the given error.
WithLogContext instantiates a new logger by appending the given key/value pairs to the context logger and setting the resulting logger in the context.
WithLogger sets the request context logger and returns the resulting new context.
WithRequiredScopes builds a context containing the given required scopes.

# Constants

ErrMissingLogValue is the value used to log keys with missing values.
FormatCIDR defines RFC4632 and RFC4291 CIDR notation IP address values.
FormatDate defines RFC3339 date values.
FormatDateTime defines RFC3339 date time values.
FormatEmail defines RFC5322 email addresses.
FormatHostname defines RFC1035 Internet host names.
FormatIP defines RFC2373 IPv4 or IPv6 address values.
FormatIPv4 defines RFC2373 IPv4 address values.
FormatIPv6 defines RFC2373 IPv6 address values.
FormatMAC defines IEEE 802 MAC-48, EUI-48 or EUI-64 MAC address values.
FormatRegexp Regexp defines regular expression syntax accepted by RE2.
FormatRFC1123 defines RFC1123 date time values.
FormatURI defines RFC3986 URI values.
FormatUUID defines RFC4122 uuid values.
LocHeader indicates the secret value should be loaded from the request headers.
LocQuery indicates the secret value should be loaded from the request URL querystring.

# Variables

ErrBadRequest is a generic bad request error.
ErrInternal is the class of error used for uncaught errors.
ErrInvalidEncoding is the error produced when a request body fails to be decoded.
ErrInvalidFile is the error produced by ServeFiles when requested to serve non-existant or non-readable files.
ErrInvalidRequest is the class of errors produced by the generated code when a request parameter or payload fails to validate.
ErrMethodNotAllowed is the error returned to requests that match the path of a registered handler but not the HTTP method.
ErrNoAuthMiddleware is the error produced when no auth middleware is mounted for a security scheme defined in the design.
ErrNotFound is the error returned to requests that don't match a registered handler.
ErrorMediaIdentifier is the media type identifier used for error responses.
ErrRequestBodyTooLarge is the error produced when the size of a request body exceeds MaxRequestBodyLength bytes.
ErrUnauthorized is a generic unauthorized error.

# Structs

APIKeySecurity represents the `apiKey` security scheme.
BasicAuthSecurity represents the `Basic` security scheme, which consists of a simple login/pass, accessible through Request.BasicAuth().
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
JWTSecurity represents an api key based scheme, with support for scopes and a token URL.
NoOpSink default NOOP metrics recorder.
OAuth2Security represents the `oauth2` security scheme.
No description provided by the author
No description provided by the author
No description provided by the author

# Interfaces

Collector is the interface used for collecting metrics.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Type aliases

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
Format defines a validation format.
No description provided by the author
Location is the enum defining where the value of key based security schemes should be read: either a HTTP request header or a URL querystring value.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author