Documentation
¶
Overview ¶
Package rocco provides a type-safe HTTP framework for Go with automatic OpenAPI generation.
Rocco enables building HTTP APIs with compile-time type safety, automatic request validation, and OpenAPI 3.1.0 specification generation from your Go types.
Core Types ¶
The framework is built around these primary types:
- Engine: HTTP server managing routing, middleware, and handler registration
- Handler: Type-safe request handler with generic input/output types
- StreamHandler: Handler for Server-Sent Events (SSE) streaming
- Request: Typed request container with body, parameters, and identity
- Error: Structured API error with typed details for OpenAPI generation
Creating an Engine ¶
Create an engine:
engine := rocco.NewEngine()
For authenticated APIs, configure an identity extractor:
engine := rocco.NewEngine().WithAuthenticator(func(ctx context.Context, r *http.Request) (rocco.Identity, error) {
// Extract identity from request (e.g., JWT token)
return identity, nil
})
Defining Handlers ¶
Handlers are generic over input and output types:
handler := rocco.NewHandler[CreateUserInput, UserOutput](
"create-user",
"POST",
"/users",
func(req *rocco.Request[CreateUserInput]) (UserOutput, error) {
return UserOutput{ID: "123", Name: req.Body.Name}, nil
},
)
For handlers without a request body, use NoBody:
handler := rocco.NewHandler[rocco.NoBody, UserOutput](
"get-user",
"GET",
"/users/{id}",
func(req *rocco.Request[rocco.NoBody]) (UserOutput, error) {
userID := req.Params.Path["id"]
return UserOutput{ID: userID}, nil
},
).WithPathParams("id")
Streaming (SSE) ¶
For real-time server-to-client communication:
handler := rocco.NewStreamHandler[rocco.NoBody, PriceUpdate](
"price-stream",
"GET",
"/prices/stream",
func(req *rocco.Request[rocco.NoBody], stream rocco.Stream[PriceUpdate]) error {
for {
select {
case <-stream.Done():
return nil
default:
stream.Send(PriceUpdate{Price: 100.0})
}
}
},
)
Error Handling ¶
Use sentinel errors for typed HTTP error responses:
if user == nil {
return UserOutput{}, rocco.ErrNotFound.WithMessage("user not found")
}
Declare errors in handler configuration:
handler.WithErrors(rocco.ErrNotFound, rocco.ErrBadRequest)
OpenAPI Generation ¶
Register an OpenAPI endpoint to serve the generated specification:
engine.RegisterOpenAPIHandler("/openapi.json", rocco.Info{
Title: "My API",
Version: "1.0.0",
})
Observability ¶
Rocco emits lifecycle events via capitan for observability integration:
capitan.Hook(rocco.RequestReceived, func(ctx context.Context, e *capitan.Event) {
method, _ := rocco.MethodKey.From(e)
path, _ := rocco.PathKey.From(e)
log.Printf("Request: %s %s", method, path)
})
Package rocco provides a type-safe HTTP framework for Go with automatic OpenAPI generation.
Index ¶
- Constants
- Variables
- func NewValidationError(fields []ValidationFieldError) error
- type BadGatewayDetails
- type BadRequestDetails
- type Codec
- type ConflictDetails
- type Endpoint
- type Engine
- func (e *Engine) GenerateOpenAPI(identity Identity) *openapi.OpenAPI
- func (e *Engine) Router() *http.ServeMux
- func (e *Engine) Shutdown(ctx context.Context) error
- func (e *Engine) Start(host string, port int) error
- func (e *Engine) WithAuthenticator(extractor func(context.Context, *http.Request) (Identity, error)) *Engine
- func (e *Engine) WithCodec(codec Codec) *Engine
- func (e *Engine) WithHandlers(handlers ...Endpoint) *Engine
- func (e *Engine) WithMiddleware(middleware ...func(http.Handler) http.Handler) *Engine
- func (e *Engine) WithOpenAPIInfo(info openapi.Info) *Engine
- func (e *Engine) WithSpec(spec *EngineSpec) *Engine
- func (e *Engine) WithTag(name, description string) *Engine
- func (e *Engine) WithTagGroup(name string, tags ...string) *Engine
- type EngineConfig
- type EngineSpec
- type Entryable
- type Error
- func (e *Error[D]) Code() string
- func (e *Error[D]) Details() D
- func (e *Error[D]) DetailsAny() any
- func (e *Error[D]) DetailsMeta() sentinel.Metadata
- func (e *Error[D]) Error() string
- func (e *Error[D]) Is(target error) bool
- func (e *Error[D]) Message() string
- func (e *Error[D]) Status() int
- func (e *Error[D]) Unwrap() error
- func (e *Error[D]) WithCause(cause error) *Error[D]
- func (e *Error[D]) WithDetails(details D) *Error[D]
- func (e *Error[D]) WithMessage(message string) *Error[D]
- type ErrorDefinition
- type Event
- type ForbiddenDetails
- type Handler
- func DELETE[In, Out any](path string, fn func(*Request[In]) (Out, error)) *Handler[In, Out]
- func GET[In, Out any](path string, fn func(*Request[In]) (Out, error)) *Handler[In, Out]
- func NewHandler[In, Out any](name string, method, path string, fn func(*Request[In]) (Out, error)) *Handler[In, Out]
- func PATCH[In, Out any](path string, fn func(*Request[In]) (Out, error)) *Handler[In, Out]
- func POST[In, Out any](path string, fn func(*Request[In]) (Out, error)) *Handler[In, Out]
- func PUT[In, Out any](path string, fn func(*Request[In]) (Out, error)) *Handler[In, Out]
- func (*Handler[In, Out]) Close() error
- func (h *Handler[In, Out]) ErrorDefs() []ErrorDefinition
- func (h *Handler[In, Out]) Middleware() []func(http.Handler) http.Handler
- func (h *Handler[In, Out]) Process(ctx context.Context, r *http.Request, w http.ResponseWriter) (int, error)
- func (h *Handler[In, Out]) Spec() HandlerSpec
- func (h *Handler[In, Out]) WithAuthentication() *Handler[In, Out]
- func (h *Handler[In, Out]) WithCodec(codec Codec) *Handler[In, Out]
- func (h *Handler[In, Out]) WithDescription(desc string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithErrors(errs ...ErrorDefinition) *Handler[In, Out]
- func (h *Handler[In, Out]) WithMaxBodySize(size int64) *Handler[In, Out]
- func (h *Handler[In, Out]) WithMiddleware(middleware ...func(http.Handler) http.Handler) *Handler[In, Out]
- func (h *Handler[In, Out]) WithName(name string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithOutputValidation() *Handler[In, Out]
- func (h *Handler[In, Out]) WithPathParams(params ...string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithQueryParams(params ...string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithResponseHeaders(headers map[string]string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithRoles(roles ...string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithScopes(scopes ...string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithSuccessStatus(status int) *Handler[In, Out]
- func (h *Handler[In, Out]) WithSummary(summary string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithTags(tags ...string) *Handler[In, Out]
- func (h *Handler[In, Out]) WithUsageLimit(key string, thresholdFunc func(Identity) int) *Handler[In, Out]
- type HandlerSpec
- type Identity
- type InternalServerDetails
- type JSONCodec
- type NoBody
- type NoDetails
- type NoIdentity
- type NotFoundDetails
- type NotImplementedDetails
- type Params
- type PayloadTooLargeDetails
- type Redirect
- type Request
- type Sendable
- type ServiceUnavailableDetails
- type Stream
- type StreamHandler
- func (*StreamHandler[In, Out]) Close() error
- func (h *StreamHandler[In, Out]) ErrorDefs() []ErrorDefinition
- func (h *StreamHandler[In, Out]) Middleware() []func(http.Handler) http.Handler
- func (h *StreamHandler[In, Out]) Process(ctx context.Context, r *http.Request, w http.ResponseWriter) (int, error)
- func (h *StreamHandler[In, Out]) Spec() HandlerSpec
- func (h *StreamHandler[In, Out]) WithAuthentication() *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithDescription(desc string) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithErrors(errs ...ErrorDefinition) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithMiddleware(middleware ...func(http.Handler) http.Handler) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithPathParams(params ...string) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithQueryParams(params ...string) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithRoles(roles ...string) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithScopes(scopes ...string) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithSummary(summary string) *StreamHandler[In, Out]
- func (h *StreamHandler[In, Out]) WithTags(tags ...string) *StreamHandler[In, Out]
- type TooManyRequestsDetails
- type UnauthorizedDetails
- type UnprocessableEntityDetails
- type UsageLimit
- type Validatable
- type ValidationDetails
- type ValidationFieldError
Constants ¶
const ( HostAll = "" // Bind to all interfaces (0.0.0.0) HostLocal = "localhost" // Bind to loopback (localhost) HostLoopback = "127.0.0.1" // Bind to loopback (127.0.0.1) )
Common host constants for use with Engine.Start().
const ContentTypeJSON = "application/json"
ContentTypeJSON is the MIME type for JSON content.
const DefaultRedirectStatus = http.StatusFound
DefaultRedirectStatus is used when Redirect.Status is 0.
Variables ¶
var ( // ErrBadRequest indicates the request was invalid (400) ErrBadRequest = NewError[BadRequestDetails]("BAD_REQUEST", 400, "bad request") ErrUnauthorized = NewError[UnauthorizedDetails]("UNAUTHORIZED", 401, "unauthorized") // ErrForbidden indicates the request is not allowed (403) ErrForbidden = NewError[ForbiddenDetails]("FORBIDDEN", 403, "forbidden") // ErrNotFound indicates the resource was not found (404) ErrNotFound = NewError[NotFoundDetails]("NOT_FOUND", 404, "not found") // ErrConflict indicates a conflict with existing data (409) ErrConflict = NewError[ConflictDetails]("CONFLICT", 409, "conflict") // ErrPayloadTooLarge indicates the request body exceeds the size limit (413) ErrPayloadTooLarge = NewError[PayloadTooLargeDetails]("PAYLOAD_TOO_LARGE", 413, "payload too large") // ErrUnprocessableEntity indicates the request was well-formed but semantically invalid (422) ErrUnprocessableEntity = NewError[UnprocessableEntityDetails]("UNPROCESSABLE_ENTITY", 422, "unprocessable entity") // ErrValidationFailed indicates request validation failed with detailed field errors (422) ErrValidationFailed = NewError[ValidationDetails]("VALIDATION_FAILED", 422, "validation failed") // ErrTooManyRequests indicates rate limiting (429) ErrTooManyRequests = NewError[TooManyRequestsDetails]("TOO_MANY_REQUESTS", 429, "too many requests") )
Client errors (4xx)
var ( // ErrInternalServer indicates an unexpected server error (500) ErrInternalServer = NewError[InternalServerDetails]("INTERNAL_SERVER_ERROR", 500, "internal server error") // ErrNotImplemented indicates the functionality is not implemented (501) ErrNotImplemented = NewError[NotImplementedDetails]("NOT_IMPLEMENTED", 501, "not implemented") // ErrBadGateway indicates an upstream provider returned an error (502) ErrBadGateway = NewError[BadGatewayDetails]("BAD_GATEWAY", 502, "bad gateway") ErrServiceUnavailable = NewError[ServiceUnavailableDetails]("SERVICE_UNAVAILABLE", 503, "service unavailable") )
Server errors (5xx)
var ( // EngineCreated is emitted when an Engine instance is created. // Fields: none. EngineCreated = capitan.NewSignal("http.engine.created", "HTTP engine instance created") // EngineStarting is emitted when the server starts listening for requests. // Fields: AddressKey. EngineStarting = capitan.NewSignal("http.engine.starting", "HTTP server starting to listen for requests on configured address") // EngineShutdownStarted is emitted when graceful shutdown is initiated. // Fields: none. EngineShutdownStarted = capitan.NewSignal("http.engine.shutdown.started", "HTTP engine graceful shutdown initiated") // EngineShutdownComplete is emitted when shutdown finishes. // Fields: GracefulKey, ErrorKey (if failed). EngineShutdownComplete = capitan.NewSignal("http.engine.shutdown.complete", "HTTP engine shutdown completed, graceful or with error") )
Engine lifecycle signals.
var ( // RequestReceived is emitted when a request is received. // Fields: MethodKey, PathKey, HandlerNameKey. RequestReceived = capitan.NewSignal("http.request.received", "HTTP request received by engine and routed to handler") // RequestCompleted is emitted when a request completes successfully. // Fields: MethodKey, PathKey, HandlerNameKey, StatusCodeKey, DurationMsKey. RequestCompleted = capitan.NewSignal("http.request.completed", "HTTP request completed successfully with response sent") // RequestFailed is emitted when a request fails with an error. // Fields: MethodKey, PathKey, HandlerNameKey, StatusCodeKey, DurationMsKey, ErrorKey. RequestFailed = capitan.NewSignal("http.request.failed", "HTTP request failed during processing with error") )
Request lifecycle signals.
var ( // HandlerExecuting is emitted when handler execution begins. // Fields: HandlerNameKey. HandlerExecuting = capitan.NewSignal("http.handler.executing", "Handler execution started for incoming request") // HandlerSuccess is emitted when a handler returns successfully. // Fields: HandlerNameKey, StatusCodeKey. HandlerSuccess = capitan.NewSignal("http.handler.success", "Handler completed successfully and returned response") // HandlerError is emitted when a handler returns an error. // Fields: HandlerNameKey, ErrorKey. HandlerError = capitan.NewSignal("http.handler.error", "Handler returned unexpected error during execution") // HandlerSentinelError is emitted when a declared sentinel error is returned. // Fields: HandlerNameKey, ErrorKey, StatusCodeKey. HandlerSentinelError = capitan.NewSignal("http.handler.sentinel.error", "Handler returned declared sentinel error mapped to HTTP status") // HandlerUndeclaredSentinel is emitted when an undeclared sentinel error is returned (programming error). // Fields: HandlerNameKey, ErrorKey, StatusCodeKey. HandlerUndeclaredSentinel = capitan.NewSignal("http.handler.sentinel.undeclared", "Handler returned undeclared sentinel error, programming error detected") // RequestParamsInvalid is emitted when path or query parameter extraction fails. // Fields: HandlerNameKey, ErrorKey. RequestParamsInvalid = capitan.NewSignal("http.request.params.invalid", "Request path or query parameter extraction failed") // RequestBodyReadError is emitted when reading the request body fails. // Fields: HandlerNameKey, ErrorKey. RequestBodyReadError = capitan.NewSignal("http.request.body.read.error", "Failed to read request body from HTTP stream") // RequestBodyParseError is emitted when parsing the JSON request body fails. // Fields: HandlerNameKey, ErrorKey. RequestBodyParseError = capitan.NewSignal("http.request.body.parse.error", "Failed to parse JSON request body") // RequestValidationInputFailed is emitted when input validation fails. // Fields: HandlerNameKey, ErrorKey. RequestValidationInputFailed = capitan.NewSignal("http.request.validation.input.failed", "Request input validation failed against defined rules") // RequestValidationOutputFailed is emitted when output validation fails. // Fields: HandlerNameKey, ErrorKey. RequestValidationOutputFailed = capitan.NewSignal("http.request.validation.output.failed", "Response output validation failed, internal error") // RequestResponseMarshalError is emitted when marshaling the response fails. // Fields: HandlerNameKey, ErrorKey. RequestResponseMarshalError = capitan.NewSignal("http.request.response.marshal.error", "Failed to marshal response to JSON") // RequestBodyCloseError is emitted when closing the request body fails. // Fields: HandlerNameKey, ErrorKey. RequestBodyCloseError = capitan.NewSignal("http.request.body.close.error", "Failed to close request body stream") // ResponseBodyCloseError is emitted when closing an HTTP client response body fails. // Fields: EndpointKey, ErrorKey. ResponseBodyCloseError = capitan.NewSignal("http.response.body.close.error", "Failed to close HTTP client response body") // ResponseWriteError is emitted when writing the response body fails. // Fields: HandlerNameKey, ErrorKey. ResponseWriteError = capitan.NewSignal("http.response.write.error", "Failed to write response body to client") )
Handler processing signals.
var ( // AuthenticationFailed is emitted when authentication extraction fails. // Fields: MethodKey, PathKey, HandlerNameKey, ErrorKey. AuthenticationFailed = capitan.NewSignal("http.auth.failed", "Authentication extraction failed for request") // AuthenticationSucceeded is emitted when authentication succeeds. // Fields: MethodKey, PathKey, HandlerNameKey, IdentityIDKey, TenantIDKey. AuthenticationSucceeded = capitan.NewSignal("http.auth.succeeded", "Authentication succeeded for request") )
Authentication signals.
var ( // AuthorizationScopeDenied is emitted when scope requirement is not met. // Fields: MethodKey, PathKey, HandlerNameKey, IdentityIDKey, RequiredScopesKey, UserScopesKey. AuthorizationScopeDenied = capitan.NewSignal("http.authz.scope.denied", "Authorization failed due to insufficient scopes") // AuthorizationRoleDenied is emitted when role requirement is not met. // Fields: MethodKey, PathKey, HandlerNameKey, IdentityIDKey, RequiredRolesKey, UserRolesKey. AuthorizationRoleDenied = capitan.NewSignal("http.authz.role.denied", "Authorization failed due to insufficient roles") // AuthorizationSucceeded is emitted when authorization checks pass. // Fields: MethodKey, PathKey, HandlerNameKey, IdentityIDKey. AuthorizationSucceeded = capitan.NewSignal("http.authz.succeeded", "Authorization checks passed for request") )
Authorization signals.
var ( // StreamExecuting is emitted when stream handler execution begins. // Fields: HandlerNameKey. StreamExecuting = capitan.NewSignal("http.stream.executing", "Stream handler execution started for incoming request") // StreamStarted is emitted when SSE stream is established and headers sent. // Fields: HandlerNameKey. StreamStarted = capitan.NewSignal("http.stream.started", "SSE stream established and response headers sent") // StreamEnded is emitted when stream handler completes normally. // Fields: HandlerNameKey. StreamEnded = capitan.NewSignal("http.stream.ended", "SSE stream handler completed normally") // StreamClientDisconnected is emitted when client disconnects from stream. // Fields: HandlerNameKey. StreamClientDisconnected = capitan.NewSignal("http.stream.client.disconnected", "Client disconnected from SSE stream") // StreamError is emitted when stream handler encounters an error. // Fields: HandlerNameKey, ErrorKey. StreamError = capitan.NewSignal("http.stream.error", "SSE stream handler encountered error") )
Stream (SSE) lifecycle signals.
var ( // Engine fields. AddressKey = capitan.NewStringKey("address") // Request/Response fields. MethodKey = capitan.NewStringKey("method") PathKey = capitan.NewStringKey("path") HandlerNameKey = capitan.NewStringKey("handler_name") StatusCodeKey = capitan.NewIntKey("status_code") DurationMsKey = capitan.NewInt64Key("duration_ms") ErrorKey = capitan.NewStringKey("error") GracefulKey = capitan.NewBoolKey("graceful") EndpointKey = capitan.NewStringKey("endpoint") // Authentication/Authorization fields. IdentityIDKey = capitan.NewStringKey("identity_id") TenantIDKey = capitan.NewStringKey("tenant_id") RequiredScopesKey = capitan.NewStringKey("required_scopes") UserScopesKey = capitan.NewStringKey("user_scopes") RequiredRolesKey = capitan.NewStringKey("required_roles") UserRolesKey = capitan.NewStringKey("user_roles") // Rate limiting fields. LimitKeyKey = capitan.NewStringKey("limit_key") CurrentValueKey = capitan.NewIntKey("current_value") ThresholdKey = capitan.NewIntKey("threshold") )
Event field keys (primitive types only).
var ( // HandlerRegistered is emitted when a handler is registered with the engine. // Fields: HandlerNameKey, MethodKey, PathKey. HandlerRegistered = capitan.NewSignal("http.handler.registered", "HTTP handler registered with engine for specific route") )
Handler registration signals.
var ( // RateLimitExceeded is emitted when usage limit threshold is exceeded. // Fields: MethodKey, PathKey, HandlerNameKey, IdentityIDKey, LimitKeyKey, CurrentValueKey, ThresholdKey. RateLimitExceeded = capitan.NewSignal("http.ratelimit.exceeded", "Usage limit threshold exceeded for request") )
Rate limiting signals.
Functions ¶
func NewValidationError ¶
func NewValidationError(fields []ValidationFieldError) error
NewValidationError creates a validation error with field details. Use this when implementing custom validators to return structured validation errors.
Types ¶
type BadGatewayDetails ¶
type BadGatewayDetails struct {
Provider string `json:"provider,omitempty" description:"The upstream provider that failed"`
Reason string `json:"reason,omitempty" description:"Error details from the provider"`
}
BadGatewayDetails provides context for upstream provider errors.
type BadRequestDetails ¶
type BadRequestDetails struct {
Reason string `json:"reason,omitempty" description:"Why the request was invalid"`
}
BadRequestDetails provides context for bad request errors.
type Codec ¶
type Codec interface {
// ContentType returns the MIME type for this codec (e.g., "application/json").
ContentType() string
// Marshal encodes a value to bytes.
Marshal(v any) ([]byte, error)
// Unmarshal decodes bytes into a value.
Unmarshal(data []byte, v any) error
}
Codec defines the interface for request/response serialization. Implementations handle marshaling and unmarshaling of handler payloads.
type ConflictDetails ¶
type ConflictDetails struct {
Reason string `json:"reason,omitempty" description:"What caused the conflict"`
}
ConflictDetails provides context for conflict errors.
type Endpoint ¶
type Endpoint interface {
// Process handles the HTTP request and writes the response.
// Returns the HTTP status code written and any error encountered.
Process(ctx context.Context, r *http.Request, w http.ResponseWriter) (int, error)
// Spec returns the declarative specification for this handler
Spec() HandlerSpec
// ErrorDefs returns the declared error definitions for this handler.
// Used by OpenAPI generation to extract error schemas.
ErrorDefs() []ErrorDefinition
// Middleware returns handler-specific middleware
Middleware() []func(http.Handler) http.Handler
// Lifecycle
Close() error
}
Endpoint represents an HTTP route handler with metadata.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the core HTTP server that manages routing, middleware, and handler registration.
func NewEngine ¶
func NewEngine() *Engine
NewEngine creates a new Engine. Use WithAuthenticator to configure identity extraction for authenticated handlers.
func (*Engine) GenerateOpenAPI ¶
GenerateOpenAPI creates an OpenAPI specification from registered handlers. If identity is provided, only handlers accessible to that identity will be included.
func (*Engine) Router ¶
Router returns the underlying http.ServeMux for advanced use cases. This allows power users to register custom routes that won't appear in OpenAPI documentation.
func (*Engine) Start ¶
Start begins listening for HTTP requests on the given host and port. Use an empty string for host to bind to all interfaces. This method blocks until the server is shutdown.
func (*Engine) WithAuthenticator ¶
func (e *Engine) WithAuthenticator(extractor func(context.Context, *http.Request) (Identity, error)) *Engine
WithAuthenticator sets the identity extraction function for authenticated handlers. The extractor is called for handlers that require authentication (via WithAuthentication).
func (*Engine) WithCodec ¶
WithCodec sets the default codec for all handlers registered with this engine. Handlers that explicitly call WithCodec() will use their own codec instead.
func (*Engine) WithHandlers ¶
WithHandlers adds one or more Endpoints to the engine and returns the engine for chaining.
Threading model: All handler registration must complete before calling Start(). Calling WithHandlers concurrently or after Start() results in undefined behavior. This follows the standard Go pattern for HTTP server configuration.
func (*Engine) WithMiddleware ¶
WithMiddleware adds global middleware to the engine and returns the engine for chaining.
func (*Engine) WithOpenAPIInfo ¶
WithOpenAPIInfo sets the OpenAPI Info metadata.
func (*Engine) WithSpec ¶
func (e *Engine) WithSpec(spec *EngineSpec) *Engine
WithSpec sets the engine specification for OpenAPI generation.
type EngineConfig ¶
type EngineConfig struct {
ReadTimeout time.Duration // Maximum duration for reading entire request
WriteTimeout time.Duration // Maximum duration for writing response
IdleTimeout time.Duration // Maximum time to wait for next request on keep-alive
}
EngineConfig holds configuration for the Engine.
func DefaultConfig ¶
func DefaultConfig() *EngineConfig
DefaultConfig returns an EngineConfig with sensible defaults.
type EngineSpec ¶
type EngineSpec struct {
// OpenAPI Info
Info openapi.Info `json:"info" yaml:"info"`
// Global Tags with descriptions
Tags []openapi.Tag `json:"tags,omitempty" yaml:"tags,omitempty"`
// Tag Groups for hierarchical tag organization (x-tagGroups vendor extension)
TagGroups []openapi.TagGroup `json:"x-tagGroups,omitempty" yaml:"x-tagGroups,omitempty"`
// Servers
Servers []openapi.Server `json:"servers,omitempty" yaml:"servers,omitempty"`
// External Documentation
ExternalDocs *openapi.ExternalDocumentation `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
// Global Security (optional, for APIs that require auth on all endpoints)
Security []openapi.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"`
}
EngineSpec contains declarative configuration for the API engine. This spec is serializable and represents API-level metadata used for OpenAPI generation and documentation.
func DefaultEngineSpec ¶
func DefaultEngineSpec() *EngineSpec
DefaultEngineSpec returns an EngineSpec with sensible defaults.
type Entryable ¶
Entryable is implemented by input types that transform themselves before handler execution. Hooks run after body parsing and validation, before the handler function.
type Error ¶
type Error[D any] struct { // contains filtered or unexported fields }
Error represents a structured API error with code, status, message, and typed details. The type parameter D captures the details type at compile time for type-safe usage and OpenAPI schema generation.
func NewError ¶
NewError creates a new error definition with the given code, status, and message. The type parameter D specifies the details struct type for this error. Use this to define custom error types with typed details.
func (*Error[D]) DetailsAny ¶
DetailsAny returns the error details as any for serialization. This satisfies the ErrorDefinition interface.
func (*Error[D]) DetailsMeta ¶
DetailsMeta returns the sentinel metadata for the details type. Used by OpenAPI generation to create schemas.
func (*Error[D]) Is ¶
Is enables errors.Is() to match against base error codes. Two errors match if they have the same code.
func (*Error[D]) WithCause ¶
WithCause returns a new error with the given underlying cause. The cause is not exposed to API clients but is available for logging/debugging. The original error is not modified (immutable).
func (*Error[D]) WithDetails ¶
WithDetails returns a new error with the given typed details. The original error is not modified (immutable).
func (*Error[D]) WithMessage ¶
WithMessage returns a new error with a custom message. The original error is not modified (immutable).
type ErrorDefinition ¶
type ErrorDefinition interface {
error
Code() string
Status() int
Message() string
DetailsAny() any // For serialization (type-erased)
DetailsMeta() sentinel.Metadata // For OpenAPI schema generation
}
ErrorDefinition is the interface that all error definitions satisfy. It provides non-generic access to error metadata for the engine and OpenAPI generation.
type Event ¶
type Event struct {
// Event identification
Type string // Event type (e.g., "request.received", "worker.started")
Timestamp time.Time // When this event occurred
// Request context (immutable snapshot)
Method string // HTTP method
Path string // Request path
Host string // Request host
// Processing state (snapshot at event time)
Status int // HTTP status code
Error string // Error message if any
Metadata map[string]any // Event-specific metadata
// System context
WorkerID int // Worker processing this request (-1 if N/A)
QueueDepth int // Queue depth at event time
QueueWaitMs float64 // Milliseconds spent in queue
DurationMs float64 // Processing duration in milliseconds
}
Event represents a snapshot of system state at a specific moment. Events are immutable once created and safe for async processing. They capture relevant data from Jobs and add temporal context for linear analysis in observability systems.
func NewRequestEvent ¶
NewRequestEvent creates an Event from an HTTP request and additional context. It copies relevant data to ensure immutability.
type ForbiddenDetails ¶
type ForbiddenDetails struct {
Reason string `json:"reason,omitempty" description:"Why access was denied"`
}
ForbiddenDetails provides context for authorization errors.
type Handler ¶
type Handler[In, Out any] struct { // Type metadata from sentinel. InputMeta sentinel.Metadata OutputMeta sentinel.Metadata // contains filtered or unexported fields }
Handler wraps a typed handler function with metadata for documentation and parsing. It implements Endpoint interface. The handler function receives a Request with typed input and parameters.
func NewHandler ¶
func NewHandler[In, Out any](name string, method, path string, fn func(*Request[In]) (Out, error)) *Handler[In, Out]
NewHandler creates a new typed handler with sentinel metadata.
func (*Handler[In, Out]) ErrorDefs ¶
func (h *Handler[In, Out]) ErrorDefs() []ErrorDefinition
ErrorDefs returns the declared error definitions for this handler. Used by OpenAPI generation to extract error schemas.
func (*Handler[In, Out]) Middleware ¶
Middleware implements Endpoint.
func (*Handler[In, Out]) Process ¶
func (h *Handler[In, Out]) Process(ctx context.Context, r *http.Request, w http.ResponseWriter) (int, error)
Process implements Endpoint.
func (*Handler[In, Out]) Spec ¶
func (h *Handler[In, Out]) Spec() HandlerSpec
Spec implements Endpoint.
func (*Handler[In, Out]) WithAuthentication ¶
WithAuthentication marks this handler as requiring authentication.
func (*Handler[In, Out]) WithCodec ¶
WithCodec sets the codec for request/response serialization. This overrides the engine's default codec for this handler.
func (*Handler[In, Out]) WithDescription ¶
WithDescription sets the OpenAPI description.
func (*Handler[In, Out]) WithErrors ¶
func (h *Handler[In, Out]) WithErrors(errs ...ErrorDefinition) *Handler[In, Out]
WithErrors declares which errors this handler may return. Undeclared errors will be converted to 500 Internal Server Error. This is used for OpenAPI documentation generation with proper error schemas.
func (*Handler[In, Out]) WithMaxBodySize ¶
WithMaxBodySize sets the maximum request body size in bytes for this handler. Set to 0 for unlimited (not recommended for production).
func (*Handler[In, Out]) WithMiddleware ¶
func (h *Handler[In, Out]) WithMiddleware(middleware ...func(http.Handler) http.Handler) *Handler[In, Out]
WithMiddleware adds middleware to this handler and returns the handler for chaining.
func (*Handler[In, Out]) WithName ¶
WithName sets a custom handler name, overriding the auto-generated one. This affects the OpenAPI operationId and log entries.
func (*Handler[In, Out]) WithOutputValidation ¶
WithOutputValidation enables validation of output structs before sending responses. This is disabled by default for performance. Enable in development to catch bugs early. Output validation failures return 500 Internal Server Error.
func (*Handler[In, Out]) WithPathParams ¶
WithPathParams specifies required path parameters.
func (*Handler[In, Out]) WithQueryParams ¶
WithQueryParams specifies required query parameters.
func (*Handler[In, Out]) WithResponseHeaders ¶
WithResponseHeaders sets default response headers for this handler.
func (*Handler[In, Out]) WithRoles ¶
WithRoles adds a role requirement group (OR logic within group, AND across multiple calls). Example: .WithRoles("admin", "moderator") requires (admin OR moderator). Calling multiple times creates AND: .WithRoles("admin").WithRoles("verified") = admin AND verified.
func (*Handler[In, Out]) WithScopes ¶
WithScopes adds a scope requirement group (OR logic within group, AND across multiple calls). Example: .WithScopes("read", "write") requires (read OR write). Calling multiple times creates AND: .WithScopes("read").WithScopes("admin") = read AND admin.
func (*Handler[In, Out]) WithSuccessStatus ¶
WithSuccessStatus sets the HTTP status code for successful responses.
func (*Handler[In, Out]) WithSummary ¶
WithSummary sets the OpenAPI summary.
func (*Handler[In, Out]) WithUsageLimit ¶
func (h *Handler[In, Out]) WithUsageLimit(key string, thresholdFunc func(Identity) int) *Handler[In, Out]
WithUsageLimit adds a usage limit check based on identity stats. The handler will return 429 Too Many Requests if identity.Stats()[key] >= thresholdFunc(identity). The thresholdFunc is called with the identity to allow dynamic limits per user/tenant. Usage limits require authentication.
type HandlerSpec ¶
type HandlerSpec struct {
// Routing
Name string `json:"name" yaml:"name"`
Method string `json:"method" yaml:"method"`
Path string `json:"path" yaml:"path"`
// Documentation
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
// Request/Response
PathParams []string `json:"pathParams,omitempty" yaml:"pathParams,omitempty"`
QueryParams []string `json:"queryParams,omitempty" yaml:"queryParams,omitempty"`
InputTypeFQDN string `json:"-" yaml:"-"`
InputTypeName string `json:"inputTypeName" yaml:"inputTypeName"`
OutputTypeFQDN string `json:"-" yaml:"-"`
OutputTypeName string `json:"outputTypeName" yaml:"outputTypeName"`
SuccessStatus int `json:"successStatus" yaml:"successStatus"`
ErrorCodes []int `json:"errorCodes,omitempty" yaml:"errorCodes,omitempty"`
ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` // MIME type for request/response bodies
// Authentication & Authorization
RequiresAuth bool `json:"requiresAuth" yaml:"requiresAuth"`
ScopeGroups [][]string `json:"scopeGroups,omitempty" yaml:"scopeGroups,omitempty"` // OR within group, AND across groups
RoleGroups [][]string `json:"roleGroups,omitempty" yaml:"roleGroups,omitempty"` // OR within group, AND across groups
// Rate Limiting
UsageLimits []UsageLimit `json:"usageLimits,omitempty" yaml:"usageLimits,omitempty"`
// Streaming
IsStream bool `json:"isStream,omitempty" yaml:"isStream,omitempty"` // SSE stream handler
}
HandlerSpec contains declarative configuration for a route handler. This spec is serializable and represents all metadata about a handler that can be used for documentation, authorization checks, and filtering.
type Identity ¶
type Identity interface {
// ID returns the unique identifier for this identity (e.g., user ID, service account ID).
ID() string
// TenantID returns the tenant/organization this identity belongs to.
// Return empty string if not applicable.
TenantID() string
// Email returns the email address associated with this identity.
// Return empty string if not available.
Email() string
// Scopes returns all scopes/permissions granted to this identity.
Scopes() []string
// Roles returns all roles assigned to this identity.
Roles() []string
// HasScope checks if this identity has the given scope/permission.
HasScope(scope string) bool
// HasRole checks if this identity has the given role.
HasRole(role string) bool
// Stats returns usage metrics for rate limiting.
// Keys are metric names (e.g., "requests_today", "api_calls_this_hour").
// Values are current counts.
Stats() map[string]int
}
Identity represents an authenticated user or service account. Users must implement this interface with their own identity type.
type InternalServerDetails ¶
type InternalServerDetails struct {
Reason string `json:"reason,omitempty" description:"Internal error context"`
}
InternalServerDetails provides context for internal errors (not exposed to clients).
type JSONCodec ¶
type JSONCodec struct{}
JSONCodec implements Codec using encoding/json.
func (JSONCodec) ContentType ¶
ContentType returns "application/json".
type NoBody ¶
type NoBody struct{}
NoBody represents an empty input for handlers that don't expect a request body. Used for GET, HEAD, DELETE requests.
type NoDetails ¶
type NoDetails struct{}
NoDetails is used for errors that don't carry additional details.
type NoIdentity ¶
type NoIdentity struct{}
NoIdentity represents the absence of authentication. Used for public endpoints that don't require authentication.
func (NoIdentity) HasScope ¶
func (NoIdentity) HasScope(_ string) bool
HasScope implements Identity.
type NotFoundDetails ¶
type NotFoundDetails struct {
Resource string `json:"resource,omitempty" description:"The type of resource that was not found"`
}
NotFoundDetails provides context for not found errors.
type NotImplementedDetails ¶
type NotImplementedDetails struct {
Feature string `json:"feature,omitempty" description:"The feature that is not implemented"`
}
NotImplementedDetails provides context for not implemented errors.
type Params ¶
type Params struct {
Path map[string]string // Path parameters (e.g., /users/{id})
Query map[string]string // Query parameters (e.g., ?page=1)
}
Params holds extracted request parameters.
type PayloadTooLargeDetails ¶
type PayloadTooLargeDetails struct {
MaxSize int64 `json:"max_size,omitempty" description:"Maximum allowed payload size in bytes"`
}
PayloadTooLargeDetails provides context for payload size errors.
type Redirect ¶
type Redirect struct {
URL string // Target URL (required)
Status int // HTTP status code (default: 302 Found)
Headers http.Header // Additional response headers (e.g., Set-Cookie)
}
Redirect represents an HTTP redirect response. Return this from a handler to redirect the client instead of returning a body.
Headers are written to the response before the redirect. Use this to set cookies or other headers on the redirect response (e.g., Set-Cookie for sessions).
Example:
handler := rocco.GET[rocco.NoBody, rocco.Redirect]("/old-path", func(req *rocco.Request[rocco.NoBody]) (rocco.Redirect, error) {
return rocco.Redirect{URL: "/new-path"}, nil
})
type Request ¶
type Request[In any] struct { context.Context // Embedded for deadline, cancellation, values *http.Request // Embedded for direct access when needed (use sparingly) Params *Params Body In Identity Identity // Authenticated identity (nil/NoIdentity for public endpoints) }
Request holds all data needed by handler callbacks. It embeds context and the underlying HTTP request for full access.
IMPORTANT: Modifying the embedded *http.Request (headers, etc.) is not recommended as changes won't be reflected in OpenAPI documentation or handler configuration. Use Handler builder methods (WithResponseHeaders, WithSuccessStatus) for documented behavior.
type Sendable ¶
Sendable is implemented by output types that transform themselves before marshaling. Hooks run after the handler function, before output validation and marshaling.
type ServiceUnavailableDetails ¶
type ServiceUnavailableDetails struct {
}
ServiceUnavailableDetails provides context for service unavailable errors.
type Stream ¶
type Stream[T any] interface { // Send sends a data-only event. Send(data T) error // SendEvent sends a named event with data. SendEvent(event string, data T) error // SendComment sends a comment (useful for keep-alive). SendComment(comment string) error // Done returns a channel closed when client disconnects. Done() <-chan struct{} }
Stream provides a typed interface for sending SSE events.
type StreamHandler ¶
type StreamHandler[In, Out any] struct { // Type metadata from sentinel. InputMeta sentinel.Metadata OutputMeta sentinel.Metadata // contains filtered or unexported fields }
StreamHandler wraps a typed streaming handler function with metadata. It implements Endpoint interface for SSE (Server-Sent Events) responses.
func NewStreamHandler ¶
func NewStreamHandler[In, Out any](name string, method, path string, fn func(*Request[In], Stream[Out]) error) *StreamHandler[In, Out]
NewStreamHandler creates a new typed streaming handler with sentinel metadata.
func (*StreamHandler[In, Out]) Close ¶
func (*StreamHandler[In, Out]) Close() error
Close implements Endpoint.
func (*StreamHandler[In, Out]) ErrorDefs ¶
func (h *StreamHandler[In, Out]) ErrorDefs() []ErrorDefinition
ErrorDefs implements Endpoint.
func (*StreamHandler[In, Out]) Middleware ¶
func (h *StreamHandler[In, Out]) Middleware() []func(http.Handler) http.Handler
Middleware implements Endpoint.
func (*StreamHandler[In, Out]) Process ¶
func (h *StreamHandler[In, Out]) Process(ctx context.Context, r *http.Request, w http.ResponseWriter) (int, error)
Process implements Endpoint.
func (*StreamHandler[In, Out]) Spec ¶
func (h *StreamHandler[In, Out]) Spec() HandlerSpec
Spec implements Endpoint.
func (*StreamHandler[In, Out]) WithAuthentication ¶
func (h *StreamHandler[In, Out]) WithAuthentication() *StreamHandler[In, Out]
WithAuthentication marks this handler as requiring authentication.
func (*StreamHandler[In, Out]) WithDescription ¶
func (h *StreamHandler[In, Out]) WithDescription(desc string) *StreamHandler[In, Out]
WithDescription sets the OpenAPI description.
func (*StreamHandler[In, Out]) WithErrors ¶
func (h *StreamHandler[In, Out]) WithErrors(errs ...ErrorDefinition) *StreamHandler[In, Out]
WithErrors declares which errors this handler may return. Note: Errors can only be returned before the stream starts.
func (*StreamHandler[In, Out]) WithMiddleware ¶
func (h *StreamHandler[In, Out]) WithMiddleware(middleware ...func(http.Handler) http.Handler) *StreamHandler[In, Out]
WithMiddleware adds middleware to this handler.
func (*StreamHandler[In, Out]) WithPathParams ¶
func (h *StreamHandler[In, Out]) WithPathParams(params ...string) *StreamHandler[In, Out]
WithPathParams specifies required path parameters.
func (*StreamHandler[In, Out]) WithQueryParams ¶
func (h *StreamHandler[In, Out]) WithQueryParams(params ...string) *StreamHandler[In, Out]
WithQueryParams specifies required query parameters.
func (*StreamHandler[In, Out]) WithRoles ¶
func (h *StreamHandler[In, Out]) WithRoles(roles ...string) *StreamHandler[In, Out]
WithRoles adds a role requirement group.
func (*StreamHandler[In, Out]) WithScopes ¶
func (h *StreamHandler[In, Out]) WithScopes(scopes ...string) *StreamHandler[In, Out]
WithScopes adds a scope requirement group.
func (*StreamHandler[In, Out]) WithSummary ¶
func (h *StreamHandler[In, Out]) WithSummary(summary string) *StreamHandler[In, Out]
WithSummary sets the OpenAPI summary.
func (*StreamHandler[In, Out]) WithTags ¶
func (h *StreamHandler[In, Out]) WithTags(tags ...string) *StreamHandler[In, Out]
WithTags sets the OpenAPI tags.
type TooManyRequestsDetails ¶
type TooManyRequestsDetails struct {
RetryAfter int `json:"retry_after,omitempty" description:"Seconds until the client can retry"`
}
TooManyRequestsDetails provides context for rate limit errors.
type UnauthorizedDetails ¶
type UnauthorizedDetails struct {
}
UnauthorizedDetails provides context for authentication errors.
type UnprocessableEntityDetails ¶
type UnprocessableEntityDetails struct {
Reason string `json:"reason,omitempty" description:"Why the entity was unprocessable"`
}
UnprocessableEntityDetails provides context for validation errors.
type UsageLimit ¶
type UsageLimit struct {
Key string // Stats key to check (e.g., "requests_today")
ThresholdFunc func(Identity) int // Function that returns threshold for this identity
}
UsageLimit represents a usage limit check with a dynamic threshold callback.
type Validatable ¶
type Validatable interface {
Validate() error
}
Validatable is implemented by types that can validate themselves. Input and output structs can implement this interface to opt-in to validation.
type ValidationDetails ¶
type ValidationDetails struct {
Fields []ValidationFieldError `json:"fields" description:"List of validation errors"`
}
ValidationDetails provides detailed validation errors for request validation. Implements error interface for use with errors.As().
func (ValidationDetails) Error ¶
func (v ValidationDetails) Error() string
Error implements the error interface.
type ValidationFieldError ¶
type ValidationFieldError struct {
Field string `json:"field" description:"The field that failed validation"`
Message string `json:"message" description:"Description of the validation failure"`
}
ValidationFieldError represents a single field validation error.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package oauth provides OAuth 2.0 protocol functions for the authorization code flow.
|
Package oauth provides OAuth 2.0 protocol functions for the authorization code flow. |
|
Package session provides cookie-based session management for rocco APIs.
|
Package session provides cookie-based session management for rocco APIs. |
|
Package testing provides test utilities for rocco.
|
Package testing provides test utilities for rocco. |