Help us shape the future of Go! Take the 2026 Go Developer Survey

api

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: AGPL-3.0 Imports: 32 Imported by: 0

Documentation

Overview

Package api is the REST layer: it mounts every operation registered in kernel.APIRegistry under /api/v1 (with scope enforcement, panic recovery and a JSON default content type), serves the generated OpenAPI 3.1 document at /api/v1/openapi.json and the Scalar reference at /api/docs, and provides the core operations (/site, /me, /health) plus helpers (pagination, decoding, error envelopes) for other modules' handlers.

Index

Constants

View Source
const (
	CodeBadRequest       = "bad_request"
	CodeInvalidJSON      = "invalid_json"
	CodeEmptyBody        = "empty_body"
	CodeUnauthorized     = "unauthorized"
	CodeForbidden        = "forbidden"
	CodeNotFound         = "not_found"
	CodeMethodNotAllowed = "method_not_allowed"
	CodePayloadTooLarge  = "payload_too_large"
	CodeValidation       = "validation_failed"
	CodeRateLimited      = "rate_limited"
	CodeInternal         = "internal_error"
)

Error codes used in the error envelope.

View Source
const (
	DefaultPerPage = 20
	MaxPerPage     = 100
)

Pagination defaults.

View Source
const ContractVersion = "1"

Variables

View Source
var CoreVersion = "dev"

CoreVersion is the binary build version; Version remains the API family.

View Source
var PageParams = []kernel.APIParam{
	{Name: "page", In: "query", Type: "integer", Description: "Page number, from 1 (default 1)."},
	{Name: "per_page", In: "query", Type: "integer", Description: "Items per page (default 20, max 100)."},
	{Name: "sort", In: "query", Type: "string", Description: "Sort field; prefix with - for descending."},
	{Name: "order", In: "query", Type: "string", Description: "asc or desc."},
}

PageParams documents ?page, ?per_page, ?sort and ?order for list operations: append it to APIOperation.Params.

View Source
var Version = "0.1.0"

Version is the API version reported in the OpenAPI document. It is a var so main can set it from its build version (or -ldflags "-X github.com/zadstack/gorun/internal/api.Version=…").

Functions

func Decode

func Decode(w http.ResponseWriter, r *http.Request, v any) error

Decode reads a single JSON value from the request body into v: at most 1 MiB (w lets net/http close the connection on oversize bodies), unknown fields rejected. Errors are *DecodeError (400 empty_body / invalid_json with field hints, or 413); pass them to Fail or ParseValidation.

func Fail

func Fail(w http.ResponseWriter, r *http.Request, err error)

Fail writes the envelope that fits err: decode errors 400/413, validation 422 with fields, known not-found sentinels (or errors with a NotFound() bool method returning true) 404, anything else 500 with a generic message (the error is logged, never sent).

func NotFound

func NotFound(w http.ResponseWriter)

NotFound writes the 404 envelope.

func Op

func Op(method, path, summary string, tags, scopes []string, req, resp any, h http.HandlerFunc) kernel.APIOperation

Op builds a kernel.APIOperation. path may be given with or without the /v1 prefix ("/content/{id}" and "/v1/content/{id}" are equivalent; chi patterns, including {id:regex}, are allowed). req and resp are example values whose types become the OpenAPI request body and the envelope's data schema (nil for none; a slice for list operations). Every scope in scopes is enforced with auth.Can before h runs. Add query parameters to the returned value's Params (see PageParams).

func Pagination

func Pagination(r *http.Request) (page, perPage int)

Pagination reads ?page and ?per_page: page defaults to 1, per_page to 20 and is capped at 100; invalid values fall back to the defaults.

func ParseValidation

func ParseValidation(err error) (status int, fields map[string]string)

ParseValidation classifies err for an HTTP response: a *DecodeError gives its status (400/413) and hints; a FieldErrorer, or any error in the chain that is a struct (pointer) with a string-to-string map field named Fields (content.ValidationError, auth.ValidationError), gives 422 and the fields. Other errors give (0, nil).

func Sort

func Sort(r *http.Request, allowed []string) (by, dir string)

Sort reads ?sort and ?order against allowed (the first entry is the default). Without a valid ?sort it returns allowed[0] descending (newest first); an explicit ?sort is ascending unless ?order=desc or the field is prefixed with "-". Unknown fields and orders are ignored, so the result is safe to interpolate into ORDER BY.

func WriteError

func WriteError(w http.ResponseWriter, status int, code, msg string, fields map[string]string)

WriteError writes the error envelope; empty fields are omitted.

func WriteItem

func WriteItem(w http.ResponseWriter, v any)

WriteItem writes a 200 envelope around v.

func WriteList

func WriteList(w http.ResponseWriter, items any, total, page, perPage int)

WriteList writes a 200 list envelope with pagination meta; a nil slice is written as [] (never null).

Types

type DecodeError

type DecodeError struct {
	Status  int    // 400 or 413
	Code    string // invalid_json or payload_too_large
	Message string
	Fields  map[string]string // field → hint, when known
}

DecodeError is returned by Decode for a body that cannot be bound.

func (*DecodeError) Error

func (e *DecodeError) Error() string

type FieldErrorer

type FieldErrorer interface {
	FieldErrors() map[string]string
}

FieldErrorer is implemented by validation errors that carry per-field messages (field → message or i18n key). ParseValidation and Fail map them to 422 validation_failed.

type Health

type Health struct {
	Status   string `json:"status" enum:"ok"`
	Database string `json:"database" enum:"ok"`
}

Health is GET /health (the data /readyz checks).

type Me

type Me struct {
	Kind   string   `json:"kind" enum:"user,api_key"`
	User   MeUser   `json:"user"`
	APIKey *MeKey   `json:"api_key" doc:"Set when authenticated with an API key"`
	Scopes []string `json:"scopes" doc:"Effective permissions (role ∩ key scopes)"`
}

Me is GET /me: who is calling and what they may do.

type MeKey

type MeKey struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

MeKey identifies the API key a request was made with.

type MeUser

type MeUser struct {
	ID     string `json:"id"`
	Email  string `json:"email"`
	Name   string `json:"name"`
	Role   string `json:"role"`
	Locale string `json:"locale"`
}

MeUser is the acting user in GET /me.

type Module

type Module struct{}

Module is the api kernel.Module.

func New

func New() *Module

New returns the api module.

func (*Module) Migrations

func (*Module) Migrations() fs.FS

Migrations implements kernel.Module (the api module has no tables).

func (*Module) Name

func (*Module) Name() string

Name implements kernel.Module.

func (*Module) Register

func (*Module) Register(d *kernel.Deps) error

Register adds the core operations and installs the routes.

Mounting is lazy: modules after api in BootOrder (admin, commerce, wpmigrate, mcp, ext) register operations after this Register returns, so one catch-all handler on /api/v1/* builds the operation router from d.API.Operations() on the first request (sync.Once) and never rebuilds it; operations added after serving starts are ignored. The OpenAPI document is built from the same snapshot, also once.

type SiteInfo

type SiteInfo struct {
	Title   string `json:"title"`
	Tagline string `json:"tagline"`
	BaseURL string `json:"base_url"`
	Locale  string `json:"locale" doc:"Default site locale (site.locale)"`
	Dir     string `json:"dir" enum:"rtl,ltr"`
}

SiteInfo is the site identity block of GET /site.

type SiteStructure

type SiteStructure struct {
	Site       SiteInfo             `json:"site"`
	Types      []content.Type       `json:"types"`
	Taxonomies []taxonomy.Taxonomy  `json:"taxonomies"`
	Menus      []menus.Menu         `json:"menus"`
	Theme      *theme.Info          `json:"theme" doc:"Active theme (null when no theme module is installed)"`
	Templates  []theme.TemplateFile `json:"templates"`
}

SiteStructure is GET /site: everything an agent or headless client needs to understand the site. Lists of absent modules are empty, never null.

Directories

Path Synopsis
Package apitest boots GoRun for API tests and drives /api/v1 with a bearer API key.
Package apitest boots GoRun for API tests and drives /api/v1 with a bearer API key.

Jump to

Keyboard shortcuts

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