Documentation ¶
Overview ¶
Package gouldian is Go combinator library for building HTTP services. The library is a thin layer of purely functional abstractions to building simple and declarative api implementations in the absence of pattern matching for traditional and serverless applications.
Inspiration ¶
The library is heavily inspired by Scala Finch https://github.com/finagle/finch.
Getting started ¶
Here is minimal "Hello World!" example that matches any HTTP requests to /hello endpoint.
package main import ( µ "github.com/fogfish/gouldian" "github.com/fogfish/gouldian/server/httpd" "net/http" ) func main() { http.ListenAndServe(":8080", httpd.Serve(hello()), ) } func hello() µ.Endpoint { return µ.GET( µ.URI(µ.Path("hello")), func(ctx µ.Context) error { return µ.Status.OK(µ.WithText("Hello World!")) }, ) }
See examples folder for advanced use-case.
Next steps ¶
↣ Study Endpoint type and its composition, see User Guide
↣ Check build-in collection of endpoints to deal with HTTP request. See types: HTTP, APIGateway
↣ Endpoint always returns some `Output` that defines HTTP response. There are three cases of output: HTTP Success, HTTP Failure and general error. See Output, Issue types.
Index ¶
- Constants
- Variables
- func FromContext[S any](ctx *Context, val *S) error
- func Optics2[T, A, B any](attr ...string) (Lens, Lens)
- func Optics3[T, A, B, C any](attr ...string) (Lens, Lens, Lens)
- func Optics4[T, A, B, C, D any](attr ...string) (Lens, Lens, Lens, Lens)
- func Optics5[T, A, B, C, D, E any](attr ...string) (Lens, Lens, Lens, Lens, Lens)
- func Optics6[T, A, B, C, D, E, F any](attr ...string) (Lens, Lens, Lens, Lens, Lens, Lens)
- func Optics7[T, A, B, C, D, E, F, G any](attr ...string) (Lens, Lens, Lens, Lens, Lens, Lens, Lens)
- func Optics8[T, A, B, C, D, E, F, G, H any](attr ...string) (Lens, Lens, Lens, Lens, Lens, Lens, Lens, Lens)
- func Optics9[T, A, B, C, D, E, F, G, H, I any](attr ...string) (Lens, Lens, Lens, Lens, Lens, Lens, Lens, Lens, Lens)
- type Context
- type Endpoint
- func Authorization(f func(string, string) error) Endpoint
- func Body(lens Lens) Endpoint
- func FMap[A any](f func(*Context, *A) error) Endpoint
- func Header[T MatchableHeaderValues](hdr string, val T) Endpoint
- func HeaderAny(hdr string) Endpoint
- func HeaderMaybe(header string, lens Lens) Endpoint
- func JWT[T Pattern](claim JWTClaim, val T) Endpoint
- func JWTAllOf(claim JWTClaim, vals ...string) Endpoint
- func JWTMaybe(claim JWTClaim, lens optics.Lens) Endpoint
- func JWTOneOf(claim JWTClaim, vals ...string) Endpoint
- func Join(seq ...Endpoint) Endpoint
- func Map[A, B any](f func(*Context, *A) (*B, error)) Endpoint
- func Method(verb string) Endpoint
- func Or(seq ...Endpoint) Endpoint
- func Param[T Pattern](key string, val T) Endpoint
- func ParamAny(key string) Endpoint
- func ParamJSON(key string, lens Lens) Endpoint
- func ParamMaybe(key string, lens Lens) Endpoint
- func ParamMaybeJSON(key string, lens Lens) Endpoint
- func Params(lens Lens) Endpoint
- type Endpoints
- type HeaderEnumConnection
- type HeaderEnumContent
- func (h HeaderEnumContent) Any(ctx *Context) error
- func (h HeaderEnumContent) ApplicationJSON(ctx *Context) error
- func (h HeaderEnumContent) Form(ctx *Context) error
- func (h HeaderEnumContent) HTML(ctx *Context) error
- func (h HeaderEnumContent) Is(value string) Endpoint
- func (h HeaderEnumContent) JSON(ctx *Context) error
- func (h HeaderEnumContent) Text(ctx *Context) error
- func (h HeaderEnumContent) TextHTML(ctx *Context) error
- func (h HeaderEnumContent) TextPlain(ctx *Context) error
- func (h HeaderEnumContent) To(lens Lens) Endpoint
- type HeaderEnumTransferEncoding
- func (h HeaderEnumTransferEncoding) Any(ctx *Context) error
- func (h HeaderEnumTransferEncoding) Chunked(ctx *Context) error
- func (h HeaderEnumTransferEncoding) Identity(ctx *Context) error
- func (h HeaderEnumTransferEncoding) Is(value string) Endpoint
- func (h HeaderEnumTransferEncoding) To(lens Lens) Endpoint
- type HeaderOf
- type Issue
- type JWTClaim
- type Lens
- type MatchableHeaderValues
- type NoMatch
- type Node
- type Output
- type Pattern
- type Query
- type ReadableHeaderValues
- type Result
- type Routable
- func ANY(path Routable, arrows ...Endpoint) Routable
- func DELETE(path Routable, arrows ...Endpoint) Routable
- func GET(path Routable, arrows ...Endpoint) Routable
- func HTTP(verb string, path Routable, arrows ...Endpoint) Routable
- func PATCH(path Routable, arrows ...Endpoint) Routable
- func POST(path Routable, arrows ...Endpoint) Routable
- func PUT(path Routable, arrows ...Endpoint) Routable
- func Route(path Routable, seq ...Endpoint) Routable
- func URI(segments ...Segment) Routable
- type Router
- type Segment
- type Token
Constants ¶
const ( Accept = HeaderEnumContent("Accept") AcceptCharset = HeaderOf[string]("Accept-Charset") AcceptEncoding = HeaderOf[string]("Accept-Encoding") AcceptLanguage = HeaderOf[string]("Accept-Language") CacheControl = HeaderOf[string]("Cache-Control") Connection = HeaderEnumConnection("Connection") ContentEncoding = HeaderOf[string]("Content-Encoding") ContentLength = HeaderOf[int]("Content-Length") ContentType = HeaderEnumContent("Content-Type") Cookie = HeaderOf[string]("Cookie") Date = HeaderOf[time.Time]("Date") From = HeaderOf[string]("From") Host = HeaderOf[string]("Host") IfMatch = HeaderOf[string]("If-Match") IfModifiedSince = HeaderOf[time.Time]("If-Modified-Since") IfNoneMatch = HeaderOf[string]("If-None-Match") IfRange = HeaderOf[string]("If-Range") IfUnmodifiedSince = HeaderOf[time.Time]("If-Unmodified-Since") Origin = HeaderOf[string]("Origin") Range = HeaderOf[string]("Range") Referer = HeaderOf[string]("Referer") TransferEncoding = HeaderEnumTransferEncoding("Transfer-Encoding") UserAgent = HeaderOf[string]("User-Agent") Upgrade = HeaderOf[string]("Upgrade") )
List of supported HTTP header constants https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields
const (
// Any constant matches any term
Any = "_"
)
Variables ¶
var ErrNoMatch error = NoMatch(255)
ErrNoMatch constant
var Sequence = guid.NewClock()
Sequence for generating unique identity
Functions ¶
func FromContext ¶
Get decodes context into structure
Types ¶
type Context ¶
type Context struct { context.Context Request *http.Request JWT Token // contains filtered or unexported fields }
Context of HTTP request. The context accumulates matched terms of HTTP and passes it to destination function.
func NewContext ¶
NewContext create a new context for HTTP request
type Endpoint ¶
Endpoint is a composable function that abstract HTTP endpoint. The function takes HTTP request and returns value of some type: `Context => Output`.
↣ `Context` is a wrapper over HTTP request with additional context.
↣ `Output` is sum type that represents if it is matched on a given input or not. The library uses `error` type to represent both valid and invalid variants.
Any `Endpoint A` can be composed with `Endpoint B` into new `Endpoint C`. It supports two combinators: and-then, or-else.
↣ Use `and-then` to build product Endpoint. The product type matches Input if each composed function successfully matches it.
↣ Use `or-else` to build co-product Endpoint. The co-product is also known as sum-type matches first successful function.
Endpoint life-cycle - each incoming HTTP request is wrapped with `Input` and applied to an endpoint. A returned error-like results is checked against successful Output or NoMatch error. All these machinery is handled by the libray, you should only dare to declare Endpoint from ready made primitives.
gouldian library delivers set of built-in endpoints to deal with HTTP request processing.
func Authorization ¶
Authorization defines Endpoints that simplify validation of credentials/tokens supplied within the request
e := µ.GET( µ.Authorization(func(string, string) error { ... }) ) e(mock.Input(mock.Header("Authorization", "Basic foo"))) == nil e(mock.Input(mock.Header("Authorization", "Basic bar"))) != nil
func FMap ¶
FMap applies clojure to matched HTTP request, taking the execution context as the input to closure
func Header ¶
func Header[T MatchableHeaderValues](hdr string, val T) Endpoint
Header combinator defines primitives to match Headers of HTTP requests.
endpoint := µ.GET( µ.Header("X-Foo", "Bar"), ) endpoint( mock.Input( mock.Header("X-Foo", "Bar") ) ) == nil
func HeaderAny ¶
HeaderAny is a wildcard matcher of header. It fails if header is not defined.
e := µ.GET( µ.HeaderAny("X-Foo") ) e(mock.Input(mock.Header("X-Foo", "Bar"))) == nil e(mock.Input(mock.Header("X-Foo", "Baz"))) == nil e(mock.Input()) != nil
func HeaderMaybe ¶
HeaderMaybe matches header value to the request context. It uses lens abstraction to decode HTTP header into Golang type. The Endpoint does not cause no-match if header value cannot be decoded to the target type. See optics.Lens type for details.
type myT struct{ Val string } x := µ.Optics1[myT, string]() e := µ.GET(µ.HeaderMaybe("X-Foo", x)) e(mock.Input(mock.Header("X-Foo", "Bar"))) == nil
func JWT ¶
JWT combinator defines primitives to match JWT token in the HTTP requests.
endpoint := µ.GET( µ.JWT(µ.Token.Username, "joedoe"), ) endpoint( mock.Input( mock.JWT(µ.Token{"username": "joedoe"}) ) ) == nil
func JWTAllOf ¶
JWTAllOf matches a key of JWT if it contains one of the tokens
µ.GET( µ.JWTAllOf(µ.JWT.Scope, "ro", "rw") )
func JWTMaybe ¶
JWTMaybe matches key of JWT to the request context. It uses lens abstraction to decode value into Golang type. The Endpoint does not cause no-match if header value cannot be decoded to the target type. See optics.Lens type for details.
type MyT struct{ Username string } username := µ.Optics1[MyT, string]() e := µ.GET( µ.JWTMaybe(µ.JWT.Sub).Maybe(username) ) e(mock.Input(mock.JWT(µ.JWT{"username": "joedoe"}))) == nil
func JWTOneOf ¶
JWTOneOf matches a key of JWT if it contains one of the tokens
µ.GET( µ.JWTOneOf(µ.JWT.Scope, "ro", "rw") )
func Map ¶
Map applies clojure to matched HTTP request, taking the execution context and matched parameters as the input to closure. The output is always returned as JSON.
func Param ¶
Param combinator defines primitives to match query param in the HTTP requests.
endpoint := µ.GET( µ.Param("foo", "bar"), ) endpoint( mock.Input( mock.URL("/?foo=bar") ) ) == nil
func ParamAny ¶
ParamAny is a wildcard matcher of param key. It fails if key is not defined.
e := µ.GET( µ.ParamAny("foo") ) e(mock.Input(mock.URL("/?foo"))) == nil e(mock.Input(mock.URL("/?foo=bar"))) == nil e(mock.Input(mock.URL("/?foo=baz"))) == nil e(mock.Input()) != nil
func ParamJSON ¶
JSON matches a param key to struct. It assumes that key holds JSON value as url encoded string
func ParamMaybe ¶
ParamMaybe matches param value to the request context. It uses lens abstraction to decode value into Golang type. The Endpoint does not cause no-match if header value cannot be decoded to the target type. See optics.Lens type for details.
type myT struct{ Val string } x := µ.Optics1[myT, string]() e := µ.GET( µ.ParamMaybe("foo", x) ) e(mock.Input(mock.URL("/?foo=bar"))) == nil e(mock.Input(mock.URL("/?foo"))) == nil e(mock.Input(mock.URL("/"))) == nil
func ParamMaybeJSON ¶
MaybeJSON matches a param key to closed struct. It assumes that key holds JSON value as url encoded string. It does not fail if key is not defined.
type Endpoints ¶
type Endpoints []Endpoint
Endpoints is sequence of Endpoints
type HeaderEnumConnection ¶
type HeaderEnumConnection string
Type of HTTP Header, Connection enumeration
const Connection = HeaderEnumConnection("Connection") µ.Connection.KeepAlive
func (HeaderEnumConnection) Any ¶
func (h HeaderEnumConnection) Any(ctx *Context) error
Matches header to any value
func (HeaderEnumConnection) Close ¶
func (h HeaderEnumConnection) Close(ctx *Context) error
Close defines header `???: close`
func (HeaderEnumConnection) Is ¶
func (h HeaderEnumConnection) Is(value string) Endpoint
Matches value of HTTP header
func (HeaderEnumConnection) KeepAlive ¶
func (h HeaderEnumConnection) KeepAlive(ctx *Context) error
KeepAlive defines header `???: keep-alive`
func (HeaderEnumConnection) To ¶
func (h HeaderEnumConnection) To(lens Lens) Endpoint
Matches value of HTTP header
type HeaderEnumContent ¶
type HeaderEnumContent string
Type of HTTP Header, Content-Type enumeration
const ContentType = HeaderEnumContent("Content-Type") µ.ContentType.JSON
func (HeaderEnumContent) Any ¶
func (h HeaderEnumContent) Any(ctx *Context) error
Matches header to any value
func (HeaderEnumContent) ApplicationJSON ¶
func (h HeaderEnumContent) ApplicationJSON(ctx *Context) error
ApplicationJSON defines header `???: application/json`
func (HeaderEnumContent) Form ¶
func (h HeaderEnumContent) Form(ctx *Context) error
Form defined Header `???: application/x-www-form-urlencoded`
func (HeaderEnumContent) HTML ¶
func (h HeaderEnumContent) HTML(ctx *Context) error
HTML defined Header `???: text/html`
func (HeaderEnumContent) Is ¶
func (h HeaderEnumContent) Is(value string) Endpoint
Matches value of HTTP header
func (HeaderEnumContent) JSON ¶
func (h HeaderEnumContent) JSON(ctx *Context) error
JSON defines header `???: application/json`
func (HeaderEnumContent) Text ¶
func (h HeaderEnumContent) Text(ctx *Context) error
Text defined Header `???: text/plain`
func (HeaderEnumContent) TextHTML ¶
func (h HeaderEnumContent) TextHTML(ctx *Context) error
TextHTML defined Header `???: text/html`
func (HeaderEnumContent) TextPlain ¶
func (h HeaderEnumContent) TextPlain(ctx *Context) error
TextPlain defined Header `???: text/plain`
func (HeaderEnumContent) To ¶
func (h HeaderEnumContent) To(lens Lens) Endpoint
Matches value of HTTP header
type HeaderEnumTransferEncoding ¶
type HeaderEnumTransferEncoding string
Type of HTTP Header, Transfer-Encoding enumeration
const TransferEncoding = HeaderEnumTransferEncoding("Transfer-Encoding") µ.TransferEncoding.Chunked
func (HeaderEnumTransferEncoding) Any ¶
func (h HeaderEnumTransferEncoding) Any(ctx *Context) error
Matches header to any value
func (HeaderEnumTransferEncoding) Chunked ¶
func (h HeaderEnumTransferEncoding) Chunked(ctx *Context) error
Chunked defines header `Transfer-Encoding: chunked`
func (HeaderEnumTransferEncoding) Identity ¶
func (h HeaderEnumTransferEncoding) Identity(ctx *Context) error
Identity defines header `Transfer-Encoding: identity`
func (HeaderEnumTransferEncoding) Is ¶
func (h HeaderEnumTransferEncoding) Is(value string) Endpoint
Matches value of HTTP header
func (HeaderEnumTransferEncoding) To ¶
func (h HeaderEnumTransferEncoding) To(lens Lens) Endpoint
Matches value of HTTP header
type HeaderOf ¶
type HeaderOf[T MatchableHeaderValues] string
Internal type
type Issue ¶
type Issue struct { ID string `json:"instance"` Type string `json:"type"` Status int `json:"status"` Title string `json:"title"` }
Issue implements RFC 7807: Problem Details for HTTP APIs
type MatchableHeaderValues ¶
type MatchableHeaderValues interface { ReadableHeaderValues | Lens }
type Node ¶
type Node struct { Path string // substring from the route "owned" by the node Heir []*Node // heir nodes Func Endpoint // end point associated with node }
Node of trie
type Output ¶
type Output struct { Status int Headers []struct{ Header, Value string } Body string Failure error }
Output
type Result ¶
Result is a composable function that abstract results of HTTP endpoint. The function takes instance of HTTP output and mutates its value
return µ.Status.OK( µ.WithHeader(headers.ContentType, headers.ApplicationJson), µ.WithJSON(value), )
type Routable ¶
Routable is endpoint with routing metadata
func ANY ¶
ANY composes Endpoints into Routable that matches HTTP any request.
e := µ.ANY( µ.URI(µ.Path("foo"), µ.Path("bar")), ... ) e(mock.Input(mock.Method("PUT"))) == nil e(mock.Input(mock.Method("OTHER"))) == nil
func DELETE ¶
DELETE composes Endpoints into Routable that matches HTTP DELETE request.
e := µ.DELETE( µ.URI(µ.Path("foo"), µ.Path("bar")), ... ) e(mock.Input(mock.Method("DELETE"))) == nil e(mock.Input(mock.Method("OTHER"))) != nil
func GET ¶
GET composes Endpoints into Routable that matches HTTP GET request.
e := µ.GET( µ.URI(µ.Path("foo"), µ.Path("bar")), ... ) e(mock.Input(mock.Method("GET"))) == nil e(mock.Input(mock.Method("OTHER"))) != nil
func PATCH ¶
PATCH composes Endpoints into Routable that matches HTTP PATCH request.
e := µ.PATCH( µ.URI(µ.Path("foo"), µ.Path("bar")), ... ) e(mock.Input(mock.Method("PATCH"))) == nil e(mock.Input(mock.Method("OTHER"))) != nil
func POST ¶
POST composes Endpoints into Routable that matches HTTP POST request.
e := µ.POST( µ.URI(µ.Path("foo"), µ.Path("bar")), ... ) e(mock.Input(mock.Method("POST"))) == nil e(mock.Input(mock.Method("OTHER"))) != nil
func PUT ¶
PUT composes Endpoints into Routable that matches HTTP PUT request.
e := µ.PUT( µ.URI(µ.Path("foo"), µ.Path("bar")), ... ) e(mock.Input(mock.Method("PUT"))) == nil e(mock.Input(mock.Method("OTHER"))) != nil
func URI ¶
URI is an endpoint to match URL of HTTP request. The function takes a sequence of segment patterns as input. These patterns are either literals or lenses, where each term corresponds to the path segment. The function do not match if length of path is not equal to the length of pattern or segment do not match to pattern
e := µ.GET( µ.URI(µ.Path("foo")) ) e(mock.Input(mock.URL("/foo"))) == nil e(mock.Input(mock.URL("/bar"))) != nil
type Router ¶
type Router interface {
Endpoint() Endpoint
}
Router is data structure that holds routing information, convertable to Endpoint
type Segment ¶
type Segment struct {
// contains filtered or unexported fields
}
Segment union type, make URI type safe
func Path ¶
Path is an endpoint to match a single URL segment of HTTP request. The function takes a path pattern as arguments. The pattern is either literal or lens. The function do not match if segment do not match to pattern
e := µ.GET( µ.URI(µ.Path("foo")) ) e(mock.Input(mock.URL("/foo"))) == nil e(mock.Input(mock.URL("/bar"))) != nil