rest

package module
v0.24.0 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package rest builds type-safe HTTP APIs with OpenAPI documentation generated from the same declarations that wire up the handlers — no separate codegen step, no struct tags to keep in sync.

A Controller groups Routes under a path prefix, tag, and guards. Each Route declares its OpenAPI doc (summary, parameters, request body, responses) and a Handler. ReflectParameter/ReflectResponse/ReflectRequestBody derive the OpenAPI schema straight from a Go type via oas.ReflectType, so entity structs don't need to be described twice.

Errors are mapped to HTTP status codes declaratively with ErrorOf (sentinel, matched via errors.Is) and ErrorAs (typed, matched via errors.As), at three layers — global (MapErrorOf/MapErrorAs), Controller.MapError, and Route.MapError — resolved most-specific first. A typed error's own json-tagged fields are merged into both the response body and the auto-generated OpenAPI schema for its status code.

rest itself is HTTP-engine-agnostic: Adapter is the interface an HTTP engine implements to serve a Server's controllers. The reference implementation is Fiber, in the adapter/fiber subpackage; other engines (Gin, Echo, net/http) can implement Adapter the same way.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func JoinPath

func JoinPath(prefix, path string) string

JoinPath joins a controller's path prefix with a route's path, normalizing slashes. Adapters use this to register routes on the underlying engine — paths keep the engine's own param syntax (e.g. Fiber's ":id").

func MapErrorAs

func MapErrorAs[T error](status int, description ...string)

MapErrorAs registers a global typed error->status mapping (errors.As).

func MapErrorOf

func MapErrorOf(err error, status int, description ...string)

MapErrorOf registers a global sentinel error->status mapping (errors.Is). It's the default for every route that doesn't declare its own override via Controller.MapError/Route.MapError.

func MustBodyJson

func MustBodyJson[T any](ctx Context, key string) T

MustBodyJson decodes the request body as JSON into T. If key is non-empty, it's treated as a top-level field name and only that sub-value is decoded into T; an empty key decodes the whole body.

func MustHeader

func MustHeader[T any](ctx Context, name string, optional ...T) T

MustHeader extracts and converts a header to T, panicking if it's missing (unless an optional default value is given) or can't be converted.

func MustParam

func MustParam[T any](ctx Context, name string, optional ...T) T

MustParam extracts and converts a path parameter to T, panicking if it's missing (unless an optional default value is given) or can't be converted.

func MustQuery

func MustQuery[T any](ctx Context, name string, optional ...T) T

MustQuery extracts and converts a query parameter to T, panicking if it's missing (unless an optional default value is given) or can't be converted.

Types

type Adapter

type Adapter interface {
	// Bind wires every controller/route from server onto the underlying engine.
	Bind(server *Server) error
	// Listen starts serving on address.
	Listen(address string) error
}

Adapter binds a Server's controllers/routes onto a concrete HTTP engine. Fiber ships as the reference implementation (github.com/leandroluk/gox/rest/adapter/fiber); Gin/Echo/Mux/etc can plug in later by implementing the same interface.

type Context

type Context interface {
	RawContext
	// Response starts building the HTTP response for this request.
	Response() *Response
}

Context is the adapter-agnostic request context passed to handlers and guards.

func NewContext

func NewContext(route *Route, raw RawContext) Context

NewContext wraps a RawContext (produced by an adapter) into a rest.Context bound to route, so Response().Error(err) can resolve the layered error registry (route -> controller -> global).

type Controller

type Controller struct {
	PathPrefix string

	Routes []*Route
	// contains filtered or unexported fields
}

Controller groups routes under a common path prefix, tag, guards, and error-mapping defaults.

func NewController

func NewController(build func(*Controller)) *Controller

NewController builds a Controller via the given configuration callback.

func (*Controller) ApiBearerAuth

func (c *Controller) ApiBearerAuth() *Controller

ApiBearerAuth marks every route in this controller as requiring bearer auth.

func (*Controller) ApiTag

func (c *Controller) ApiTag(value string) *Controller

ApiTag sets the OpenAPI tag for every route in this controller.

func (*Controller) GuardList

func (c *Controller) GuardList() []*Guard

GuardList exposes this controller's guards, run before every route's own, for adapters.

func (*Controller) MapError

func (c *Controller) MapError(mapping ErrorMapping) *Controller

MapError registers an error->status mapping shared by every route in this controller that doesn't declare its own override via Route.MapError.

func (*Controller) Path

func (c *Controller) Path(value string) *Controller

Path sets the base path prefix for every route in this controller.

func (*Controller) Route

func (c *Controller) Route(method, path string, build func(*Route)) *Controller

Route declares a new route under this controller.

func (*Controller) UseGuard

func (c *Controller) UseGuard(guards ...*Guard) *Controller

UseGuard attaches guards run before every route in this controller.

type ErrorMapping

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

ErrorMapping is a declarative error->status mapping built via ErrorOf or ErrorAs. Register it globally with MapErrorOf/MapErrorAs, or scope it to a single Controller/Route via their MapError method (Go doesn't allow generic methods, so ErrorAs[T] has to stay a package-level function; MapError just stores the value it returns).

func ErrorAs

func ErrorAs[T error](status int, description ...string) ErrorMapping

ErrorAs builds a typed error mapping, matched at resolution time via errors.As. T must be the concrete type carried by the error chain (typically a pointer, e.g. *ValidationError, mirroring what you'd pass to errors.As yourself).

func ErrorOf

func ErrorOf(err error, status int, description ...string) ErrorMapping

ErrorOf builds a sentinel error mapping, matched at resolution time via errors.Is.

type Guard

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

Guard runs before a route's handler; a non-nil Response short-circuits the request (e.g. 401 on missing auth) without reaching the handler.

func NewGuard

func NewGuard(build func(*GuardBuilder)) *Guard

NewGuard builds a Guard via the given configuration callback.

func (*Guard) Run

func (g *Guard) Run(ctx Context) *Response

Run executes the guard; a non-nil Response means the request was rejected.

type GuardBuilder

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

GuardBuilder configures a Guard.

func (*GuardBuilder) Handler

func (b *GuardBuilder) Handler(fn func(Context) *Response) *GuardBuilder

Handler sets the function that runs for this guard.

type Parameter

type Parameter = oas.Parameter

Parameter is a plain alias to oas.Parameter.

func ReflectParameter

func ReflectParameter[T any](name string) *Parameter

ReflectParameter builds a Parameter for T named name, with its schema derived from T's shape (via oas.ReflectType — same reflection already used for entity schemas). Chain .InPath()/.InQuery()/etc and .Description() on the result.

type RawContext

type RawContext interface {
	// Param returns a path parameter's raw value, and whether it was present.
	Param(name string) (string, bool)
	// Query returns a query parameter's raw value, and whether it was present.
	Query(name string) (string, bool)
	// Header returns a request header's raw value, and whether it was present.
	Header(name string) (string, bool)
	// Body returns the raw request body.
	Body() []byte
	// Ctx returns the request's context.Context, for cancellation/deadline
	// propagation into downstream calls (DB queries, usecases, etc).
	Ctx() context.Context
}

RawContext is implemented by adapters (one per HTTP engine — Fiber at minimum) to expose the underlying request. rest builds Context and error resolution on top of it, so handlers/guards never touch the engine directly.

type RequestBody

type RequestBody = oas.RequestBody

RequestBody is a plain alias to oas.RequestBody.

func ReflectRequestBody

func ReflectRequestBody[T any]() *RequestBody

ReflectRequestBody builds a RequestBody whose JSON schema is derived from T's shape.

type Response

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

Response is a dual-purpose builder. At doc-declaration time (route.ApiResponse, ReflectResponse) it configures OpenAPI metadata (Description/ContentTypeJson/ Schema/Example). At request time (returned from a Handler) the same type carries the actual HTTP status/body to send — Status/JSON/Error.

func Recover

func Recover(ctx Context, recovered any) *Response

Recover converts a recovered panic value into a Response via the same error-mapping path as Error(err): if recovered is an error, it's resolved against the registry as usual; otherwise it's wrapped so it still falls back to 500 with a {"message": ...} body instead of crashing the request.

Adapters call this from their own recover(); it's here (not per-adapter) so every engine gets identical panic->response behavior. This exists because MustParam/MustBodyJson/MustHeader/MustQuery panic by design, and callers commonly propagate domain errors via panic too (e.g. panic(ErrNotFound) from inside a usecase), relying on the HTTP layer to recover and map them.

func ReflectResponse

func ReflectResponse[T any]() *Response

ReflectResponse builds a doc Response whose schema is derived from T's shape.

func (*Response) ContentTypeJson

func (r *Response) ContentTypeJson() *Response

ContentTypeJson marks this response's content type as application/json.

func (*Response) Description

func (r *Response) Description(value string) *Response

Description sets the OpenAPI description for this response.

func (*Response) Error

func (r *Response) Error(err error) *Response

Error resolves err against the route/controller/global error registry (most specific first) and sets status + {"message": ...} body accordingly, falling back to 500 if nothing matches. An explicit prior call to Status always wins over the resolved mapping.

func (*Response) Example

func (r *Response) Example(value any) *Response

Example sets the OpenAPI example for this response's body.

func (*Response) HTTPBody

func (r *Response) HTTPBody() any

HTTPBody returns the response body to be serialized by the adapter.

func (*Response) HTTPStatus

func (r *Response) HTTPStatus() int

HTTPStatus returns the resolved HTTP status code, defaulting to 200.

func (*Response) JSON

func (r *Response) JSON(value any) *Response

JSON sets the response body, sent as JSON.

func (*Response) Schema

func (r *Response) Schema(build func(*oas.Schema)) *Response

Schema configures the OpenAPI schema for this response's body.

func (*Response) Status

func (r *Response) Status(code int) *Response

Status sets the HTTP status code returned to the client. Calling it explicitly takes priority over whatever Error(err) would otherwise resolve.

type Route

type Route struct {
	Method string
	Path   string
	// contains filtered or unexported fields
}

Route describes a single HTTP endpoint: its OpenAPI documentation, guards, error-mapping overrides, and the handler that actually serves it.

func (*Route) ApiDescription

func (r *Route) ApiDescription(value string) *Route

ApiDescription sets the OpenAPI description for this route.

func (*Route) ApiParameter

func (r *Route) ApiParameter(p *Parameter) *Route

ApiParameter documents a single request parameter (path/query/header/cookie).

func (*Route) ApiRequestBody

func (r *Route) ApiRequestBody(rb *RequestBody) *Route

ApiRequestBody documents the request body.

func (*Route) ApiResponse

func (r *Route) ApiResponse(status int, arg any) *Route

ApiResponse declares (or overrides) the OpenAPI doc for a status code. arg accepts either a pre-built *Response (e.g. from ReflectResponse) or a builder callback func(*Response) — Go has no method overloading, so this mirrors ApiRequestBody/ApiParameter's "give me a built value" style while still allowing the inline-callback style used for one-off responses.

func (*Route) ApiSummary

func (r *Route) ApiSummary(value string) *Route

ApiSummary sets the OpenAPI summary for this route.

func (*Route) GuardList

func (r *Route) GuardList() []*Guard

GuardList exposes this route's own guards (run after the controller's) for adapters.

func (*Route) Handler

func (r *Route) Handler(fn func(Context) *Response) *Route

Handler sets the function that serves this route.

func (*Route) MapError

func (r *Route) MapError(mapping ErrorMapping) *Route

MapError registers an error->status mapping scoped to this route only, taking priority over the controller and global registries. Build the mapping with ErrorOf (sentinel, errors.Is) or ErrorAs[T] (typed, errors.As).

func (*Route) Serve

func (r *Route) Serve(ctx Context) *Response

Serve invokes this route's handler, if set.

func (*Route) UseGuard

func (r *Route) UseGuard(guards ...*Guard) *Route

UseGuard attaches guards to this route only, run after the controller's own guards.

type Schema

type Schema = oas.Schema

Schema is a plain alias to oas.Schema — rest doesn't need its own copy of the OpenAPI schema builder.

type Server

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

Server holds the registered controllers and the adapter(s) that serve them.

func NewServer

func NewServer(build func(*Server)) *Server

NewServer builds a Server via the given configuration callback.

func (*Server) Adapter

func (s *Server) Adapter(a Adapter) *Server

Adapter registers and binds an Adapter to this server's controllers.

func (*Server) ControllerList

func (s *Server) ControllerList() []*Controller

ControllerList exposes registered controllers for adapters to walk.

func (*Server) Controllers

func (s *Server) Controllers(controllers []*Controller) *Server

Controllers registers the given controllers with the server.

func (*Server) Document

func (s *Server) Document() *oas.Document

Document builds (lazily, once) the OpenAPI document describing every registered controller/route, auto-generating a response entry for every mapped error not already documented manually via ApiResponse.

func (*Server) Listen

func (s *Server) Listen(address string) error

Listen starts every registered adapter, returning the first error.

func (*Server) OAS

func (s *Server) OAS(fn func(*oas.Document)) *Server

OAS configures the root OpenAPI document (info, servers, security schemes, ...).

Directories

Path Synopsis
adapter
fiber
Package fiber is the reference rest.Adapter implementation, wiring rest.Server's controllers/routes onto a github.com/gofiber/fiber/v2 App.
Package fiber is the reference rest.Adapter implementation, wiring rest.Server's controllers/routes onto a github.com/gofiber/fiber/v2 App.

Jump to

Keyboard shortcuts

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