Categorygithub.com/blueowlopensource/endpoint
modulepackage
1.0.2
Repository: https://github.com/blueowlopensource/endpoint.git
Documentation: pkg.go.dev

# README

endpoint - dependency injection and wrapping for http handlers

GoDoc

Install:

go get github.com/BlueOwlOpenSource/endpoint

This package attempts to solve several issues with http endpoint handlers:

  • Chaining actions to create composite handlers
  • Wrapping endpoint handlers with before/after actions
  • Dependency injection for endpoint handlers
  • Binding handlers to their endpoints next to the code that defines the endpoints
  • Delaying initialization code execution until services are started allowing services that are not used to remain uninitialized
  • Reducing the cost of refactoring endpoint handlers passing data directly from producers to consumers without needing intermediaries to care about what is being passed
  • Avoid the overhead of verifying that requested items are present when passed indirectly (a common problem when using context.Context to pass data indirectly)

It does this by defining endpoints as a sequence of handler functions. Handler functions come in different flavors. Data is passed from one handler to the next using reflection to match the output types with input types requested later in the handler chain. To the extent possible, the handling is precomputed so there is very little reflection happening when the endpoint is actually invoked.

Endpoints are registered to services before or after the service is started.

When services are pre-registered and started later, it is possible to bind endpoints to them in init() functions and thus colocate the endpoint binding with the endpoint definition and spread the endpoint definition across multiple files and/or packages.

Services come in two flavors: one flavor binds endpoints with http.HandlerFunc and the other binds using mux.Router.

Example uses include:

  • Turning output structs into json
  • Common error handling
  • Common logging
  • Common initialization
  • Injection of resources and dependencies

Small Example

CreateEndpoint is the simplest way to start using the endpoint framework. It generates an http.HandlerFunc from a list of handlers. The handlers will be called in order. In the example below, first WriteErrorResponse() will be called. It has an inner() func that it uses to invoke the rest of the chain. When WriteErrorResponse() calls its inner() function, the db injector returned by InjectDB is called. If that does not return error, then the inline function below to handle the endpint is called.

mux := http.NewServeMux()
mux.HandleFunc("/my/endpoint", endpoint.CreateEndpoint(
	WriteErrorResponse,
	InjectDB("postgres", "postgres://..."),
	func(r *http.Request, db *sql.DB, w http.ResponseWriter) error {
		// Write response to w or return error...
		return nil
	}))

WriteErrorResponse invokes the remainder of the handler chain by calling inner().

func WriteErrorResponse(inner func() endpoint.TerminalError, w http.ResponseWriter) {
	err := inner()
	if err != nil {
		w.Write([]byte(err.Error()))
		w.WriteHeader(500)
	}
}

InjectDB returns a handler function that opens a database connection. If the open fails, executation of the handler chain is terminated.

func InjectDB(driver, uri string) func() (endpoint.TerminalError, *sql.DB) {
	return func() (endpoint.TerminalError, *sql.DB) {
		db, err := sql.Open(driver, uri)
		if err != nil {
			return err, nil
		}
		return nil, db
	}
}

# Functions

AnnotateCallsInner wraps middleware handlers and marks that the middleware handler as guaranteed to call inner().
AnnotateNotStatic wraps any handler func argument and marks that handler as not being a static injector.
AnnotateSideEffects wraps any handler func argument and marks that handler as having side effects.
AutoInject makes a promise that values will be provided to the service via service.Inject() before the service is started.
CreateEndpoint generates a http.HandlerFunc from a list of handlers.
GetTypeCode maps Go types to integers.
NewHandlerCollection creates a collection of handlers.
NewHandlerCollectionWithEndpoint creates a handler collection that includes an endpoint.
PreRegisterService creates a service that must be Start()ed later.
PreRegisterServiceWithMux creates a service that must be Start()ed later.
RegisterService creates a service and starts it immediately.
RegisterServiceWithMux creates a service and starts it immediately.

# Structs

No description provided by the author
EndpointRegistrationWithMux holds endpoint definitions for services that will be Start()ed with gorilla mux.
HandlerCollection provides a way to bundle up several handlers so that they can be refered to together as a set.
Service allows a group of related endpoints to be started together.
ServiceRegistration allows a group of related endpoints to be started together.
ServiceRegistrationWithMux allows a group of related endpoints to be started together.
ServiceWithMux allows a group of related endpoints to be started together.

# Interfaces

TerminalError is a standard error interface.

# Type aliases

EndpointBinder is the signature of the binding function used to start a ServiceRegistration.