dispatch

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2021 License: MIT Imports: 12 Imported by: 0

README

github.com/flick-web/dispatch

Dispatch is a not-too-complicated framework meant for creating super quick and easy JSON APIs. Error handling, CORS, JSON parsing, and more are all handled out of the box.

Basic Usage

This program creates and serves an API with a single endpoint. The endpoint simply returns a string composed with the {name} path variable.

package main

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

	"github.com/flick-web/dispatch"
)

func rootHandler(ctx *dispatch.Context) string {
	return fmt.Sprintf("Hello, %s!", ctx.PathVars["name"])
}

func main() {
	api := &dispatch.API{}
	api.AddEndpoint("GET/{name}", rootHandler)
	http.HandleFunc("/", api.HTTPProxy)
	log.Fatal(http.ListenAndServe(":8000", nil))
}

For a real-world example, check out pjournal, a fully-fledged personal journaling web app.

API Paths

Paths are expressed as a simple string, in the form:

METHOD/path/{pathvar}

Any path variables in curly braces will be automatically parsed and provided to handler functions in the dispatch.Context.PathVars map. Any path elements not in curly braces are treated as literals, and must be matched for the handler to be called.

API Endpoints

Endpoints return JSON when used, but the handler functions themselves can accept and return any time, with certain restrictions.

A handler function's input signature can be any of these four types:

  • (none)
  • (*dispatch.Context)
  • (<AnyType>)
  • (<AnyType>, *dispatch.Context) (order does not matter)

If the handler accepts an input type other than *dispatch.Context, it can be anything—a string, a struct, or whatever else. Dispatch will automagically marshal any incoming JSON into your type for you.

A handler function's output signature is slightly more restricted:

  • (none)
  • (error)
  • (<AnyType>)
  • (<AnyType>, error) (order does matter)

If your function returns a *dispatch.APIError, its status code and error message will be used for the response. If your function returns a plain error, the handler provided by the api package will automatically return an HTTP error. dispatch.ErrorNotFound and dispatch.ErrorBadRequest errors will also be accompanied by correct HTTP status codes. Otherwise, dispatch will simply return status 500 and the text of your error.

Middleware

The api.AddEndpoint method also allows adding middleware hooks. These hooks are functions which will be called before the endpoint handler is called, and can choose to modify the method, path, context, or input of the endpoint before it is passed along. If the hook returns an error, execution of the endpoint will halt. This is useful for things like authentication checks, which must happen before the function is triggered, and must be able to return early if a call isn't authorized.

Known Issues/Disclaimer

Access control headers allow a hardcoded value of * for the origin, and only specific content types.

Dispatch was created for a specific purpose, so there are many parts of the library that are too inflexible for many use cases.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrBadRequest = errors.New("bad request")

ErrBadRequest represents an error from a malformed request.

View Source
var ErrInternal = errors.New("internal error")

ErrInternal represents some unexpected internal error.

View Source
var ErrNotFound = errors.New("path not found")

ErrNotFound represents a 404 error.

Functions

func APIGatewayUserID added in v0.0.5

func APIGatewayUserID(ctx events.APIGatewayProxyRequestContext) string

APIGatewayUserID returns the subject from the proxy request's authorizer.

func ContextLambdaRequest added in v0.1.0

func ContextLambdaRequest(ctx context.Context) *events.APIGatewayProxyRequest

func ContextLambdaResponse added in v0.1.0

func ContextLambdaResponse(ctx context.Context) *events.APIGatewayProxyResponse

func SetContextLambdaRequest added in v0.1.0

func SetContextLambdaRequest(ctx context.Context, req *events.APIGatewayProxyRequest) context.Context

func SetContextLambdaResponse added in v0.1.0

func SetContextLambdaResponse(ctx context.Context, res *events.APIGatewayProxyResponse) context.Context

func SetContextPathVars added in v0.1.0

func SetContextPathVars(ctx context.Context, pathVars PathVars) context.Context

Types

type API

type API struct {
	Endpoints []*Endpoint
}

API is an object that holds all API methods and can dispatch them.

func (*API) AddEndpoint

func (api *API) AddEndpoint(path string, handler interface{}, hooks ...MiddlewareHook)

AddEndpoint registers an endpoint with this API. It also allows adding middleware hooks to the endpoint.

func (*API) Call

func (api *API) Call(ctx context.Context, method, path string, input json.RawMessage) (out interface{}, err error)

Call sends the input to the endpoint and returns the result.

func (*API) GetMethodsForPath

func (api *API) GetMethodsForPath(path string) []string

GetMethodsForPath returns the list of valid methods for a specified path (for use in OPTIONS requests).

func (*API) HTTPProxy

func (api *API) HTTPProxy(w http.ResponseWriter, r *http.Request)

HTTPProxy is a handler function suitable for use in http.HandleFunc. For example:

http.HandleFunc("/", api.HTTPProxy)
log.Fatal(http.ListenAndServe(":8000", nil))

The provided handler takes care of access control headers, CORS requests, JSON marshalling, and error handling.

func (*API) LambdaProxy

func (api *API) LambdaProxy(corsAllowedOrigin string) func(*events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error)

LambdaProxy returns a handler function suitable for use with github.com/aws/aws-lambda-go/lambda. For example:

import "github.com/aws/aws-lambda-go/lambda"
func main() {
	lambda.Start(api.LambdaProxy("*"))
}

The provided handler takes care of access control headers, CORS requests, JSON marshalling, and error handling.

func (*API) MatchEndpoint

func (api *API) MatchEndpoint(method, path string) (*Endpoint, PathVars)

MatchEndpoint matches a request to an endpoint, creating a map of path variables in the process.

type APIError

type APIError struct {
	StatusCode int
	ErrorText  string
}

APIError is an error that contains status code information as well as error text.

func NewAPIError added in v0.0.2

func NewAPIError(statusCode int, errorText string) *APIError

NewAPIError returns an APIError from the given HTTP status code and error string.

func NewAPIErrorFromStatus added in v0.0.2

func NewAPIErrorFromStatus(statusCode int) *APIError

NewAPIErrorFromStatus creates an APIError with text from an HTTP status code.

func (*APIError) Error

func (apiErr *APIError) Error() string

type APIPath

type APIPath struct {
	PathParts []string
	Method    string
}

An APIPath represents a specified path and method, such as GET/users/{uuid}.

func NewAPIPath

func NewAPIPath(path string) (*APIPath, error)

NewAPIPath creates an APIPath object from a path string, in the format GET/users/{uuid}.

func (*APIPath) Match

func (a *APIPath) Match(method, path string) (pathVars PathVars, ok bool)

Match tests an APIPath against a path string, and returns a map of path variables and a boolean representing whether it was a match.

func (*APIPath) MatchPath

func (a *APIPath) MatchPath(path string) bool

MatchPath tests an APIPath against a path string, and returns true if the path matches.

type Endpoint

type Endpoint struct {

	// Path is the API path string that will be exposed as an API endpoint. Must
	// be unique.
	//
	// The format of Path is METHOD/path/{pathvar}. Any path variables in curly
	// brace notation will be parsed during API.Call and passed to Handler as
	// a Context struct value.
	Path string

	// Handler must be a function that receives any single input variable, an
	// input variable of type Context, neither, or both. It can return one
	// output variable of any time, an error, neither, or both in the order
	// (output, error).
	//
	// The input value for Handler, if not Context, will automatically be
	// unmarshalled from the input to API.Call.
	Handler interface{}

	// PreRequestHook is a middleware hook that runs before the handler. If the
	// hook returns an error, that error will be returned and the handler will
	// not be called.
	PreRequestHooks []MiddlewareHook
	// contains filtered or unexported fields
}

An Endpoint represents an API procedure.

type EndpointInput

type EndpointInput struct {
	Method string
	Path   string
	Ctx    context.Context
	Input  json.RawMessage
}

EndpointInput represents the input to an endpoint call. These inputs can be modified by middleware hooks.

type MiddlewareHook

type MiddlewareHook func(*EndpointInput) (*EndpointInput, error)

MiddlewareHook is a function type that is called for each request.

These hooks are functions which will be called before the endpoint handler is called, and can choose to modify the method, path, context, or input of the endpoint before it is passed along. If the hook returns an error, execution of the endpoint will halt. This is useful for things like authentication checks, which must happen before the function is triggered, and must be able to return early if a call isn't authorized.

type PathVars

type PathVars map[string]string

PathVars is an alias for map[string]string, used for captured path variables.

func ContextPathVars added in v0.1.0

func ContextPathVars(ctx context.Context) PathVars

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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