Middleware


A middleware consists of a function that accepts and returns a endpoint or a transport specific handler.

Endpoint Middlewares

Endpoint middlewares operate at the endpoint level and are transport agnostic. They consist of functions that accept and return a Goa endpoint. Here is an example of an endpoint middleware that logs errors:

// ErrorLogger is an endpoint middleware that logs errors using the given
// logger. All log entries start with the given prefix.
func ErrorLogger(l *log.Logger, prefix string) func (goa.Endpoint) goa.Endpoint {
    return func(e goa.Endpoint) goa.Endpoint {
        // A Goa endpoint is itself a function.
        return goa.Endpoint(func (ctx context.Context, req interface{}) (interface{}, error) {
            // Call the original endpoint function.
            res, err := e(ctx, req)
            // Log any error.
            if err != nil {
                l.Printf("%s: %s", prefix, err.Error())
            }
            // Return endpoint results.
            return res, err
        })
    }
}

The service code generated by Goa defines a Use method that applies the middleware to all endpoints defined by the service. Alternatively the middleware can be applied to specific endpoints by wrapping the corresponding endpoints struct field.

import (
    calcsvc "goa.design/examples/basic/gen/calc"
    calc "goa.design/examples/basic"
)

func main() {
    // ...
    var svc calcsvc.Service
    {
        svc = calc.NewCalc(logger)
    }

    var eps *calcsvc.Endpoints
    {
        eps = calcsvc.NewEndpoints(svc)

        // Apply ErrorLogger to all endpoints.
        calcsvc.Use(ErrorLogger(logger, "calc"))

        // Or apply ErrorLogger to specific endpoint.
        // eps.Add = ErrorLogger(logger, "add")(eps.Add)
    }
    // ...
}

Transport Middlewares

Transport middlewares operate at the transport layer. They apply to HTTP handlers or gRPC methods.

HTTP Middlewares

A HTTP middlewares is a function that takes and returns a HTTP handler.

Here is an example of a HTTP middleware that reads the value of the X-Request-Id request header and writes it in the request context if present.

// InitRequestID is a HTTP server middleware that reads the value of the
// X-Request-Id header and if present writes it in the request context.
func InitRequestID() func(http.Handler) http.Handler {
    return func(h http.Handler) http.Handler {
        // A HTTP handler is a function.
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            req := r
            // Grab X-Request-Id header and initialize request context with it.
            if id := r.Header.Get("X-Request-Id"); id != "" {
                ctx = context.WithValue(r.Context(), RequestIDKey, id)
                req = r.WithContext(ctx)
            }

            // Call initial handler.
            h.ServeHTTP(w, req)
        })
    }
}

HTTP middlewares can be applied using the generated Use method on the HTTP server. This method applies the middleware to all the server handlers. Alternatively the middleware may be applied to a specific server handler similarly to how endpoint middlewares may be applied to a specific endpoint. HTTP middlewares may also be mounted directly on the goa Muxer to execute the middleware on all requests independently of any handler.

func main() {
    // ...
    var mux goahttp.Muxer
    {
        mux = goahttp.NewMuxer()
    }

    var calcServer *calcsvcsvr.Server
    {
        calcServer = calcsvcsvr.New(calcEndpoints, mux, dec, enc, errorHandler(logger))
        // Apply InitRequestID middleware to all server handlers.
        calcServer.Use(InitRequestID())
    }

    // Alternatively apply middleware to all requests.
    // var handler http.Handler = mux
    // handler = InitRequestID()(handler)
    // ... Now use handler to start the HTTP server instead of mux.
}

Goa has implementations for the following HTTP middlewares:

gRPC Middleware

gRPC middlewares are gRPC transport specific and consist of server and client gRPC interceptors.

Goa implements the following gRPC middlewares:

Refer to the tracing example for an illustration of how gRPC middlewares may be applied to gRPC endpoints.