apigo

package module
v2.1.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2019 License: MIT Imports: 12 Imported by: 0

README

apigo

Documentation Build Status Go Report Card

Package apigo is an drop-in adapter to AWS Lambda functions (based on go1.x runtime) with a AWS API Gateway to easily reuse logic from serverfull http.Handlers and provide the same experience for serverless function.

Installation

Add apigo dependency using your vendor package manager (i.e. dep) or go get it:

go get -v github.com/drivetopurchase/apigo

Usage

Default behaviour

If you have already registered some http.Handlers, you can easily reuse them with apigo.Gateway. Example below illustrates how to create a hello world serverless application with apigo:

package main

import (
	"net/http"

	"github.com/drivetopurchase/apigo"
)

func main() {
	http.HandleFunc("/hello", helloHandler)

	apigo.ListenAndServe("api.example.com", http.DefaultServeMux)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(`"Hello World"`))
}
Custom event-to-request transformation

If you have a bit more sophisticated deployment of your AWS Lambda functions then you probably would love to have more control over event-to-request transformation. Imagine a situation if you have your API in one serverless function and you also have additional custom authorizer in separate AWS Lamda function. In following scenario (presented in example below) context variable provided by serverless authorizer is passed to the API's http.Request context, which can be further inspected during request handling:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"

	"github.com/aws/aws-lambda-go/events"
	"github.com/go-chi/chi"
	"github.com/drivetopurchase/apigo"
)

func main() {
	g := &apigo.Gateway{
		Proxy:   &CustomProxy{apigo.DefaultProxy{"api.example.com"}},
		Handler: routing(),
	}
	g.ListenAndServe()
}

type contextUsername struct{}

var keyUsername = &contextUsername{}

type CustomProxy struct {
	apigo.DefaultProxy
}

func (p *CustomProxy) Transform(ctx context.Context, ev events.APIGatewayProxyRequest) (*http.Request, error) {
	r, err := p.DefaultProxy.Transform(ctx, ev)
	if err != nil {
		return nil, err
	}
	// Add username to the http.Request's context from the custom authorizer
	r = r.WithContext(
		context.WithValue(
			r.Context(),
			keyUsername,
			ev.RequestContext.Authorizer["username"],
		),
	)
	return r, err
}

func routing() http.Handler {
	r := chi.NewRouter()

	r.Get("/{id}", func(w http.ResponseWriter, r *http.Request) {
		log.Printf("id: %s", chi.URLParam(r, "id"))

		// Remember: headers, status and then payload - always in this order
		// set headers
		w.Header().Set("Content-Type", "application/json")
		// set status
		w.WriteHeader(http.StatusOK)
		// set response payload
		username, _ := r.Context().Value(keyUsername).(string)
		fmt.Fprintf(w, `"Hello %s"`, username)
	})

	return r
}
Goroutines

If you are going to use goroutines in your AWS Lambda handler, then it is worth noting you should control its execution (i.e. by using sync.WaitGroup), otherwise code in the goroutine might be killed after returning a response to AWS API Gateway.

package main

import (
	"net/http"
	"sync"

	"github.com/go-chi/chi"
	"github.com/drivetopurchase/apigo"
)

func main() {
	apigo.ListenAndServe("api.example.com", routing())
}

func routing() http.Handler {
	r := chi.NewRouter()

	r.Post("/cat", func(w http.ResponseWriter, r *http.Request) {
		var wg sync.WaitGroup
		wg.Add(2)
		go sendIoTMessage(&wg)
		go sendSlackNotification(&wg)
		wg.Wait()

		// Headers, status, payload
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`"Meow"`))
	})

	return r
}

func sendIoTMessage(wg *sync.WaitGroup) {
	// ...
	wg.Done()
}

func sendSlackNotification(wg *sync.WaitGroup) {
	// ...
	wg.Done()
}

Credits

Project has been forked from fabulous tj's apex/gateway repository, at 0bee09a.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ListenAndServe

func ListenAndServe(host string, h http.Handler)

ListenAndServe is a drop-in replacement for http.ListenAndServe for use within AWS Lambda.

func NewContext

NewContext populates a context.Context from the http.Request with a request context provided in event from the AWS API Gateway proxy.

func RequestContext

RequestContext returns the APIGatewayProxyRequestContext value stored in ctx.

Types

type DefaultProxy

type DefaultProxy struct {
	Host string
}

DefaultProxy is a default proxy for AWS API Gateway events

func (*DefaultProxy) Transform

Transform returns a new http.Request created from the given Lambda event.

type Gateway

type Gateway struct {
	Proxy   Proxy
	Handler http.Handler
}

Gateway mimics the http.Server definition and takes care of proxying AWS Lambda event to http.Request via Proxy and then handling it using Handler

func NewGateway

func NewGateway(host string, handler http.Handler) *Gateway

NewGateway creates new Gateway, which utilizes handler (or http.DefaultServeMux if nil passed) as a Gateway.Handler and apigo.http.DefaultProxy as a Gateway.Proxy.

func (*Gateway) ListenAndServe

func (g *Gateway) ListenAndServe()

ListenAndServe registers a listener of AWS Lambda events.

func (*Gateway) Serve

Serve handles incoming event from AWS Lambda by wraping them into http.Request which is further processed by http.Handler to reply as a APIGatewayProxyResponse.

type Proxy

type Proxy interface {
	Transform(context.Context, events.APIGatewayProxyRequest) (*http.Request, error)
}

Proxy transforms an event and context provided from the API Gateway to the http.Request.

type ProxyFunc

ProxyFunc implements the Proxy interface to allow use of ordinary function as a handler.

func (ProxyFunc) Transform

Transform calls f(ctx, ev).

type Request

type Request struct {
	Context context.Context
	Event   events.APIGatewayProxyRequest

	Path string
	Body *bytes.Reader
}

Request is an wrapper which helps transforming event from AWS API Gateway as a http.Request.

func NewRequest

NewRequest defines new RequestBuilder with context and event data provided from the API Gateway.

func (*Request) AttachContext

func (r *Request) AttachContext(req *http.Request)

AttachContext attaches events' RequestContext to the http.Request.

func (*Request) CreateRequest

func (r *Request) CreateRequest(host string) (*http.Request, error)

CreateRequest provides *http.Request to the RequestBuilder.

func (*Request) ParseBody

func (r *Request) ParseBody() error

ParseBody provides body of the request to the RequestBuilder.

func (*Request) ParseURL

func (r *Request) ParseURL(host string) (*url.URL, error)

ParseURL provides URL (as a *url.URL) to the RequestBuilder.

func (*Request) SetContentLength

func (r *Request) SetContentLength(req *http.Request)

SetContentLength sets Content-Length to the request if it has not been set.

func (*Request) SetCustomHeaders

func (r *Request) SetCustomHeaders(req *http.Request)

SetCustomHeaders assigns X-Request-Id and X-Stage from the event's Request Context.

func (*Request) SetHeaderFields

func (r *Request) SetHeaderFields(req *http.Request)

SetHeaderFields sets headers to the request.

func (*Request) SetRemoteAddr

func (r *Request) SetRemoteAddr(req *http.Request)

SetRemoteAddr sets RemoteAddr to the request.

func (*Request) SetXRayHeader

func (r *Request) SetXRayHeader(req *http.Request)

SetXRayHeader sets AWS X-Ray Trace ID from the event's context.

func (*Request) StripBasePath

func (r *Request) StripBasePath(basePath string)

StripBasePath removes a BasePath from the Path fragment of the URL. StripBasePath must be run before RequestBuilder.ParseURL function.

type ResponseWriter

type ResponseWriter struct {
	// contains filtered or unexported fields
}

ResponseWriter implements the http.ResponseWriter interface in order to support the API Gateway Lambda HTTP "protocol".

func NewResponse

func NewResponse() *ResponseWriter

NewResponse returns a new response writer to capture http output.

func (*ResponseWriter) CloseNotify

func (w *ResponseWriter) CloseNotify() <-chan bool

CloseNotify notify when the response is closed

func (*ResponseWriter) End

End the request.

func (*ResponseWriter) Header

func (w *ResponseWriter) Header() http.Header

Header implementation.

func (*ResponseWriter) Write

func (w *ResponseWriter) Write(b []byte) (int, error)

Write implementation.

func (*ResponseWriter) WriteHeader

func (w *ResponseWriter) WriteHeader(status int)

WriteHeader implementation.

type StripBasePathProxy

type StripBasePathProxy struct {
	Host     string
	BasePath string
}

func (*StripBasePathProxy) Transform

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL