Documentation
¶
Overview ¶
Package stdocs turns a standard library net/http.ServeMux into a self-documenting API: routes registered on the wrapped mux are served as interactive documentation — a /docs UI rendered by Scalar, Swagger UI, Redoc, Stoplight Elements, or a built-in page — backed by a generated OpenAPI 3.0, 3.1, or 3.2 document.
The pattern syntax it documents ("GET /users/{id}") is the method+path routing introduced in Go 1.22. The module supports the two most recent Go releases, matching the Go project's release policy. There are no dependencies beyond the standard library and no code generation: the patterns you already write are the source of truth.
Two ways to use it ¶
New returns a Mux — an *http.ServeMux wrapper that records route metadata as you register handlers and generates the OpenAPI document from it. This is the recommended way to use stdocs.
DocsHandler serves a docs UI for a hand-written OpenAPI document supplied via WithSpec, without introspecting any mux.
Example ¶
type User struct {
ID string `json:"id" doc:"Unique ID"`
Name string `json:"name" minLength:"1" maxLength:"100"`
}
func getUser(w http.ResponseWriter, r *http.Request) { /* ... */ }
func main() {
mux := stdocs.New(
stdocs.WithTitle("My API"),
stdocs.WithVersion(stdocs.OpenAPI31),
stdocs.WithBearerAuth("bearerAuth", "JWT"),
)
mux.HandleFunc("GET /users/{id}", getUser,
stdocs.Summary("Get user by id"),
stdocs.WithResponse(200, User{}),
stdocs.WithSecurity("bearerAuth"),
)
mux.Mount() // docs UI at /docs/, spec at /docs/openapi.json
log.Fatal(http.ListenAndServe(":8080", mux))
}
Serve the mux itself — it is the http.Handler for your routes.
Options and route opts ¶
Mux-level configuration uses Option values passed to New or DocsHandler (all named With*, e.g. WithTitle, WithVersion, WithDocsPrefix, WithDisabled). Per-route documentation uses RouteOpt values passed to Mux.HandleFunc / Mux.Handle: bare-named opts set simple operation metadata (Summary, Description, Tags, Deprecated, OperationID, Optional, Hidden, Internal), while opts that attach bodies, responses, parameters, or security are named With* (WithBody, WithResponse, WithParam, WithSecurity, ...). Opts combines route opts into reusable bundles. Parameter declarations take ParamOpt refinements (ParamRequired, ParamDefault, ParamMinimum, ...).
Smart defaults ¶
Undocumented routes still document themselves: the summary is inferred from the handler's function name (getUser → "Get user"; closures excluded), the tag from the first non-version path segment (/v1/tasks groups under Tasks; WithTagFunc overrides the inference, and a matching WithTag declaration's casing is adopted), path parameters from the pattern's wildcards, a 200 response when none is declared, and a document-unique operationId from the method and path (get_users_by_id) that stays stable across rebuilds. Operations carrying a security requirement also document a 401 — see WithAutoUnauthorized.
Invalid documentation input — an unknown parameter type, a constraint tag on the wrong field type, an unparseable example — panics at registration or document-build time rather than publishing a wrong contract.
Field tags ¶
Struct fields reflected into schemas (via WithBody, WithResponse, WithParams, or webhook payloads) carry documentation and constraints in tags:
type CreateTask struct {
Title string `json:"title" doc:"Short title" minLength:"1" maxLength:"200"`
Priority int `json:"priority" minimum:"1" maximum:"5" default:"3" example:"2"`
Status string `json:"status" enum:"pending,active,done"`
Tags []string `json:"tags" maxItems:"10" uniqueItems:"true"`
Email string `json:"email" format:"email"`
}
doc: (or description:) sets the schema description. The constraint vocabulary is minimum, maximum, exclusiveMinimum, exclusiveMaximum (numeric fields), minLength, maxLength, pattern (string fields), minItems, maxItems, uniqueItems (slice and array fields), and enum, default, example, format (any scalar field). Values are parsed according to the field's type — enum:"1,2,3" on an int emits numbers — and validated against it; a misapplied or unparseable constraint panics at registration (WithParams structs) or document-build time (bodies, responses, webhooks). Exclusive bounds render per version: the boolean draft-4 form on 3.0, numeric 2020-12 keywords on 3.1/3.2.
Required-ness follows the encoding/json contract: every non-pointer field without omitempty/omitzero is required. An explicit required tag overrides the contract in both directions — required:"true" forces a field into the required list (with a pointer field, that documents required-but-nullable), and required:"false" keeps it out. Maps document as objects whose additionalProperties schema comes from the value type.
When reflection cannot infer a field's wire format, the openapi tag is the per-field escape hatch: openapi:"-" excludes the field from the document (JSON serialization is unaffected), openapi:"type=string,format=date-time" replaces the reflected schema entirely — constraint and doc tags still compose on top — and a bare openapi:"nullable" stacks with reflection, decoupling wire-level null from Go pointers (with required:"true", that is required-but-nullable without changing the Go type).
Composing view types: when a list endpoint returns a subset of a canonical model (an OrderSummary next to Order), share the common fields through an embedded core instead of re-declaring them — reflection flattens embedded structs with encoding/json's exact dominance rules (shallower wins, a lone tagged field beats untagged rivals, unresolvable collisions drop the field), so the documented shape and the served JSON stay in agreement by construction:
type OrderCore struct {
ID string `json:"id" required:"true"`
Status string `json:"status" enum:"open,paid,refunded"`
}
type Order struct {
OrderCore
Items []Item `json:"items"`
}
type OrderSummary struct{ OrderCore }
The embedded core appears as its own component schema; that is expected, not a leak. Fields promoted through a pointer embed are documented per their own declarations — a nil embed omits them all at serialization time, an all-or-nothing gap OpenAPI cannot express and DriftWarn exists to catch. There is deliberately no doc-only subset helper: encoding/json has no way to drop a promoted field at serialization time, so a document trimmed below what the handler writes would be precisely the divergence DriftWarn reports.
Parameters ¶
Path parameters come from the pattern's wildcards automatically. Query, header, and cookie parameters are declared either inline — QueryParam, HeaderParam, CookieParam, or the general WithParam — refined with ParamOpt values:
stdocs.QueryParam("limit", "integer", "Page size",
stdocs.ParamDefault(20), stdocs.ParamMinimum(1), stdocs.ParamMaximum(100))
or as a struct via WithParams, sharing the field-tag vocabulary:
type ListParams struct {
Cursor string `query:"cursor" doc:"Opaque pagination cursor"`
Limit int `query:"limit" default:"20" minimum:"1" maximum:"100"`
Trace string `header:"X-Trace-Id" required:"true"`
}
Responses ¶
WithResponse declares a response per status; response-decorating opts (WithResponseDescription, WithResponseHeader, WithResponseExample, WithResponseContentType) are order-independent. Status 0 declares the OpenAPI "default" response — the catch-all consumers fall back to for undeclared statuses; in plain terms, "any status not listed here: expect this shape", conventionally the shared error body. A response with a string body and a non-JSON content type documents raw downloads: WithResponse(200, "") + WithResponseContentType(200, "text/csv"). WithDefaultResponse declares a response once at the mux level (typically the shared error envelope) and documents it on every operation that does not declare the status itself. WithMultipartBody documents multipart/form-data file uploads from FilePart and FieldPart declarations. WithRawResponse documents raw responses (CSV, plain text, downloads) as a string-typed body under a given content type in one opt, and WithFallbackResponse is the route-scoped counterpart of WithDefaultResponse — built for Opts bundles, so codebases with several error-shape eras declare one fallback per era (explicit declarations win, then route fallbacks, then mux defaults). Both forms have raw twins, WithFallbackRawResponse and WithDefaultRawResponse, so a plain-text error era bundles the same way a JSON one does; precedence is unchanged, and a WithResponseContentType already on the route survives a raw fallback's content type.
Visibility ¶
Hidden excludes a route from the document everywhere; Internal excludes it unless the mux was built with WithInternal(true), in which case the operation carries the conventional x-internal: true extension. Excluded routes leave no trace — no paths, schemas, or operation-id effects — and still serve traffic: visibility shapes documentation, it is not access control.
Mounting and toggling ¶
Mux.Mount registers the docs handler on the mux itself under the docs prefix (default /docs, configurable with WithDocsPrefix); Mux.Docs returns the same handler for mounting elsewhere or wrapping in middleware. Both accept an optional bool — mux.Mount(env != "prod") — and an explicit per-call value wins over WithDisabled in both directions. Disabled docs serve 404; the spec stays buildable via Mux.JSON and Mux.YAML.
Mount registers the docs on the mux itself, so blanket auth middleware wrapped around the mux guards the docs page too — often surprising in dev. Skip the docs prefix in the middleware when the docs should stay open:
func auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/docs" || strings.HasPrefix(r.URL.Path, "/docs/") {
next.ServeHTTP(w, r) // docs stay public
return
}
// ... verify credentials ...
next.ServeHTTP(w, r)
})
}
When the mux is mounted under a prefix the application never sees — net/http.StripPrefix or a stripping reverse proxy — WithPathPrefix prepends the public prefix to every documented path.
Docs UIs ¶
The default docs page is a dependency-free ~1.6 KB route list. Four rich UIs ship as sub-packages, each in two flavors: a CDN variant pinning exact versions with sha384 subresource-integrity hashes, and an embedded variant vendoring the npm bundle bytes for air-gapped builds:
import "github.com/FumingPower3925/stdocs/ui/scalar" // CDN, SRI-pinned
import "github.com/FumingPower3925/stdocs/ui/scalaremb" // embedded
mux := stdocs.New(stdocs.WithTitle("My API"), scalar.WithUI())
The packages are ui/scalar, ui/swaggerui, ui/redoc, and ui/stoplight, plus their *emb twins. SRI means subresource integrity: the CDN script tags carry sha384 hashes browsers verify before executing the fetched assets. The embedded twins serve their bundles from the binary — Mux.Mount registers their asset route automatically; only when mounting the handler manually via Mux.Docs does the asset route need its own registration:
mux.ServeMux.Handle("GET /docs/", mux.Docs())
mux.ServeMux.Handle("GET /docs/_assets/",
http.StripPrefix("/docs/_assets/", scalaremb.AssetHandler()))
Try-it requests and drift ¶
The rich UIs' try-it consoles send real requests. FromDocs identifies them (best-effort, Referer-based) so middleware can restrict what they may do — never use it to grant access. DriftWarn wraps the mux in a development aid that warns when a handler returns a status the document does not declare or serves a response with a content type contradicting the declared body; with DriftSampleBodies it additionally compares response bodies' keys — top-level and one level of list rows — against the documented schema. DriftNotify delivers every finding as a DriftFinding with a stable Code, turning replayed traffic into a CI gate.
Component names ¶
Schema component names come from the Go type name; same-named types from different packages get numeric suffixes, and generic instantiations simplify to readable identifiers (Page[main.Task] → Page_Task). A type can name itself — useful because component names become class names in generated clients — by implementing:
func (TaskPage) SchemaName() string { return "TaskPage" }
The override wins over every automatic rule; collisions are still suffixed.
Using the spec downstream ¶
Mux.JSON and Mux.YAML return the exact bytes served at the spec endpoints. Output is deterministic — sorted keys, stable operationIds and component names across rebuilds — so the spec works as a committed artifact: golden-file tests, contract diffing in PRs, linting, and client generation. For documents published as contracts, WithCleanOutput strips the stdocs annotation extensions and auto-generated descriptions, and Mux.Lint reports advisory consumability findings (operations without error responses, untyped fields, collision-suffixed names). Determinism holds within a release; upgrading stdocs itself may legitimately change the emitted bytes, so regenerate golden files when bumping the dependency and review the diff like any contract change.
Generator notes: current Go client generators (ogen, oapi-codegen) reject the numeric exclusive-bound form that 3.1/3.2 correctly emit — generate from the 3.0.4 document when exclusiveMinimum or exclusiveMaximum tags are in play (Mux.Lint warns about this), and oapi-codegen consumes 3.0 documents only. oapi-codegen also generates a spurious "<nil>" constant from nullable enums (the null member 3.0 legally requires; an upstream bug) — prefer non-nullable enum fields (no pointer and no openapi:"nullable") in generator-facing contracts. On 3.1/3.2, nullability combined with a default, uniqueItems, or byte format produces anyOf forms that ogen releases before v1.17.0 reject — and oapi-codegen, consuming 3.0 only, never sees (Mux.Lint warns: nullable-facet-generators); the 3.0.4 document handles them all. An explicit Webhook.Security requirement trips the same ogen webhook-codegen bug that motivated the security: [] default — prefer documenting webhook auth in the description until upstream fixes it. openapi-typescript and similar TypeScript generators consume either version directly; enums become string-literal unions and SchemaName methods control the generated type names. A document-level default response (WithDefaultResponse with status 0) enables ogen's typed convenient-error handling; DriftWarn still checks the default entry's body and content-type contracts, but a default entry covers every status, so undeclared-status findings are off the table — weigh that against the generator convenience when drift gating matters. WithOpenAPI registers a hook that may mutate the document before caching, as an escape hatch for anything stdocs does not expose; Mux.Refresh forces a rebuild.
OpenAPI versions ¶
stdocs emits the latest patch of each supported minor: OpenAPI30 (3.0.4, the default), OpenAPI31 (3.1.2), and OpenAPI32 (3.2.0). Select one with WithVersion. For 3.2, WithSelfURL sets the document's canonical URI ($self — the URL at which the published document itself lives). All three outputs are validated against external validators in CI.
Scope and non-goals ¶
stdocs documents stdlib ServeMux applications; it does not integrate with third-party routers, does not validate requests or enforce the documented contract at runtime, and uses no code generation, comment annotations, or dependencies — permanently, by design. The document describes intent; keeping handlers honest is the application's job (DriftWarn helps notice when they are not).
Index ¶
- Constants
- func DocsHandler(opts ...Option) http.Handler
- func DriftWarn(m *Mux, logf func(format string, args ...any), opts ...DriftOption) http.Handler
- func FromDocs(r *http.Request, docsPrefix string) bool
- type BodyPart
- type Config
- type Contact
- type DefaultResponse
- type DriftFinding
- type DriftOption
- type Info
- type License
- type Mux
- func (m *Mux) Config() *Config
- func (m *Mux) Docs(enabled ...bool) http.Handler
- func (m *Mux) FromDocs(r *http.Request) bool
- func (m *Mux) Handle(p string, h http.Handler, opts ...RouteOpt)
- func (m *Mux) HandleFunc(p string, h func(http.ResponseWriter, *http.Request), opts ...RouteOpt)
- func (m *Mux) JSON() ([]byte, error)
- func (m *Mux) Lint() []Warning
- func (m *Mux) Mount(enabled ...bool)
- func (m *Mux) Refresh()
- func (m *Mux) YAML() ([]byte, error)
- type OAuthFlow
- type OAuthFlows
- type Operation
- type Option
- func WithAPIKeyAuth(name, in, paramName string) Option
- func WithAPIVersion(v string) Option
- func WithAutoUnauthorized(enabled bool) Option
- func WithBasicAuth(name string) Option
- func WithBearerAuth(name, bearerFormat string) Option
- func WithCleanOutput(enabled bool) Option
- func WithContact(name, email, url string) Option
- func WithDefaultRawResponse(status int, contentType string) Option
- func WithDefaultResponse(status int, body any) Option
- func WithDefaultSummary(template string) Option
- func WithDescription(s string) Option
- func WithDisabled(disabled bool) Option
- func WithDocsPrefix(prefix string) Option
- func WithExternalDocs(url, description string) Option
- func WithGlobalSecurity(name string, scopes ...string) Option
- func WithInternal(show bool) Option
- func WithLicense(name, url string) Option
- func WithOAuth2Auth(name string, flows OAuthFlows) Option
- func WithOpenAPI(fn func(doc map[string]any)) Option
- func WithOperationIDFunc(f func(method, path string) string) Option
- func WithPathPrefix(prefix string) Option
- func WithSPDXLicense(name, identifier string) Option
- func WithSecurityScheme(name string, scheme SecurityScheme) Option
- func WithSelfURL(selfURL string) Option
- func WithServer(url, description string) Option
- func WithSpec(specJSON []byte) Option
- func WithTag(name, description string) Option
- func WithTagExternalDocs(tag, url, description string) Option
- func WithTagFunc(f func(method, path string) string) Option
- func WithTitle(title string) Option
- func WithVersion(v SpecVersion) Option
- func WithWebhooks(hooks map[string]Webhook) Option
- type Param
- type ParamOpt
- func ParamDefault(v any) ParamOpt
- func ParamEnum(values ...any) ParamOpt
- func ParamExample(v any) ParamOpt
- func ParamExclusiveMaximum(n float64) ParamOpt
- func ParamExclusiveMinimum(n float64) ParamOpt
- func ParamFormat(format string) ParamOpt
- func ParamItems(typ string) ParamOpt
- func ParamMaxItems(n uint64) ParamOpt
- func ParamMaxLength(n uint64) ParamOpt
- func ParamMaximum(n float64) ParamOpt
- func ParamMinItems(n uint64) ParamOpt
- func ParamMinLength(n uint64) ParamOpt
- func ParamMinimum(n float64) ParamOpt
- func ParamPattern(pattern string) ParamOpt
- func ParamRequired() ParamOpt
- func ParamUniqueItems() ParamOpt
- type PathItem
- type RequestBody
- type Response
- type RouteOpt
- func CookieParam(name, typ, desc string, opts ...ParamOpt) RouteOpt
- func Deprecated() RouteOpt
- func Description(s string) RouteOpt
- func ExternalDocs(url, description string) RouteOpt
- func HeaderParam(name, typ, desc string, opts ...ParamOpt) RouteOpt
- func Hidden() RouteOpt
- func Internal() RouteOpt
- func OperationID(id string) RouteOpt
- func Optional() RouteOpt
- func Opts(opts ...RouteOpt) RouteOpt
- func QueryParam(name, typ, desc string, opts ...ParamOpt) RouteOpt
- func Summary(s string) RouteOpt
- func Tags(names ...string) RouteOpt
- func WithBody(body any) RouteOpt
- func WithBodyContentType(ct string) RouteOpt
- func WithExample(value any) RouteOpt
- func WithFallbackRawResponse(status int, contentType string) RouteOpt
- func WithFallbackResponse(status int, body any) RouteOpt
- func WithMultipartBody(parts ...BodyPart) RouteOpt
- func WithNoSecurity() RouteOpt
- func WithParam(name, in, typ, description string, opts ...ParamOpt) RouteOpt
- func WithParams(v any) RouteOpt
- func WithRawResponse(status int, contentType string) RouteOpt
- func WithResponse(status int, body any) RouteOpt
- func WithResponseContentType(status int, contentType string) RouteOpt
- func WithResponseDescription(status int, description string) RouteOpt
- func WithResponseExample(status int, value any) RouteOpt
- func WithResponseHeader(status int, name, typ, description string) RouteOpt
- func WithSecurity(name string, scopes ...string) RouteOpt
- type SecurityRequirement
- type SecurityScheme
- type SecuritySchemeType
- type Server
- type SpecInput
- type SpecVersion
- type TagDecl
- type Warning
- type Webhook
Examples ¶
Constants ¶
const ( SecurityHTTP = spec.SecurityHTTP SecurityAPIKey = spec.SecurityAPIKey SecurityOAuth2 = spec.SecurityOAuth2 SecurityOpenIDConnect = spec.SecurityOpenIDConnect SecurityAPIKeyInHeader = spec.SecurityAPIKeyInHeader SecurityAPIKeyInQuery = spec.SecurityAPIKeyInQuery SecurityAPIKeyInCookie = spec.SecurityAPIKeyInCookie )
const ( // OpenAPI30 is the latest 3.0.x patch (3.0.4), the default. OpenAPI30 = version.OpenAPI30 // OpenAPI31 is the latest 3.1.x patch (3.1.2). OpenAPI31 = version.OpenAPI31 // OpenAPI32 is the latest 3.2.x patch (3.2.0). OpenAPI32 = version.OpenAPI32 )
The version constants accepted by WithVersion: the latest patch of each supported OpenAPI minor.
Variables ¶
This section is empty.
Functions ¶
func DocsHandler ¶
DocsHandler returns an http.Handler that serves the docs UI and a static OpenAPI spec. This is the Tier-1 entry point: it does not introspect any mux. Provide the document with WithSpec; without it, a minimal valid placeholder built from WithTitle / WithAPIVersion / WithVersion is served, so every bundled UI still renders.
The handler serves:
GET <prefix>/ -> the HTML docs UI GET <prefix>/openapi.json -> the spec as JSON GET <prefix>/openapi.yaml -> the spec as YAML
To customise the docs UI, pass a UI option from one of the github.com/FumingPower3925/stdocs/ui/* sub-packages:
mux := http.NewServeMux()
mux.Handle("GET /docs/", stdocs.DocsHandler(
stdocs.WithTitle("My API"),
scalar.WithUI(),
))
mux.HandleFunc("GET /users", listUsers)
If you want a spec generated from registered routes instead, use Tier 2: build the application on a *stdocs.Mux (see New).
If WithDisabled(true) is passed, the returned handler responds with 404 on every request, equivalent to Mux.Docs(false).
Example ¶
Serving a docs UI for a hand-written spec (Tier 1) — no route introspection involved.
package main
import (
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
spec := []byte(`{"openapi":"3.0.4","info":{"title":"Hand-written","version":"1.0.0"},"paths":{}}`)
mux := http.NewServeMux()
mux.Handle("GET /docs/", stdocs.DocsHandler(
stdocs.WithTitle("Hand-written"),
stdocs.WithSpec(spec),
))
fmt.Println("docs mounted")
}
Output: docs mounted
func DriftWarn ¶ added in v0.3.0
DriftWarn wraps the mux in a development aid that compares what handlers actually do against what the document claims, and logs a warning on divergence:
- a handler writes a status code its operation does not document (and no "default" response is declared), or
- a response documented with a JSON body is written with a non-JSON Content-Type.
Each (route, finding) pair warns once, so a hot endpoint does not flood the log. logf receives Printf-style arguments; nil means log.Printf.
handler := mux // production
if os.Getenv("ENV") == "dev" {
handler = stdocs.DriftWarn(mux, nil)
}
log.Fatal(http.ListenAndServe(":8080", handler))
DriftWarn builds the document up front, so the fail-fast panics from invalid constraint or params tags surface at the DriftWarn call; routes registered later are picked up automatically on the next request. A mux-level "default" response counts as documenting any status — but its body contract still applies: a JSON-documented default served with a non-JSON Content-Type is drift. DriftWarn observes what the mux's handlers write; it wraps the mux directly, so responses written by surrounding middleware (a recovery handler's 500, an auth layer's 401) are invisible to it.
DriftWarn is a development aid, not validation: it checks responses only, by design — request bodies and parameters are never sampled — and adds a small per-request bookkeeping cost, so wrap only in environments where the warnings are read. Routes excluded from the document (Hidden, non-shown Internal) and requests under the docs prefix are ignored. The documented-status set is the served document's: mux-level default responses and the automatic 401 count as documented. DriftNotify delivers the same findings in structured form for CI gates and dashboards.
func FromDocs ¶ added in v0.2.0
FromDocs reports whether r appears to originate from the interactive docs UI served under docsPrefix — that is, from a "Try it out" / "Test Request" console on the docs page rather than from a regular API client. An empty (or all-slash) docsPrefix means the default "/docs".
Detection is based on the Referer header: browsers attach the docs page's URL to the fetch calls the consoles make. The check matches on the URL path only, so it keeps working behind reverse proxies that add their own path prefix (a page at /api/docs/ still matches a docs prefix of "/docs").
FromDocs is a convenience guardrail, NOT a security control. The Referer header is fully client-controlled: a caller can forge it (false positives), and there are false negatives too — privacy extensions or a strict Referrer-Policy can strip the header, and try-it requests sent to a DIFFERENT origin (an absolute WithServer URL on another host) carry an origin-only Referer under browsers' default policy, so FromDocs reports false for them (Scalar additionally routes cross-origin try-it calls through its own proxy). Detection is reliable only when the docs page and the API share an origin — the normal stdocs setup, where one mux serves both. Use FromDocs only to RESTRICT what docs-originated traffic may do — never to grant access, skip authentication, or relax validation. Endpoints that must not be mutated by strangers need real authentication regardless.
The intended use is a small middleware in front of the mux:
guard := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && stdocs.FromDocs(r, "/docs") {
http.Error(w, "try-it requests cannot modify data", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
log.Fatal(http.ListenAndServe(":8080", guard(mux)))
Teams that prefer other policies can branch on FromDocs however they like: route writes to a scratch datastore, add a dry-run flag, tag the request for observability, and so on.
Example ¶
Restricting what try-it consoles may do: the docs page's "Try it out" buttons send real requests, identified (best-effort) by their Referer. FromDocs gates a restriction — never use it to grant access, since the header is client-controlled.
package main
import (
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
mux := stdocs.New(stdocs.WithTitle("My API"))
mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {})
mux.Mount()
guard := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && mux.FromDocs(r) {
http.Error(w, "try-it requests cannot modify data", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
_ = guard // pass guard(mux) to http.ListenAndServe
r, _ := http.NewRequest(http.MethodPost, "/users", nil)
r.Header.Set("Referer", "https://api.example.com/docs/")
fmt.Println("from docs:", mux.FromDocs(r))
}
Output: from docs: true
Types ¶
type BodyPart ¶ added in v0.4.0
type BodyPart struct {
// contains filtered or unexported fields
}
BodyPart is one part of a multipart/form-data request body; see WithMultipartBody.
type Config ¶
type Config struct {
// Info carries the OpenAPI "info" object.
Info Info
// Servers is the list of OpenAPI "servers".
Servers []Server
// Tags are the declared top-level tags. Tags attached to operations
// that aren't in this list are still emitted on the operation; the
// declarations are just for richer descriptions.
Tags []TagDecl
// DocsPrefix is the URL prefix under which the docs UI and spec are
// served. Defaults to "/docs".
DocsPrefix string
// SelfURL is the OpenAPI 3.2 "$self" field — the canonical URI
// of the document. When non-empty, the 3.2 emitter includes it
// in the spec root. Ignored for 3.0 and 3.1 (the field does
// not exist in those versions).
SelfURL string
// Version is the OpenAPI version to emit. Defaults to OpenAPI30.
Version SpecVersion
// DefaultSummary is a fallback summary used for routes that have
// neither a per-route Summary nor a function-name-based inference.
DefaultSummary string
// UIDoc is the HTML template for the docs UI page. The default is
// the small dependency-free page in stdocs.defaultUIDoc. UI
// sub-packages override this field via a stdocs.Option to swap in
// their own page. The template may contain {{.Title}} and
// {{.SpecURL}} placeholders; the page is rendered once, when the
// docs handler is constructed.
UIDoc string
// Hooks is the list of post-build callbacks registered via
// WithOpenAPI. Each is called once per spec build, with the
// emitted spec as a map[string]any, and may mutate it in place.
Hooks []func(doc map[string]any)
// Security is the list of registered security schemes. The user
// adds entries via WithSecurityScheme / WithBearerAuth / etc.;
// each is rendered into components.securitySchemes at build time.
Security []spec.NamedSecurityScheme
// GlobalSecurity is the default security requirement applied to
// every operation. Operations may override with WithSecurity or
// opt out with WithNoSecurity.
GlobalSecurity []SecurityRequirement
// Webhooks are emitted for 3.1 and 3.2 specs and ignored for 3.0
// (the field does not exist there). The map is keyed by webhook
// name.
Webhooks map[string]Webhook
// Disabled turns off the docs handler. When true, Mux.Docs and
// DocsHandler return a 404 handler instead of serving the UI and
// the spec. Mux.Mount respects this and registers nothing.
Disabled bool
// StaticSpec is a hand-written OpenAPI document (JSON bytes) set
// via WithSpec. It is served verbatim by DocsHandler (Tier 1) and
// ignored by *Mux (Tier 2), which generates its own document.
StaticSpec []byte
// ShowInternal controls whether routes marked with the Internal
// route opt appear in the generated document. Defaults to false
// (internal routes hidden). Set via WithInternal.
ShowInternal bool
// DefaultResponses are mux-level response entries documented on
// every operation that does not itself declare the same status.
// Populated via WithDefaultResponse.
DefaultResponses []DefaultResponse
// documented on operations that carry a security requirement.
// The zero value keeps the feature on; set via
// WithAutoUnauthorized(false).
DisableAutoUnauthorized bool
// PathPrefix is prepended to every documented path. Documentation
// only — routing is unaffected. Set via WithPathPrefix.
PathPrefix string
// CleanOutput strips stdocs vendor noise from the generated
// document. Set via WithCleanOutput.
CleanOutput bool
// ExternalDocs is the document-level externalDocumentation
// object. Set via WithExternalDocs.
ExternalDocs *spec.ExternalDocs
// OperationIDFunc overrides the automatic operationId derivation.
// Set via WithOperationIDFunc.
OperationIDFunc func(method, path string) string
// TagFunc overrides the automatic tag inference. Set via
// WithTagFunc.
TagFunc func(method, path string) string
// Assets, when non-nil, serves the docs UI's static assets. The
// embedded UI sub-packages set it from their WithUI option so
// Mount can register the <prefix>/_assets/ route automatically.
Assets http.Handler
}
Config holds the resolved configuration for an stdocs Mux. It is built by applying a list of Options to a fresh Config and is shared with the registry and the spec emitter.
Config is exported (rather than unexported like in many libraries) so that UI sub-packages (e.g. stdocs/ui/scalar) can mutate it via a stdocs.Option. UI sub-packages should not construct or copy a Config; they should only read or write the UIDoc field.
type Contact ¶
Contact is the OpenAPI "info.contact" object. Fields: Name, URL, Email. Set via WithContact; empty fields are omitted from the document.
type DefaultResponse ¶ added in v0.3.0
type DefaultResponse struct {
// Status is the HTTP status code; 0 means the OpenAPI "default"
// response.
Status int
// Body is a zero value whose type is reflected into the response
// schema, like the body argument of WithResponse; nil means no
// body. Ignored when RawContentType is set.
Body any
// RawContentType, when set, declares the entry as a raw
// string-typed body under this content type instead — the
// fallback/default twin of WithRawResponse. Populated via
// WithDefaultRawResponse and WithFallbackRawResponse.
RawContentType string
}
DefaultResponse is a mux-level response declaration applied to every documented operation; see WithDefaultResponse.
type DriftFinding ¶ added in v0.5.0
type DriftFinding struct {
// Code identifies the finding kind stably across releases —
// allow-lists and CI gates should match on it, never on Message
// prose, which is free to improve. The same discipline as
// [Warning].Code. The codes:
//
// build-failed the document could not be built;
// findings may be incomplete
// undeclared-status a handler wrote a status the document
// does not declare
// content-type-mismatch the served Content-Type contradicts
// the declared body
// body-kind-mismatch a sampled body is valid JSON of the
// wrong top-level kind — null, an array
// where an object is documented, or vice
// versa (DriftSampleBodies)
// missing-required-field a sampled body lacks a key its schema
// requires (DriftSampleBodies)
// undocumented-fields a sampled body carries keys its schema
// does not document (DriftSampleBodies)
Code string `json:"code"`
// Route is the registered pattern ("GET /tasks"); empty for
// document-level findings (build-failed).
Route string `json:"route"`
// Status is the observed response status; 0 for document-level
// findings.
Status int `json:"status"`
// Field locates body findings: a top-level key ("fee_cents"), a
// row path ("orders[].fee_cents", "[].id"), or the row prefix
// alone when rows carry undocumented fields ("orders[]"). Empty
// for everything else.
Field string `json:"field,omitempty"`
// Message is the human-readable line — the logged text without
// its "stdocs drift: " prefix.
Message string `json:"message"`
}
DriftFinding is one drift observation in structured form, delivered to DriftNotify callbacks alongside the log line.
type DriftOption ¶ added in v0.4.2
type DriftOption func(*driftWarner)
DriftOption configures DriftWarn.
func DriftNotify ¶ added in v0.5.0
func DriftNotify(fn func(DriftFinding)) DriftOption
DriftNotify registers fn to receive every finding DriftWarn would log, in structured form — the bridge from log lines to CI gates and dashboards. fn runs once per deduplicated finding, on the goroutine serving the request that observed it (the up-front build failure arrives on the DriftWarn caller's goroutine instead), so it must synchronize anything it touches:
var mu sync.Mutex
var found []stdocs.DriftFinding
h := stdocs.DriftWarn(mux, nil, stdocs.DriftNotify(func(f stdocs.DriftFinding) {
mu.Lock()
defer mu.Unlock()
found = append(found, f)
}))
Replaying traffic through h in a test and asserting on the collected findings turns drift into a CI gate, with the same allow-list shape as Mux.Lint:
accepted := map[string]bool{"undocumented-fields": true}
for _, f := range found {
if !accepted[f.Code] {
t.Errorf("%s", f.Message)
}
}
A gate is only as strong as its traffic — findings exist for exercised routes only — and build-failed arrives through fn too, so a broken document cannot pass as zero drift. The flat fields support finer allow-lists than the Code-only map above: f.Code+" "+f.Route waives one route's known divergence without waiving the whole class. Findings are deduplicated for the warner's lifetime — a long-running process reports each finding once and never re-emits it, so dashboards distinguish "fixed" from "already reported" by restart, not by silence. Multiple DriftNotify callbacks all receive each finding.
func DriftSampleBodies ¶ added in v0.4.2
func DriftSampleBodies() DriftOption
DriftSampleBodies additionally compares response bodies against the documented schema, for responses documented with a JSON object or array-of-objects schema: each missing documented-required key warns once per route, status, and field; the appearance of undocumented keys warns once per route, status, and path. The comparison covers top-level keys and one level of rows — elements of array-of-object properties ("orders[].fee_cents") and of array bodies ("[].id") — never values, deeper nesting, or rows of rows. A body of the wrong top-level kind entirely (a literal null, an array where an object is documented, or vice versa) warns as body-kind-mismatch instead of being compared key by key. Sampling copies up to 64 KB of each tracked response, which is why it is a separate opt; like DriftWarn itself, it is a development aid.
type Info ¶
Info is the OpenAPI "info" object. Fields: Title, Version, Description, TermsOfService, Contact (*Contact), License (*License). Title and Version are set via WithTitle and WithAPIVersion.
type License ¶
License is the OpenAPI "info.license" object. Fields: Name, URL. Set via WithLicense; empty fields are omitted.
type Mux ¶
Mux is an *http.ServeMux that also records route metadata for OpenAPI generation. Use stdocs.New to construct one. Mux embeds *http.ServeMux, so all its methods are available transparently.
func New ¶
New returns a *stdocs.Mux ready to register routes on.
Example ¶
The simplest possible setup: wrap the mux, register routes, mount the docs, serve. The generated document is available programmatically through JSON() too.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
mux := stdocs.New(stdocs.WithTitle("My API"))
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {})
mux.Mount() // docs UI at /docs/, spec at /docs/openapi.json
b, _ := mux.JSON()
var doc struct {
OpenAPI string `json:"openapi"`
Info struct {
Title string `json:"title"`
} `json:"info"`
}
_ = json.Unmarshal(b, &doc)
fmt.Println(doc.OpenAPI, doc.Info.Title)
}
Output: 3.0.4 My API
func (*Mux) Config ¶
Config returns the resolved configuration for the mux. It is useful for UI sub-packages that need to read or override Config fields.
func (*Mux) Docs ¶
Docs returns an http.Handler that serves the docs UI and the OpenAPI spec for this mux. The returned handler is a sub-mux that internally serves:
GET <prefix>/ -> the HTML docs UI GET <prefix>/openapi.json -> the spec as JSON GET <prefix>/openapi.yaml -> the spec as YAML
In most setups, call Mount instead, which registers this handler at the configured docs prefix (default "/docs", changeable via WithDocsPrefix). When mounting manually, the registration pattern must match the configured prefix:
mux.ServeMux.Handle("GET /docs/", mux.Docs())
(Plain mux.Handle works too — routes under the docs prefix are excluded from the generated spec.)
The optional bool argument enables per-call toggling: pass false to get a handler that responds 404 to everything, pass true to force the docs on, or omit it to follow the WithDisabled config. An explicit per-call value wins over WithDisabled in both directions. Only the first value is consulted; passing more than one bool panics.
mux.ServeMux.Handle("GET /docs/", mux.Docs(os.Getenv("ENV") != "prod"))
The decision is taken when Docs is called. For a per-request switch, wrap the returned handler in your own middleware.
Example ¶
Turning the docs off per environment without touching routes.
package main
import (
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
prod := true
mux := stdocs.New(stdocs.WithTitle("My API"))
mux.HandleFunc("GET /users", func(w http.ResponseWriter, r *http.Request) {})
// An explicit per-call value wins over WithDisabled in both
// directions; mux.Docs(false) serves 404 for every docs request.
mux.ServeMux.Handle("GET /docs/", mux.Docs(!prod))
fmt.Println("docs enabled:", !prod)
}
Output: docs enabled: false
func (*Mux) FromDocs ¶ added in v0.2.0
FromDocs reports whether r appears to originate from this mux's docs UI. It is FromDocs(r, prefix) with the mux's configured docs prefix; see the package-level FromDocs for the detection mechanics and the security caveats.
func (*Mux) Handle ¶
Handle registers h for the given pattern. The handler's underlying function name cannot be recovered from an http.Handler, so routes registered via Handle do not benefit from function-name-based summary inference (they get a blank summary unless Summary is provided).
func (*Mux) HandleFunc ¶
HandleFunc registers h for the given pattern. opts are RouteOpts that document the route.
The pattern must be a Go 1.22+ ServeMux pattern (e.g. "GET /users/{id}"). If parsing fails, HandleFunc panics. This matches the stdlib's behavior of panicking on invalid patterns.
func (*Mux) JSON ¶
JSON returns the OpenAPI spec as JSON bytes. The version depends on the mux's configured Version. First call is cached.
func (*Mux) Lint ¶ added in v0.4.0
Lint reports advisory findings about the generated document — things that are structurally valid but consume badly downstream (client generators, linters, API portals):
- operations documenting no error response (no 4xx/5xx and no "default" entry),
- operations without a summary,
- schema fields with no type (interfaces, json.RawMessage, custom marshalers), which consumers see as unconstrained,
- component names that needed a collision suffix (User_2) — fixable with a SchemaName method,
- custom-method operations, which 3.0/3.1 consumers only see under the x-stdocs-additionalOperations extension,
- host-scoped registrations shadowed in the document (OpenAPI paths cannot express ServeMux host scoping), and
- fields both required and defaulted (a required field can never take its default),
- "Generated from Go type" fallback descriptions and stdocs vendor extensions in the output when WithCleanOutput is off, and
- operationIds carrying a dangling collision suffix.
Lint never affects emission and the findings list may grow in future versions; treat it as advice, not a contract. Building the document is a side effect, so the same fail-fast panics as Mux.JSON apply. Like JSON, Lint rebuilds automatically when routes were registered since the last build; Mux.Refresh only matters for out-of-band changes (e.g. state captured by a WithOpenAPI hook).
A CI guard is one assertion:
accepted := map[string]bool{"no-summary": true} // allow-list by Code
for _, w := range mux.Lint() {
if !accepted[w.Code] {
t.Errorf("%s", w)
}
}
func (*Mux) Mount ¶
Mount registers the docs handler on the mux itself, at the configured DocsPrefix (default "/docs"). It registers exact patterns for the docs page and the two spec endpoints — plus a subtree fallback — so a user route like "GET /docs/{file}" can never shadow the spec endpoints (exact literals win over wildcards in ServeMux).
The optional bool argument mirrors Docs: pass false to register nothing, pass true to register the docs even on a mux disabled via WithDisabled, or omit it to follow the WithDisabled config. An explicit per-call value wins over WithDisabled in both directions. Only one bool is accepted; passing more panics.
mux.Mount(os.Getenv("ENV") != "prod")
Calling Mount more than once is a no-op after the first call that registered the docs. Routes registered after Mount still appear in the document on the next read; registering them before moves their fail-fast tag panics to startup, where Mount builds eagerly.
type OAuthFlow ¶
OAuthFlow describes one OAuth 2.0 flow. Fields: AuthorizationURL, TokenURL, RefreshURL, DeviceAuthorizationURL (3.2 device flow only), and Scopes (scope name -> description; always emitted, required by the spec).
type OAuthFlows ¶
type OAuthFlows = spec.OAuthFlows
OAuthFlows groups the OAuth 2.0 flows of a scheme: Implicit, Password, ClientCredentials, AuthorizationCode, and (OpenAPI 3.2 only) DeviceAuthorization — each an optional *OAuthFlow.
type Operation ¶
Operation is the per-route OpenAPI operation under construction. It is the value RouteOpts mutate; most users never touch it directly.
type Option ¶
type Option func(*Config)
Option is a function that mutates a config. Options are applied by New and DocsHandler at construction time.
func WithAPIKeyAuth ¶
WithAPIKeyAuth is a convenience for API key authentication. in is one of stdocs.SecurityAPIKeyInHeader, stdocs.SecurityAPIKeyInQuery, stdocs.SecurityAPIKeyInCookie.
func WithAPIVersion ¶
WithAPIVersion sets the API version string in the OpenAPI "info" block (e.g. "1.0.0"). This is independent of WithVersion which sets the OpenAPI specification version (3.0.4 vs 3.1.2 vs 3.2.0).
func WithAutoUnauthorized ¶ added in v0.3.0
WithAutoUnauthorized controls the automatic 401 response: by default, every operation that carries a security requirement — per-route via WithSecurity, or inherited from WithGlobalSecurity and not opted out with WithNoSecurity — documents a 401 ("Unauthorized") response, since an authenticated endpoint can always reject the credentials. A per-route WithResponse(401, ...) or a WithDefaultResponse(401, ...) body wins over the bare entry — note that WithDefaultResponse(401, ...) documents the 401 on every operation, including unsecured ones, per its own contract. Pass false to suppress the automatic 401 mux-wide.
func WithBasicAuth ¶
WithBasicAuth is a convenience for HTTP basic authentication.
func WithBearerAuth ¶
WithBearerAuth is a convenience for HTTP bearer authentication.
Example ¶
Registering a security scheme and requiring it on a route.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
mux := stdocs.New(
stdocs.WithTitle("My API"),
stdocs.WithBearerAuth("bearerAuth", "JWT"),
stdocs.WithGlobalSecurity("bearerAuth"),
)
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {},
stdocs.WithNoSecurity(), // public route opts out of the global scheme
)
b, _ := mux.JSON()
var doc struct {
Paths map[string]map[string]struct {
Security []map[string][]string `json:"security"`
} `json:"paths"`
}
_ = json.Unmarshal(b, &doc)
fmt.Println(len(doc.Paths["/health"]["get"].Security))
}
Output: 0
func WithCleanOutput ¶ added in v0.4.0
WithCleanOutput strips stdocs vendor noise from the generated document, for contracts consumed by client generators, linters, and API portals rather than humans browsing the docs page:
- the "Generated from Go type main.T." fallback schema descriptions (user-supplied doc: tags are kept), and
- the x-stdocs-type and x-stdocs-warning annotation extensions.
The x-stdocs-additionalOperations extension is NOT stripped: on 3.0/3.1 it is the only representation of custom-method operations, and removing it would silently drop documented routes. Hooks registered with WithOpenAPI run after cleaning and may add anything back.
func WithContact ¶
WithContact sets the contact info.
func WithDefaultRawResponse ¶ added in v0.5.0
WithDefaultRawResponse is WithDefaultResponse for raw bodies: it documents a string-typed response under the given content type on every operation that does not itself declare the status — an API whose shared error surface is plain text declares it once:
mux := stdocs.New(
stdocs.WithTitle("My API"),
stdocs.WithDefaultRawResponse(500, "text/plain; charset=utf-8"),
)
Precedence and accumulation follow WithDefaultResponse exactly: a per-route declaration or fallback wins, repeating a status across either default form panics, and status 0 means the OpenAPI "default" response. The filled entry is what WithRawResponse declares, except a content type set by WithResponseContentType on the route survives. The content type is required.
func WithDefaultResponse ¶ added in v0.3.0
WithDefaultResponse documents a response on every operation that does not itself declare the same status — typically the API's shared error envelope, declared once instead of on every route:
mux := stdocs.New(
stdocs.WithTitle("My API"),
stdocs.WithDefaultResponse(500, APIError{}),
)
A per-route WithResponse (even with a nil body) or WithRawResponse for the same status wins; an entry materialized only by decorators (WithResponseDescription and friends) still takes the default body. Pass status 0 for the OpenAPI "default" response and nil for a body-less entry. The entry applies to every operation — to document a 401 only on secured routes, rely on the automatic 401 instead (see WithAutoUnauthorized). Multiple calls accumulate; repeating a status panics, as does a status outside 100-599 (other than 0).
Example ¶
The shared error envelope is declared once at the mux level; every operation documents it unless it declares the status itself.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
type APIError struct {
Message string `json:"message"`
}
mux := stdocs.New(
stdocs.WithTitle("My API"),
stdocs.WithDefaultResponse(500, APIError{}),
)
mux.HandleFunc("GET /tasks", func(w http.ResponseWriter, r *http.Request) {})
raw, _ := mux.JSON()
var doc struct {
Paths map[string]map[string]struct {
Responses map[string]struct {
Description string `json:"description"`
} `json:"responses"`
} `json:"paths"`
}
json.Unmarshal(raw, &doc)
resps := doc.Paths["/tasks"]["get"].Responses
for _, code := range []string{"200", "500"} {
fmt.Printf("%s: %s\n", code, resps[code].Description)
}
}
Output: 200: OK 500: Internal Server Error
func WithDefaultSummary ¶
WithDefaultSummary sets a default summary template used for routes that do not provide one and whose function name does not yield a useful inference. Use {resource} as a placeholder for the first path segment (the inferred tag).
func WithDescription ¶
WithDescription sets the API description.
func WithDisabled ¶
WithDisabled turns off the docs UI and the spec endpoints. Useful for environment-based toggling (e.g. don't expose docs in production, or behind a feature flag):
mux := stdocs.New(
stdocs.WithDisabled(os.Getenv("ENV") == "prod"),
)
When the mux is disabled, Mux.Docs returns a 404 handler and Mux.Mount registers nothing. JSON and YAML still produce the spec bytes — disabling the docs UI does not stop spec generation.
For per-call toggling (e.g. a config that may change at runtime), pass the bool directly to Mux.Docs(enabled) instead.
func WithDocsPrefix ¶
WithDocsPrefix overrides the URL prefix for the docs UI. The default is "/docs". The value is normalized: a leading slash is added if missing, and a trailing slash is removed so the prefix is comparable to strings.TrimPrefix results. An empty prefix is replaced with the default "/docs"; to turn the docs UI off, use WithDisabled or pass false to Mux.Docs.
The root prefix "/" is rejected with a panic: it would claim the whole URL space and produce an invalid ServeMux pattern in Mount.
func WithExternalDocs ¶ added in v0.4.0
WithExternalDocs sets the document-level link to external documentation. The URL is required and must parse as a URI; tags take their own link via WithTagExternalDocs and operations via the ExternalDocs route opt.
func WithGlobalSecurity ¶
WithGlobalSecurity sets the default security requirement applied to every operation that does not specify its own. Operations can opt out with stdocs.WithNoSecurity or override with stdocs.WithSecurity.
stdocs.WithGlobalSecurity("bearerAuth")
stdocs.WithGlobalSecurity("oauth2Auth", "read:users")
func WithInternal ¶ added in v0.1.1
WithInternal sets whether routes marked with the Internal route opt appear in the generated OpenAPI document. The default is false: internal routes are hidden, so forgetting this option can never leak a sensitive endpoint into a published spec. When shown, internal operations carry an "x-internal": true extension.
Typical environment wiring, together with WithDisabled:
env := os.Getenv("ENV")
mux := stdocs.New(
stdocs.WithDisabled(env == "prod"), // prod: no docs at all
stdocs.WithInternal(env == "dev"), // dev: everything; staging: internal hidden
)
Visibility only shapes the published documentation; hidden and internal routes still serve traffic in every environment.
func WithLicense ¶
WithLicense sets the license info. For an SPDX license expression instead of a URL, use WithSPDXLicense; the spec treats url and identifier as mutually exclusive, and whichever of the two options is applied last wins.
func WithOAuth2Auth ¶
func WithOAuth2Auth(name string, flows OAuthFlows) Option
WithOAuth2Auth is a convenience for OAuth 2.0 with one or more flows.
func WithOpenAPI ¶
WithOpenAPI registers a callback that runs after the spec is built and before it is cached. The callback receives the spec as a map[string]any and may mutate it in place. This is the escape hatch for features stdocs does not expose directly: security schemes, webhooks, custom x-extensions, vendor extensions, etc.
The callback is invoked once per build (i.e. once before the cache is populated; subsequent reads use the cache). Call Refresh to force the callback to run again.
Example:
stdocs.WithOpenAPI(func(doc map[string]any) {
doc["security"] = []map[string]any{{
"bearerAuth": []string{},
}}
doc["components"].(map[string]any)["securitySchemes"] = map[string]any{
"bearerAuth": map[string]any{
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
},
}
})
func WithOperationIDFunc ¶ added in v0.4.0
WithOperationIDFunc overrides the automatic operationId derivation with f, called with the route's HTTP method (upper-case, "" for method-less patterns) and path:
stdocs.WithOperationIDFunc(func(method, path string) string {
return strings.ToLower(method) + strings.ReplaceAll(path, "/", ".")
})
An explicit OperationID route opt still wins, an empty result falls back to the default derivation, and document-wide uniqueness suffixing applies to the result as usual.
func WithPathPrefix ¶ added in v0.3.0
WithPathPrefix prepends prefix to every path in the generated document. Use it when the mux is mounted under a prefix the application never sees — http.StripPrefix("/api", mux) or a reverse proxy that strips "/api" — so the documented paths match the URLs clients actually call:
mux := stdocs.New(
stdocs.WithTitle("My API"),
stdocs.WithPathPrefix("/api"),
)
// GET /users is documented as /api/users.
Documentation only: routing, the docs prefix, and FromDocs are unaffected. The value is normalized like WithDocsPrefix (leading slash added, trailing slash removed); an empty value means no prefix and the root prefix "/" is rejected with a panic.
func WithSPDXLicense ¶ added in v0.4.0
WithSPDXLicense sets the license as an SPDX expression:
stdocs.WithSPDXLicense("Apache 2.0", "Apache-2.0")
The identifier field exists from OpenAPI 3.1 on; a 3.0 document degrades to the name alone.
func WithSecurityScheme ¶
func WithSecurityScheme(name string, scheme SecurityScheme) Option
WithSecurityScheme registers a security scheme under the given name. The name is the key in the components.securitySchemes map and is the value you pass to WithSecurity on a route. If name is empty, a sensible default is chosen (e.g. "bearerAuth" for HTTP bearer).
Use the chosen name with WithSecurity to require this scheme on a route.
func WithSelfURL ¶
WithSelfURL sets the OpenAPI 3.2 "$self" field. This is the canonical URI of the document. It is emitted only in 3.2 specs; setting it on a 3.0 or 3.1 mux has no effect because those versions do not have the field.
The value must be a valid RFC 3986 URI reference without a fragment (both constraints come from the OpenAPI 3.2 specification and its published JSON Schema). WithSelfURL panics on invalid input, consistent with WithVersion's fail-fast behavior at New()/DocsHandler() time.
func WithSpec ¶
WithSpec sets a static OpenAPI document (JSON bytes) for DocsHandler to serve at <prefix>/openapi.json (and, converted, at <prefix>/openapi.yaml). This is the Tier-1 path for exposing a hand-written spec through the docs UI:
specJSON, _ := os.ReadFile("openapi.json")
mux.Handle("GET /docs/", stdocs.DocsHandler(
stdocs.WithTitle("My API"),
stdocs.WithSpec(specJSON),
))
WithSpec has no effect on *stdocs.Mux (Tier 2), which always generates its spec from the registered routes.
func WithTag ¶
WithTag declares a top-level tag and its description. Tags attached to operations that match a declared tag are also valid; undeclared tags are still emitted.
func WithTagExternalDocs ¶ added in v0.4.0
WithTagExternalDocs attaches an external-documentation link to a declared tag, creating the declaration if WithTag has not (yet) declared it — the order of the two options does not matter.
func WithTagFunc ¶ added in v0.4.1
WithTagFunc overrides the automatic tag inference (first non-version path segment) with f, called with the route's HTTP method (upper-case, "" for method-less patterns) and path. An explicit Tags route opt still wins; an empty result falls back to the default inference.
stdocs.WithTagFunc(func(method, path string) string {
return strings.Split(path, "/")[2] // /api/{group}/...
})
func WithVersion ¶
func WithVersion(v SpecVersion) Option
WithVersion sets the OpenAPI spec version. Accepts OpenAPI30 (3.0.4), OpenAPI31 (3.1.2), or OpenAPI32 (3.2.0). A string literal like "3.0.4" is also accepted because SpecVersion is a defined string type with the same underlying values.
WithVersion panics on an unknown version string. Options run at New()/DocsHandler() time, the same fail-fast window where bad patterns already panic; silently coercing to a default would mask user errors.
Example ¶
Selecting the OpenAPI version. stdocs ships the latest patch of each supported minor: 3.0.4 (default), 3.1.2, and 3.2.0.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
mux := stdocs.New(
stdocs.WithTitle("My API"),
stdocs.WithVersion(stdocs.OpenAPI32),
stdocs.WithSelfURL("https://api.example.com/openapi.json"),
)
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {})
b, _ := mux.JSON()
var doc struct {
OpenAPI string `json:"openapi"`
Self string `json:"$self"`
}
_ = json.Unmarshal(b, &doc)
fmt.Println(doc.OpenAPI, doc.Self)
}
Output: 3.2.0 https://api.example.com/openapi.json
func WithWebhooks ¶
WithWebhooks registers one or more webhooks under their given names. Webhooks require OpenAPI 3.1 or 3.2; on 3.0 they are silently ignored (the field does not exist in that version).
Webhook payloads are described the same way route bodies are: set RequestBody.BodyValue (or Response.BodyValue) to a zero value of the Go type and its JSON Schema is reflected when the document is built.
Example:
type UserPayload struct {
ID string `json:"id"`
}
stdocs.WithWebhooks(map[string]stdocs.Webhook{
"newUser": {
Method: "POST",
Summary: "New user created",
RequestBody: &stdocs.RequestBody{Required: true, BodyValue: UserPayload{}},
Responses: map[string]*stdocs.Response{
"200": {Description: "OK"},
},
},
})
type Param ¶
Param describes one OpenAPI parameter. Fields: Name, In ("path", "query", "header", "cookie"), Required, Description, Schema. Usually created via WithParam / QueryParam / HeaderParam / CookieParam.
type ParamOpt ¶ added in v0.3.0
type ParamOpt func(p *Param)
ParamOpt refines one parameter declared with WithParam, QueryParam, HeaderParam, or CookieParam:
stdocs.QueryParam("limit", "integer", "Page size",
stdocs.ParamDefault(20),
stdocs.ParamMinimum(1),
stdocs.ParamMaximum(100),
)
Values are validated against the parameter's declared type at registration time; a mismatch (ParamDefault("x") on an integer parameter) panics.
The modifier set mirrors the constraint-tag vocabulary; the same declarations are also available as a WithParams struct.
func ParamDefault ¶ added in v0.3.0
ParamDefault documents the parameter's default value.
func ParamExample ¶ added in v0.3.0
ParamExample documents an example value for the parameter.
func ParamExclusiveMaximum ¶ added in v0.4.1
ParamExclusiveMaximum documents the parameter's exclusive upper bound. Numeric parameters only; mutually exclusive with ParamMaximum.
func ParamExclusiveMinimum ¶ added in v0.4.1
ParamExclusiveMinimum documents the parameter's exclusive lower bound. Numeric parameters only; mutually exclusive with ParamMinimum.
func ParamFormat ¶ added in v0.4.1
ParamFormat overrides the parameter's format hint (e.g. "uuid", "date-time", "email").
func ParamItems ¶ added in v0.4.1
ParamItems sets the element type of an "array" parameter, which otherwise defaults to string items. typ is one of "string", "integer", "number", or "boolean".
func ParamMaxItems ¶ added in v0.4.1
ParamMaxItems documents the array parameter's maximum length.
func ParamMaxLength ¶ added in v0.3.0
ParamMaxLength documents the parameter's maximum string length. String parameters only.
func ParamMaximum ¶ added in v0.3.0
ParamMaximum documents the parameter's inclusive upper bound. Numeric parameters only.
func ParamMinItems ¶ added in v0.4.1
ParamMinItems documents the array parameter's minimum length.
func ParamMinLength ¶ added in v0.3.0
ParamMinLength documents the parameter's minimum string length. String parameters only.
func ParamMinimum ¶ added in v0.3.0
ParamMinimum documents the parameter's inclusive lower bound. Numeric parameters only.
func ParamPattern ¶ added in v0.3.0
ParamPattern documents an ECMA-262 regular expression the parameter must match. String parameters only.
func ParamRequired ¶ added in v0.3.0
func ParamRequired() ParamOpt
ParamRequired marks the parameter required. Path parameters are always required; query, header, and cookie parameters default to optional.
func ParamUniqueItems ¶ added in v0.4.1
func ParamUniqueItems() ParamOpt
ParamUniqueItems requires the array parameter's elements to be unique.
type PathItem ¶
PathItem groups the operations of one path. Produced internally by the registry; exposed for advanced use only.
type RequestBody ¶
type RequestBody = spec.RequestBody
RequestBody describes an operation's request body. Fields: Description, Required, ContentType (defaults to "application/json"), Example, and BodyValue — a zero value of the Go body type, reflected at document-build time. Usually created via WithBody; constructed directly only for webhooks.
type Response ¶
Response describes one entry of an operation's "responses" map. Fields: Status, Description, Headers, Example, and BodyValue — a zero value of the Go response type, reflected into a JSON Schema at document-build time. Usually created via WithResponse and decorated with WithResponseDescription / WithResponseHeader / WithResponseExample; constructed directly only for webhooks.
type RouteOpt ¶
type RouteOpt func(*route)
RouteOpt is a function that mutates a route's metadata. RouteOpt is passed as variadic to *stdocs.Mux.HandleFunc and *stdocs.Mux.Handle.
func CookieParam ¶
CookieParam is shorthand for WithParam(name, "cookie", typ, desc).
func Description ¶
Description sets the route's longer Markdown description.
func ExternalDocs ¶ added in v0.4.0
ExternalDocs links the operation to external documentation. The URL is required and must parse as a URI.
func HeaderParam ¶
HeaderParam is shorthand for WithParam(name, "header", typ, desc).
func Hidden ¶ added in v0.1.1
func Hidden() RouteOpt
Hidden excludes the route from the generated OpenAPI document unconditionally, in every environment. Use it for endpoints that are not part of any contract (debug hooks, health probes).
Hiding a route only shapes the published documentation — the handler still serves traffic. It is NOT access control.
func Internal ¶ added in v0.1.1
func Internal() RouteOpt
Internal marks the route as internal: it is excluded from the generated OpenAPI document unless the mux was configured with WithInternal(true). When shown, the operation carries an "x-internal": true extension, which spec-filtering tools (e.g. Redocly) understand.
Like Hidden, this only shapes the published documentation — the handler still serves traffic in every environment. It is NOT access control.
func OperationID ¶
OperationID sets the operationId. If not set, stdocs auto-derives one from the method and path.
func Optional ¶
func Optional() RouteOpt
Optional marks the route's request body as not required — like the other body decorators, the order relative to WithBody does not matter, and without any body declaration it is a no-op.
func Opts ¶ added in v0.3.0
Opts combines several route opts into one, enabling reusable bundles shared across registrations:
paginated := stdocs.Opts(
stdocs.QueryParam("cursor", "string", "Opaque cursor"),
stdocs.QueryParam("limit", "integer", "Page size"),
)
mux.HandleFunc("GET /tasks", listTasks, paginated, stdocs.WithResponse(200, TaskPage{}))
mux.HandleFunc("GET /users", listUsers, paginated, stdocs.WithResponse(200, UserPage{}))
Opts applies its opts in order; nil opts are skipped. A bundle is stateless and safe to reuse across any number of routes.
Example ¶
Route-opt bundles: declare a preset once, reuse it across routes.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
paginated := stdocs.Opts(
stdocs.QueryParam("cursor", "string", "Opaque cursor"),
stdocs.QueryParam("limit", "integer", "Page size", stdocs.ParamDefault(20)),
)
mux := stdocs.New(stdocs.WithTitle("My API"))
mux.HandleFunc("GET /tasks", func(w http.ResponseWriter, r *http.Request) {}, paginated)
mux.HandleFunc("GET /users", func(w http.ResponseWriter, r *http.Request) {}, paginated)
raw, _ := mux.JSON()
var doc struct {
Paths map[string]map[string]struct {
Parameters []struct {
Name string `json:"name"`
} `json:"parameters"`
} `json:"paths"`
}
json.Unmarshal(raw, &doc)
for _, path := range []string{"/tasks", "/users"} {
names := make([]string, 0, 2)
for _, p := range doc.Paths[path]["get"].Parameters {
names = append(names, p.Name)
}
fmt.Println(path, names)
}
}
Output: /tasks [cursor limit] /users [cursor limit]
func QueryParam ¶
QueryParam is shorthand for WithParam(name, "query", typ, desc).
func Summary ¶
Summary sets the route's summary (the short single-line description shown next to the operation in UIs).
func WithBody ¶
WithBody sets the route's request body. body is a zero value of the type to reflect; its type is used to build the JSON Schema when the spec document is assembled. Struct fields may carry doc: (or description:) and example: tags, plus the constraint tag vocabulary — minimum, maximum, exclusiveMinimum, exclusiveMaximum, minLength, maxLength, pattern, minItems, maxItems, uniqueItems, enum, default, and format. Values are parsed according to the field type (enum:"1,2,3" on an int emits numbers) and validated against it; a misapplied or unparseable constraint panics at document-build time. The default content type is application/json (override with WithBodyContentType).
Mark the body as not required with Optional().
func WithBodyContentType ¶
WithBodyContentType sets the content type for the request body, creating the request-body entry if it does not exist yet. The default is "application/json". The order relative to WithBody does not matter.
stdocs.WithBody(MyRequest{}),
stdocs.WithBodyContentType("application/xml"),
func WithExample ¶
WithExample adds an example value to the request body if one has been declared (via WithBody), otherwise to the most recently declared response (via WithResponse). If neither has been declared, WithExample is a no-op. To target a specific response regardless of declaration order, use WithResponseExample.
The value is encoded as JSON and stored under the OpenAPI "example" field of the media type object (content.<type>.example).
Example:
mux.HandleFunc("POST /users", createUser,
stdocs.WithBody(CreateUserRequest{}),
stdocs.WithResponse(201, User{}),
stdocs.WithExample(CreateUserRequest{Title: "Buy milk"}),
)
Subsequent WithExample calls overwrite the previous example.
func WithFallbackRawResponse ¶ added in v0.5.0
WithFallbackRawResponse is WithFallbackResponse for raw bodies: it documents a string-typed response under the given content type only when the route does not declare the status itself, so an error-shape era that speaks plain text gets a reusable bundle like a JSON era does:
gen1Errors := stdocs.Opts(
stdocs.WithFallbackRawResponse(400, "text/plain; charset=utf-8"),
stdocs.WithFallbackRawResponse(404, "text/plain; charset=utf-8"),
)
Precedence is identical to WithFallbackResponse: explicit declarations win, the first fallback per status wins, route fallbacks beat mux-level defaults. The filled entry is exactly what WithRawResponse declares — except a content type already set by WithResponseContentType survives, since the decorator names the route's actual media type more specifically than a bundle can. Pass status 0 for the OpenAPI "default" response. The content type is required.
func WithFallbackResponse ¶ added in v0.4.2
WithFallbackResponse documents a response on this route only when the route does not declare the same status itself — the route-scoped counterpart of the mux-level WithDefaultResponse, built for reusable bundles: a codebase with several error-shape eras declares one fallback per era and applies it through Opts:
legacyErrors := stdocs.Opts(stdocs.WithFallbackResponse(500, LegacyError{}))
modernErrors := stdocs.Opts(stdocs.WithFallbackResponse(500, Envelope{}))
An explicit WithResponse (even with a nil body) or WithRawResponse for the status wins; entries merely materialized by decorators (WithResponseDescription and friends) still take the fallback body. The first fallback per status wins over later ones, and route fallbacks win over mux-level defaults (the narrower scope is more specific). A 401 fallback also supplies the body of the automatic 401 on secured routes — and applies unconditionally on unsecured ones, so keep 401 fallbacks in bundles that pair them with WithSecurity. Pass status 0 for the OpenAPI "default" response. Statuses outside 100-599 (other than 0) panic.
func WithMultipartBody ¶ added in v0.4.0
WithMultipartBody documents a multipart/form-data request body — the file-upload shape — from its parts:
mux.HandleFunc("POST /upload", uploadFile,
stdocs.WithMultipartBody(
stdocs.FilePart("attachment", "The file to upload"),
stdocs.FieldPart("caption", "string", "Optional caption"),
),
)
Documentation only — handlers keep parsing with r.MultipartForm. At least one part is required and duplicate part names panic. WithMultipartBody replaces any WithBody declaration on the route.
func WithNoSecurity ¶
func WithNoSecurity() RouteOpt
WithNoSecurity opts the operation out of any security requirement. This emits an empty `security: []` array at the operation level, which the OpenAPI spec defines as overriding a globally-applied scheme. Without this (i.e. leaving Security empty), the operation inherits the global security requirement.
func WithParam ¶
WithParam adds a parameter to the route. in is one of "path", "query", "header", "cookie". typ is the JSON Schema type name ("string", "integer", "number", "boolean", "array"). For arrays, the items are also string-typed.
Multiple WithParam calls accumulate.
func WithParams ¶ added in v0.3.0
WithParams declares query, header, and cookie parameters by reflecting a struct, sharing the body fields' tag vocabulary:
type ListParams struct {
Cursor string `query:"cursor" doc:"Opaque pagination cursor"`
Limit int `query:"limit" default:"20" minimum:"1" maximum:"100"`
Trace string `header:"X-Trace-Id" doc:"Propagated trace id"`
}
mux.HandleFunc("GET /tasks", listTasks, stdocs.WithParams(ListParams{}))
Every exported field carries exactly one location tag — query, header, or cookie — whose value is the parameter name ("-" skips the field). The Go type provides the schema (scalars and slices of scalars), the constraint tags apply as on body fields, and required:"true" marks a parameter required. Embedded structs are not flattened — tag them query:"-" or declare their fields directly. Violations panic at registration time. Multiple WithParams and WithParam declarations accumulate, but a duplicate (name, location) pair across them panics at document build.
Example ¶
Constraint tags and typed parameters: the struct documents its own validation rules, and WithParams reflects query parameters from a struct with the same vocabulary.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
type ListParams struct {
Cursor string `query:"cursor" doc:"Opaque pagination cursor"`
Limit int `query:"limit" default:"20" minimum:"1" maximum:"100"`
}
mux := stdocs.New(stdocs.WithTitle("My API"))
mux.HandleFunc("GET /tasks", func(w http.ResponseWriter, r *http.Request) {},
stdocs.WithParams(ListParams{}),
)
raw, _ := mux.JSON()
var doc struct {
Paths map[string]map[string]struct {
Parameters []struct {
Name string `json:"name"`
Schema struct {
Type string `json:"type"`
Default json.RawMessage `json:"default"`
Maximum json.RawMessage `json:"maximum"`
} `json:"schema"`
} `json:"parameters"`
} `json:"paths"`
}
json.Unmarshal(raw, &doc)
for _, p := range doc.Paths["/tasks"]["get"].Parameters {
fmt.Printf("%s (%s) default=%s max=%s\n",
p.Name, p.Schema.Type, p.Schema.Default, p.Schema.Maximum)
}
}
Output: cursor (string) default= max= limit (integer) default=20 max=100
func WithRawResponse ¶ added in v0.4.2
WithRawResponse documents a raw (non-JSON) response — CSV exports, plain-text bodies, file downloads — as a string-typed body under the given content type, in one opt:
mux.HandleFunc("GET /export", exportCSV,
stdocs.WithRawResponse(200, "text/csv"),
stdocs.WithResponseHeader(200, "Content-Disposition", "string", "attachment"),
)
It is order-independent like the other response decorators, supports status 0 for the "default" response, and composes with WithResponseDescription/WithResponseHeader. The content type is required.
func WithResponse ¶
WithResponse adds a response entry. body is a zero value whose type is reflected into a JSON Schema when the spec document is assembled (struct fields may carry doc:/description:, example:, and the constraint tags listed on WithBody); pass nil if there is no body (e.g. 204 No Content). Pass status 0 to declare the OpenAPI "default" response — the catch-all entry consumers use for undeclared status codes, conventionally the shared error shape. Multiple WithResponse calls accumulate. Calling WithResponse twice for the same status replaces the body but keeps any description, headers, or example attached via the other response opts.
Example ¶
Documenting request and response bodies through reflection.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/FumingPower3925/stdocs"
)
func main() {
type User struct {
ID string `json:"id" doc:"Unique ID"`
Name string `json:"name"`
}
type CreateUserRequest struct {
Name string `json:"name"`
}
mux := stdocs.New(stdocs.WithTitle("My API"))
mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {},
stdocs.Summary("Create a user"),
stdocs.WithBody(CreateUserRequest{}),
stdocs.WithResponse(201, User{}),
stdocs.WithResponseDescription(201, "The newly created user"),
)
b, _ := mux.JSON()
var doc struct {
Components struct {
Schemas map[string]json.RawMessage `json:"schemas"`
} `json:"components"`
}
_ = json.Unmarshal(b, &doc)
for name := range doc.Components.Schemas {
if name == "User" {
fmt.Println("components.schemas has User")
}
}
}
Output: components.schemas has User
func WithResponseContentType ¶ added in v0.4.0
WithResponseContentType overrides the content type of one response (the default is application/json), creating the response entry if it does not exist yet — the order relative to WithResponse does not matter:
stdocs.WithResponse(200, nil), stdocs.WithResponseContentType(200, "text/csv"),
It is the response-side counterpart of WithBodyContentType. Pass status 0 for the "default" response.
func WithResponseDescription ¶
WithResponseDescription sets a custom description for a response status, creating the response entry if it does not exist yet. The order relative to WithResponse does not matter.
stdocs.WithResponse(200, User{}),
stdocs.WithResponseDescription(200, "The user, or 404 if not found"),
func WithResponseExample ¶
WithResponseExample attaches an example to a specific response status, creating the response entry if it does not exist yet. The order relative to WithResponse does not matter.
stdocs.WithResponse(200, User{}),
stdocs.WithResponseExample(200, User{ID: "u-1", Name: "Alice"}),
func WithResponseHeader ¶
WithResponseHeader adds a header entry to a response status, creating the response entry if it does not exist yet. Useful for documenting rate-limit, pagination, or other custom headers. The order relative to WithResponse does not matter.
stdocs.WithResponse(200, User{}),
stdocs.WithResponseHeader(200, "X-RateLimit-Remaining", "integer", "Remaining quota"),
func WithSecurity ¶
WithSecurity requires the named security scheme on this operation. scopes are only meaningful for OAuth2 schemes; pass no scopes for non-OAuth schemes.
Use WithNoSecurity to opt out of a globally-applied scheme for one route.
Example:
mux.HandleFunc("GET /me", getUser,
stdocs.WithSecurity("bearerAuth"),
)
mux.HandleFunc("POST /posts", createPost,
stdocs.WithSecurity("oauth2Auth", "write:posts", "read:posts"),
)
type SecurityRequirement ¶
type SecurityRequirement = spec.SecurityRequirement
SecurityRequirement is one entry of a "security" array: scheme name -> required scopes (empty for non-OAuth schemes).
type SecurityScheme ¶
type SecurityScheme = spec.SecurityScheme
SecurityScheme describes one components.securitySchemes entry. Set Type plus the type-specific fields: Scheme/BearerFormat for http, In/Name for apiKey, Flows for oauth2, and OpenIDConnectURL for openIdConnect. Description is optional.
type SecuritySchemeType ¶
type SecuritySchemeType = spec.SecuritySchemeType
SecuritySchemeType is the "type" of a security scheme: SecurityHTTP, SecurityAPIKey, SecurityOAuth2, or SecurityOpenIDConnect.
type Server ¶
Server is one OpenAPI "servers" entry. Fields: URL, Description. Added via WithServer (the default relative "/" entry stays first).
type SpecInput ¶
SpecInput is the input consumed by the internal emitters. Exposed for advanced use only.
type SpecVersion ¶
type SpecVersion = version.SpecVersion
SpecVersion is the OpenAPI spec version ("3.0.4", "3.1.2", or "3.2.0"). Use the OpenAPI30 / OpenAPI31 / OpenAPI32 constants with WithVersion.
type Warning ¶ added in v0.4.0
type Warning struct {
// Code identifies the finding kind stably across releases —
// allow-lists and CI gates should match on it, never on Message
// prose, which is free to improve. The codes:
//
// build-failed the document could not be built
// no-error-response operation declares no 4xx/5xx/default
// no-summary operation has no summary
// pattern-approximation the registration cannot be represented
// exactly (method-less or host-scoped)
// shadowed-route registration absent from the document
// name-collision component renamed with a numeric suffix
// untyped-field schema field with no type
// exclusive-bounds 3.1/3.2 exclusive bounds vs generators
// nullable-facet-generators
// nullable + default/uniqueItems/byte vs
// generators on 3.1/3.2
// required-with-default field both required and defaulted
// auto-descriptions "Generated from Go type" text present
// dangling-id-suffix suffixed operationId without a base
// vendor-extensions x-stdocs-* present without CleanOutput
//
// Runtime drift findings follow the same discipline in
// [DriftFinding].Code.
Code string
// Where locates the finding: an operation ("GET /tasks"), a
// component ("component Task"), or "document".
Where string
// Message describes the finding and, where obvious, the remedy.
Message string
}
Warning is one advisory finding from Mux.Lint.
type Webhook ¶
Webhook describes one OpenAPI 3.1/3.2 webhook. Fields: Method, Summary, Description, OperationID, RequestBody, Responses, and Security. Without an explicit Security requirement the emitter writes security: [] on the webhook operation, so webhooks never inherit the document-level requirement (generated clients would otherwise reference schemes their webhook plumbing lacks). Describe payloads by setting BodyValue on the request body or responses; schemas are reflected at document-build time.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
demo
command
Package main is a demo of stdocs using a fictional Task Tracker API.
|
Package main is a demo of stdocs using a fictional Task Tracker API. |
|
internal
|
|
|
pattern
Package pattern parses Go 1.22+ net/http.ServeMux pattern strings.
|
Package pattern parses Go 1.22+ net/http.ServeMux pattern strings. |
|
schema
Package schema reflects Go values into a version-agnostic JSON Schema representation consumed by the OpenAPI emitters in internal/spec.
|
Package schema reflects Go values into a version-agnostic JSON Schema representation consumed by the OpenAPI emitters in internal/spec. |
|
spec
Package spec builds OpenAPI 3.0, 3.1, and 3.2 documents from internal *spec.SpecInput values.
|
Package spec builds OpenAPI 3.0, 3.1, and 3.2 documents from internal *spec.SpecInput values. |
|
spec/yaml
Package yaml converts JSON documents (the form produced by the OpenAPI emitters) to YAML.
|
Package yaml converts JSON documents (the form produced by the OpenAPI emitters) to YAML. |
|
version
Package version defines the OpenAPI spec-version identifiers used by the emitters.
|
Package version defines the OpenAPI spec-version identifiers used by the emitters. |
|
ui
|
|
|
redoc
Package redoc provides a Redoc UI for stdocs.
|
Package redoc provides a Redoc UI for stdocs. |
|
redocemb
Package redocemb provides an embedded (air-gapped) Redoc UI for stdocs.
|
Package redocemb provides an embedded (air-gapped) Redoc UI for stdocs. |
|
scalar
Package scalar provides a Scalar UI for stdocs.
|
Package scalar provides a Scalar UI for stdocs. |
|
scalaremb
Package scalaremb provides an embedded (air-gapped) Scalar UI for stdocs.
|
Package scalaremb provides an embedded (air-gapped) Scalar UI for stdocs. |
|
stoplight
Package stoplight provides a Stoplight Elements UI for stdocs.
|
Package stoplight provides a Stoplight Elements UI for stdocs. |
|
stoplightemb
Package stoplightemb provides an embedded (air-gapped) Stoplight Elements UI for stdocs.
|
Package stoplightemb provides an embedded (air-gapped) Stoplight Elements UI for stdocs. |
|
swaggerui
Package swaggerui provides a Swagger UI for stdocs.
|
Package swaggerui provides a Swagger UI for stdocs. |
|
swaggeruiemb
Package swaggeruiemb provides an embedded (air-gapped) Swagger UI for stdocs.
|
Package swaggeruiemb provides an embedded (air-gapped) Swagger UI for stdocs. |
