api

package
v0.0.0-...-51ec7a6 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Package api wraps Huma with Soro routing, errors, resources, and middleware.

Index

Constants

View Source
const (
	AudienceHeader                  = "X-Soro-API-Audience"
	AudienceExtension               = "x-soro-audience"
	AudienceScopesExtension         = "x-soro-required-scopes"
	AudienceClientAudienceExtension = "x-soro-client-audience"
)
View Source
const RequestIDHeader = "X-Request-ID"

Variables

View Source
var ErrFrozen = errors.New("scoped API router is frozen")

Functions

func HTTPError

func HTTPError(ctx context.Context, err error) error

func Register

func Register[I, O any](router *Router, operation huma.Operation, handler func(context.Context, *I) (*O, error)) error

func RegisterScoped

func RegisterScoped[I, O any](router *ScopedRouter, operation huma.Operation, handler func(context.Context, *I) (*O, error)) error

RegisterScoped registers an operation through an engine-owned API boundary. Scoped operations use paths relative to the scope.

func RequestID

func RequestID(ctx context.Context) string

func WithAudience

func WithAudience(policy AudiencePolicy) func(*huma.Operation)

WithAudience marks a Huma operation. Soro validates and enforces the policy when the operation is registered.

Types

type API

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

func New

func New(config Config, options ...Option) (*API, error)

func (*API) Config

func (api *API) Config() Config

func (*API) Handler

func (api *API) Handler() http.Handler

Handler returns the complete HTTP middleware chain.

func (*API) Huma

func (api *API) Huma() huma.API

func (*API) Mux

func (api *API) Mux() *http.ServeMux

func (*API) OpenAPI

func (api *API) OpenAPI() *huma.OpenAPI

func (*API) Routes

func (api *API) Routes() []Route

func (*API) Scope

func (api *API) Scope(version, owner, path string) (*ScopedRouter, error)

Scope creates a v1-style engine router directly from the API.

func (*API) Version

func (api *API) Version(name string, configure func(*Router)) error

func (*API) WebhookScope

func (api *API) WebhookScope(version, owner, path string) (*webhooks.Scope, error)

WebhookScope creates an engine-owned public ingress scope alongside the engine's typed API scope. Webhook routes bypass audience and request-wide transaction middleware so the engine can authenticate a source before selecting tenant state and committing its own bounded transaction.

func (*API) Webhooks

func (api *API) Webhooks() *webhooks.Registry

type Action

type Action string

Action identifies one of the standard resource operations.

const (
	Index   Action = "index"
	Show    Action = "show"
	Create  Action = "create"
	Update  Action = "update"
	Destroy Action = "destroy"
)

type Audience

type Audience string

Audience describes who may build a client for an operation. It is additive to normal authentication, authorization, tenant isolation, and auditing.

const (
	AudienceFirstParty  Audience = "first_party"
	AudienceSecondParty Audience = "second_party"
	AudienceThirdParty  Audience = "third_party"
)

type AudienceAuthorizer

type AudienceAuthorizer interface {
	RequireScopes(context.Context, Audience, []string) error
	AuthenticateClient(context.Context, *http.Request, string) (context.Context, error)
}

AudienceAuthorizer bridges Soro's audience policy to application-owned principal scopes and software-client credentials. AuthenticateClient may return a derived context containing the authenticated client identity.

type AudiencePolicy

type AudiencePolicy struct {
	Audience       Audience
	RequiredScopes []string
	ClientAudience string
}

AudiencePolicy is the complete audience requirement for one operation. ClientAudience is required only for first-party operations.

func FirstParty

func FirstParty(clientAudience string, requiredScope string, additionalScopes ...string) AudiencePolicy

FirstParty requires both normal scoped principal authorization and an independent software-client credential for clientAudience.

func SecondParty

func SecondParty(requiredScope string, additionalScopes ...string) AudiencePolicy

SecondParty requires one or more elevated scopes granted to vetted clients.

func ThirdParty

func ThirdParty(requiredScopes ...string) AudiencePolicy

ThirdParty describes the public developer surface. An empty scope list is explicit public/anonymous access; otherwise normal scope authorization runs.

func (AudiencePolicy) Validate

func (policy AudiencePolicy) Validate() error

type CollectionEnvelope

type CollectionEnvelope[T any] struct {
	Data []T            `json:"data"`
	Meta PaginationMeta `json:"meta"`
}

CollectionEnvelope is the standard body for page-based collection results.

func NewCollectionEnvelope

func NewCollectionEnvelope[T any](ctx context.Context, data []T, page, perPage, total, pages int) CollectionEnvelope[T]

NewCollectionEnvelope creates the canonical collection body for custom operations that use page pagination.

func NewUncountedCollectionEnvelope

func NewUncountedCollectionEnvelope[T any](ctx context.Context, data []T, page, perPage int) CollectionEnvelope[T]

NewUncountedCollectionEnvelope creates a collection body for operations that decline the per-request count. Total and Pages are omitted from the response.

type CollectionOutput

type CollectionOutput[T any] struct {
	Body CollectionEnvelope[T]
}

CollectionOutput can be returned directly from a custom typed Huma operation.

func NewCollectionOutput

func NewCollectionOutput[T any](ctx context.Context, data []T, page, perPage, total, pages int) *CollectionOutput[T]

NewCollectionOutput creates a custom-operation collection output using Soro's canonical request ID from ctx.

type Config

type Config struct {
	Title                            string
	Version                          string
	BasePath                         string
	OpenAPIPath                      string
	DocsPath                         string
	SchemasPath                      string
	MaxBodyBytes                     int64
	RequestTransactionTimeout        time.Duration
	MaxConcurrentRequestTransactions int
	// RequestTransactionWait bounds how long a request waits for an admission
	// slot before being shed with 503. Zero sheds immediately.
	RequestTransactionWait time.Duration
}

func DefaultConfig

func DefaultConfig() Config

func (Config) Validate

func (config Config) Validate() error

type ErrorBody

type ErrorBody struct {
	Code      string              `json:"code"`
	Message   string              `json:"message"`
	RequestID string              `json:"request_id,omitempty"`
	Fields    map[string][]string `json:"fields,omitempty"`
}

type ErrorEnvelope

type ErrorEnvelope struct {
	Error ErrorBody `json:"error"`
}

type Meta

type Meta struct {
	RequestID string `json:"request_id"`
}

Meta is included in every successful JSON response. RequestID is generated by Soro and matches the X-Request-ID response header.

type Middleware

type Middleware func(http.Handler) http.Handler

type Option

type Option func(*settings)

func WithAudienceAuthorizer

func WithAudienceAuthorizer(authorizer AudienceAuthorizer) Option

func WithLogger

func WithLogger(logger *slog.Logger) Option

func WithMiddleware

func WithMiddleware(middleware ...Middleware) Option

func WithPreTransactionMiddleware

func WithPreTransactionMiddleware(middleware ...Middleware) Option

WithPreTransactionMiddleware installs middleware outside the request transaction. Use it for credential authentication that must establish the principal consumed by a request transaction initializer.

func WithRequestIDGenerator

func WithRequestIDGenerator(generator func() string) Option

func WithRequestTransaction

func WithRequestTransaction(db *database.DB, initialize RequestTransactionInitializer) Option

WithRequestTransaction wraps every API request in one database transaction and runs initialize immediately after it begins. Responses are withheld until commit succeeds. Without this option, requests retain the existing non-transactional behavior.

func WithWebhookOptions

func WithWebhookOptions(options ...webhooks.Option) Option

WithWebhookOptions configures the public webhook ingress registry that shares this API's HTTP mux and outer middleware chain.

type PaginationMeta

type PaginationMeta struct {
	RequestID string `json:"request_id"`
	Page      int    `json:"page"`
	PerPage   int    `json:"per_page"`
	Total     *int   `json:"total,omitempty"`
	Pages     *int   `json:"pages,omitempty"`
}

PaginationMeta describes page-based collection results. PaginationMeta describes one page of a collection. Total and Pages are pointers because a resource may disable the per-request count: they are omitted when counting is off and always present otherwise, so a legitimate zero stays distinguishable from an absent count.

type Registrar

type Registrar interface {
	Register(*Router, string) error
}

type RequestTransactionInfo

type RequestTransactionInfo struct {
	Request     *http.Request
	OperationID string
	Owner       string
	Audience    AudiencePolicy
	PathValues  map[string]string
}

RequestTransactionInfo describes the matched operation before its handler or audience middleware runs. Health, metrics, docs, and unmatched paths are not request-transactional and never reach the initializer.

type RequestTransactionInitializer

type RequestTransactionInitializer func(context.Context, RequestTransactionInfo, database.LocalSettings) (context.Context, error)

RequestTransactionInitializer resolves trusted application context and transaction-local PostgreSQL settings before any API operation runs.

type Resource

type Resource[T, C, U, R any] struct {
	// contains filtered or unexported fields
}

Resource implements the five conventional REST operations for one model.

func NewResource

func NewResource[T, C, U, R any](config ResourceConfig[T, C, U, R]) (*Resource[T, C, U, R], error)

func (*Resource[T, C, U, R]) Register

func (resource *Resource[T, C, U, R]) Register(router *Router, path string) error

type ResourceConfig

type ResourceConfig[T, C, U, R any] struct {
	Name         string
	Repository   *repository.Repository[T]
	Serializer   serializer.Serializer[T, R]
	CreateEntity func(context.Context, C) (*T, error)
	UpdateEntity func(context.Context, *T, U) error
	Query        query.Definition
	Disabled     []Action
	Authorize    ResourceHook[T]
	// AuthorizeWrite validates the final entity at the persistence boundary for
	// create, update, and destroy. Engine resources with any of those actions
	// enabled must provide it unless they explicitly opt into global access
	// with AllowUnscoped.
	AuthorizeWrite ResourceHook[T]
	Before         ResourceHook[T]
	After          ResourceHook[T]
	Scope          func(context.Context, *bun.SelectQuery) *bun.SelectQuery
	// AllowUnscoped explicitly marks a resource as global. Engine resources
	// must set this or provide Scope or Authorize.
	AllowUnscoped   bool
	Audience        AudiencePolicy
	Audiences       map[Action]AudiencePolicy
	ModifyOperation map[Action]func(*huma.Operation)
}

ResourceConfig describes a typed REST resource. CreateEntity and UpdateEntity are deliberately explicit so API input can never be reflectively assigned to a persistence model.

type ResourceHook

type ResourceHook[T any] func(context.Context, Action, *T) error

type Route

type Route struct {
	Method         string
	Path           string
	OperationID    string
	Owner          string
	Tags           []string
	Audience       Audience
	RequiredScopes []string
	ClientAudience string
}

type Router

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

func (*Router) Huma

func (router *Router) Huma() huma.API

func (*Router) Prefix

func (router *Router) Prefix() string

func (*Router) Resource

func (router *Router) Resource(path string, resource Registrar) error

func (*Router) Scope

func (router *Router) Scope(owner, path string) (*ScopedRouter, error)

Scope creates an engine-owned router below this version router. The mount path is absolute within the version, while operations registered through the returned router use relative paths.

type ScopedRouter

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

ScopedRouter is the engine-facing API registration boundary. It does not expose the underlying mux or Huma API.

func (*ScopedRouter) Freeze

func (router *ScopedRouter) Freeze()

func (*ScopedRouter) Frozen

func (router *ScopedRouter) Frozen() bool

func (*ScopedRouter) Owner

func (router *ScopedRouter) Owner() string

func (*ScopedRouter) Prefix

func (router *ScopedRouter) Prefix() string

func (*ScopedRouter) Resource

func (router *ScopedRouter) Resource(path string, resource Registrar) error

Resource registers a resource below the engine scope. A path of "/" mounts the resource collection at the scope root.

type StatusError

type StatusError struct {
	ErrorEnvelope
	// contains filtered or unexported fields
}

func (*StatusError) ContentType

func (response *StatusError) ContentType(contentType string) string

func (*StatusError) Error

func (response *StatusError) Error() string

func (*StatusError) GetStatus

func (response *StatusError) GetStatus() int

type SuccessEnvelope

type SuccessEnvelope[T any] struct {
	Data T    `json:"data"`
	Meta Meta `json:"meta"`
}

SuccessEnvelope is the standard body for one successful result.

type SuccessOutput

type SuccessOutput[T any] struct {
	Body SuccessEnvelope[T]
}

SuccessOutput can be returned directly from a custom typed Huma operation.

func NewSuccessOutput

func NewSuccessOutput[T any](ctx context.Context, data T) *SuccessOutput[T]

NewSuccessOutput creates a custom-operation output using Soro's canonical request ID from ctx.

Jump to

Keyboard shortcuts

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