rest

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: May 28, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package rest provides a transport-agnostic REST API builder for go-codex.

Define routes with codec-backed request and response types; the builder returns a RouteHandle with typed Decode and Encode helpers. Pass those helpers to any HTTP framework (net/http, Gin, Chi, Echo) — this package does not import net/http or any framework.

Spec generation is also available: Builder.OpenAPISpec derives a complete OpenAPI 3.1 document from the registered routes.

Typical usage:

b := rest.NewBuilder(rest.Info{Title: "User API", Version: "1.0.0"})
b.AddServer("production", rest.Server{URL: "https://api.example.com"})

createUser := rest.AddRoute[CreateUserReq, User](b, "POST", "/users",
    createUserCodec, userCodec, rest.RouteConfig{
        OperationID:    "createUser",
        Summary:        "Create a user",
        ReqSchemaName:  "CreateUserRequest",
        RespSchemaName: "User",
    })

// In your HTTP handler (any framework):
req, err := createUser.Decode(body)      // JSON → CreateUserReq, validates
user, err := myService.CreateUser(req)
out, err  := createUser.Encode(user)     // User → JSON

// OpenAPI 3.1 spec:
doc, err := b.OpenAPISpec()
yaml, _  := doc.MarshalYAML()

Encoding is JSON only. AddRoute uses format.JSON internally; for other formats construct a format.Format directly and call its Unmarshal/Marshal.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Builder

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

Builder accumulates route registrations and produces OpenAPI specs. Create one with NewBuilder.

func NewBuilder

func NewBuilder(info Info, opts ...BuilderOption) *Builder

NewBuilder returns a Builder initialised with the given API metadata.

func (*Builder) AddSchema

func (b *Builder) AddSchema(name string, s schema.Schema) *Builder

AddSchema registers a named schema in components/schemas. Use this to register reusable schemas (e.g. shared error types) that are referenced by SchemaName in route configs but not inlined in any codec.

func (*Builder) AddServer

func (b *Builder) AddServer(name string, s Server) *Builder

AddServer appends a named server entry to the spec. name is used as the server's Description if s.Description is empty, making it consistent with [events.Builder.AddServer].

func (*Builder) OpenAPISpec

func (b *Builder) OpenAPISpec() (openapi.Document, error)

OpenAPISpec builds a complete OpenAPI 3.1 document from all registered routes. Returns an error if any non-empty SchemaName references a schema that will not be present in components/schemas (a dangling $ref).

type BuilderOption added in v0.4.0

type BuilderOption func(*Builder)

BuilderOption configures a Builder at construction time.

func WithPathCodec added in v0.4.0

func WithPathCodec(c codex.Codec[string]) BuilderOption

WithPathCodec sets a codec used to validate every path passed to AddRoute. If the path is invalid, AddRoute returns an error immediately.

Use WithPathConstraints for the common case of stacking one or more codex.Constraint values; use WithPathCodec when you need a fully-custom codex.Codec.

Example — enforce HTTP path rules:

import "github.com/DaniDeer/go-codex/validate"

b := rest.NewBuilder(info, rest.WithPathConstraints(validate.HTTPPath))

func WithPathConstraints added in v0.4.0

func WithPathConstraints(cons ...codex.Constraint[string]) BuilderOption

WithPathConstraints is a convenience wrapper around WithPathCodec that builds a codec from codex.String refined with the given constraints. Multiple constraints are applied in order; all must pass.

Users can mix built-in constraints from the validate package with their own:

sensorPrefix := codex.Constraint[string]{
    Name:    "sensor-prefix",
    Check:   func(v string) bool { return strings.HasPrefix(v, "/sensors/") },
    Message: func(v string) string { return fmt.Sprintf("path must start with /sensors/, got %q", v) },
}
b := rest.NewBuilder(info, rest.WithPathConstraints(validate.HTTPPath, sensorPrefix))

type Info

type Info = openapi.Info

Info is an alias for openapi.Info. Using the alias avoids duplicating fields and keeps the two in sync automatically.

type InvalidPathError added in v0.4.0

type InvalidPathError struct {
	Path string // the path that failed validation
	Err  error  // the underlying constraint or codec error
}

InvalidPathError is returned by AddRoute when the path fails builder-level path codec validation.

Use errors.As to extract it and inspect the failing path or the underlying constraint error:

var pathErr rest.InvalidPathError
if errors.As(err, &pathErr) {
    log.Printf("bad path %q: %v", pathErr.Path, pathErr.Err)
}

func (InvalidPathError) Error added in v0.4.0

func (e InvalidPathError) Error() string

func (InvalidPathError) Unwrap added in v0.4.0

func (e InvalidPathError) Unwrap() error

Unwrap allows errors.As and errors.Is to traverse the underlying constraint error.

type InvalidPathParamError added in v0.6.0

type InvalidPathParamError struct {
	Name string // the variable name (without braces) that is not in the template
	Path string // the path template that was validated against
}

InvalidPathParamError is returned by AddRoute when a PathParam entry names a variable that does not appear in the path template.

Use errors.As to extract the offending name and the path template:

var paramErr rest.InvalidPathParamError
if errors.As(err, &paramErr) {
    log.Printf("PathParam %q not in path %q", paramErr.Name, paramErr.Path)
}

func (InvalidPathParamError) Error added in v0.6.0

func (e InvalidPathParamError) Error() string

type MissingPathVarError added in v0.4.0

type MissingPathVarError struct {
	Name string // the variable name (without braces) that had no value
}

MissingPathVarError is returned by RouteHandle.BuildPath when a {varName} placeholder in the path template has no corresponding entry in the vars map.

Use errors.As to extract the missing variable name:

var missingErr rest.MissingPathVarError
if errors.As(err, &missingErr) {
    log.Printf("caller forgot to supply path variable {%s}", missingErr.Name)
}

func (MissingPathVarError) Error added in v0.4.0

func (e MissingPathVarError) Error() string

type Param

type Param = route.Param

Param is an alias for route.Param so callers do not need to import route just to specify query parameters.

type PathParam added in v0.6.0

type PathParam struct {
	Name        string
	Description string
	// Codec validates substituted values at [RouteHandle.BuildPath] time.
	// When non-nil, the codec's schema is also used in the OpenAPI spec.
	// Nil means no runtime validation; the spec schema will be empty.
	Codec *codex.Codec[string]
}

PathParam describes a {varName} placeholder in a route path template. It combines spec metadata with optional runtime validation via a codec.

type PathParamError added in v0.4.0

type PathParamError struct {
	Name  string // variable name without braces, e.g. "id"
	Value string // the value that failed validation
	Err   error  // the underlying constraint or codec error
}

PathParamError is returned by RouteHandle.BuildPath when a path variable value fails codec validation.

Use errors.As to extract the failing variable name and value:

var paramErr rest.PathParamError
if errors.As(err, &paramErr) {
    log.Printf("bad value for {%s}: %q — %v", paramErr.Name, paramErr.Value, paramErr.Err)
}

func (PathParamError) Error added in v0.4.0

func (e PathParamError) Error() string

func (PathParamError) Unwrap added in v0.4.0

func (e PathParamError) Unwrap() error

Unwrap allows errors.As and errors.Is to traverse the underlying constraint error.

type ResponseMeta

type ResponseMeta struct {
	Status      string // e.g. "400", "404", "default"
	Description string
	Schema      *schema.Schema // nil for description-only responses (e.g. 404)
	SchemaName  string         // non-empty → $ref in spec
}

ResponseMeta describes one additional response entry for a route (errors, redirects, etc.). The primary success response is derived from the response codec and RespStatus/RespDescription/RespSchemaName in RouteConfig.

type RouteConfig

type RouteConfig struct {
	OperationID string
	Summary     string
	Description string
	Tags        []string

	// PathParams describes {varName} placeholder variables in the path template.
	// Each entry can add a description and/or a codec for runtime validation.
	// The codec schema is also used in the OpenAPI path parameter spec.
	//
	// PathParams is optional: the builder auto-generates a minimal parameter
	// entry for every {varName} in the path. Only specify PathParams when you
	// need a description or runtime validation for a specific variable.
	//
	// Entry names must correspond to {varName} placeholders in the path template;
	// unknown names cause [AddRoute] to return an error immediately.
	PathParams []PathParam

	// QueryParams are included in the OpenAPI spec as query parameters.
	QueryParams []route.Param

	// ReqSchemaName, when non-empty, emits a $ref for the request body schema
	// in the spec and registers the schema under that name in components/schemas.
	ReqSchemaName string

	// RespStatus is the HTTP status code for the primary success response.
	// Defaults to "201" for POST, "200" for all other methods.
	RespStatus string

	// RespDescription is the description for the primary success response.
	RespDescription string

	// RespSchemaName, when non-empty, emits a $ref for the response schema.
	RespSchemaName string

	// Responses are additional response entries (error codes, etc.) appended
	// after the primary success response in the spec.
	Responses []ResponseMeta
}

RouteConfig holds metadata for a route registration. It controls both spec output and default behaviour of the returned RouteHandle.

type RouteHandle

type RouteHandle[Req, Resp any] struct {
	// Descriptor is the frozen route.Route built at registration time.
	// Use it to inspect method, path, parameters, and spec metadata.
	Descriptor route.Route

	// Decode deserialises and validates a JSON request body into Req.
	// All Refine constraints on the request codec run automatically.
	Decode func(body []byte) (Req, error)

	// Encode serialises Resp to JSON bytes.
	Encode func(resp Resp) ([]byte, error)
	// contains filtered or unexported fields
}

RouteHandle is returned by AddRoute. It holds the frozen spec descriptor and codec-backed Decode/Encode helpers.

Decode and Encode use JSON encoding. For body-less methods (GET, HEAD, DELETE), Decode can still be called if the request carries a body, but typical REST usage will not call it.

func AddRoute

func AddRoute[Req, Resp any](
	b *Builder,
	method, path string,
	reqCodec codex.Codec[Req],
	respCodec codex.Codec[Resp],
	config RouteConfig,
) (*RouteHandle[Req, Resp], error)

AddRoute registers a route with the builder and returns a RouteHandle.

reqCodec is used to decode and validate the JSON request body. respCodec is used to encode the JSON response.

If the builder was created with WithPathCodec or WithPathConstraints, the path is validated immediately. An error is returned if validation fails — no route is registered in that case.

If config.PathParams is non-empty, each entry name is verified to be a {varName} present in the path template. An unknown name is a programming error and causes AddRoute to return an error.

AddRoute is a free function (not a method) because Go requires type parameters to appear on free functions, not on method receivers.

The descriptor is built and frozen at call time; later mutations to config do not affect the registered route or the returned handle.

func (*RouteHandle[Req, Resp]) BuildPath added in v0.4.0

func (h *RouteHandle[Req, Resp]) BuildPath(vars map[string]string) (string, error)

BuildPath substitutes {varName} placeholders in the route's path template with the values provided in vars, validating each against its registered codec (if any).

All template variables must be present in vars; missing variables return an error. Values are validated before substitution; codec failures return a PathParamError that identifies the variable name and the failing value. Keys in vars that do not appear in the template are silently ignored.

If the builder was created with WithPathCodec or WithPathConstraints, the final assembled path is also validated against that codec. A failure returns an InvalidPathError with the concrete path (not the template).

Example:

path, err := getUserRoute.BuildPath(map[string]string{"id": "f47ac10b-..."})
// path = "/users/f47ac10b-..."

type Server

type Server = openapi.Server

Server is an alias for openapi.Server.

Jump to

Keyboard shortcuts

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