Documentation
¶
Overview ¶
Package openapi generates an OpenAPI 3.1 document from apikit request/response types and a router.Router, with zero third-party dependencies.
Request and response schemas are derived by reflecting on the same `json` and `validate` struct tags that request.Bind[T] and request.ValidateStruct already use, so a type only needs to be described once:
type CreateUserReq struct {
Name string `json:"name" validate:"required,min=2,max=100"`
Email string `json:"email" validate:"required,email"`
}
doc := openapi.New(openapi.Info{Title: "My API", Version: "1.0.0"})
doc.Add("POST", "/users", openapi.Operation{
Summary: "Create a user",
Request: CreateUserReq{},
Responses: map[int]openapi.Response{
201: {Description: "Created", Body: User{}},
},
})
// Fill in any route the router knows about but that Add hasn't described yet.
doc.Sync(r)
r.GetFunc("/openapi.json", doc.Handler())
r.GetFunc("/docs", doc.SwaggerUIHandler("/openapi.json"))
Cross-field and programmatic validation (request.NewValidation) is out of scope for schema generation, matching the tag engine's own documented limits.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Document ¶
type Document struct {
// contains filtered or unexported fields
}
Document accumulates operations and renders them as an OpenAPI 3.1 JSON document. The zero value is not usable; create one with New.
A Document is safe for concurrent use: Add and Sync are typically called during startup while routes are registered, and JSON/Handler are safe to call concurrently from request goroutines afterward — the rendered JSON is cached after the first call and invalidated by Add/Sync, so repeated requests to a served spec don't re-run reflection on every hit.
func (*Document) Add ¶
Add describes one method+path operation. Calling Add again for the same method and path overwrites the previous description.
func (*Document) Handler ¶
func (d *Document) Handler() http.HandlerFunc
Handler serves the document as "application/json".
func (*Document) SwaggerUIHandler ¶
func (d *Document) SwaggerUIHandler(specURL string) http.HandlerFunc
SwaggerUIHandler serves a Swagger UI page that loads the spec from specURL — typically the path where Handler is mounted, e.g. "/openapi.json".
func (*Document) Sync ¶
Sync walks r's registered routes and adds a bare (undescribed) operation for any method+path that Add hasn't already covered, so the document never drifts out of sync with the router — every real route shows up, even before someone fills in its request/response schema with Add.
Routes registered without a specific method (Handle/HandleFunc, matching every method) have no single OpenAPI operation to represent them and are skipped.
type Info ¶
type Info struct {
Title string `json:"title"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
}
Info describes the API metadata block of the document.
type Operation ¶
type Operation struct {
Summary string
Description string
Tags []string
// Request, when non-nil, becomes the JSON request body schema.
Request any
// Params declares path/query/header parameters explicitly. Path
// parameters present in the route pattern (e.g. "{id}") but missing here
// are added automatically as required string parameters.
Params []Param
// Responses maps HTTP status code to a response description and body type.
Responses map[int]Response
}
Operation describes a single method+path entry. Request and the values in Responses are sample instances (usually zero values, e.g. CreateUserReq{}) used only for their type — schemas are derived via reflection, never from the values themselves.
type Param ¶
type Param struct {
Name string
In string // "path", "query", or "header"
Required bool
Description string
// Schema, when nil, defaults to a plain string schema.
Schema *Schema
}
Param describes a single path, query, or header parameter.
type Response ¶
type Response struct {
Description string
// Body, when non-nil, becomes the JSON response body schema.
Body any
}
Response describes one entry in an operation's Responses map.
type Schema ¶
type Schema struct {
Ref string `json:"$ref,omitempty"`
Type string `json:"type,omitempty"`
Description string `json:"description,omitempty"`
Format string `json:"format,omitempty"`
Pattern string `json:"pattern,omitempty"`
Items *Schema `json:"items,omitempty"`
Properties map[string]*Schema `json:"properties,omitempty"`
Required []string `json:"required,omitempty"`
AdditionalProperties *Schema `json:"additionalProperties,omitempty"`
// AnyOf is populated only by MarshalJSON, to express a nullable $ref
// (see MarshalJSON's doc comment) — it's not set directly by schema.go.
AnyOf []*Schema `json:"anyOf,omitempty"`
Enum []string `json:"enum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMinimum *float64 `json:"exclusiveMinimum,omitempty"`
ExclusiveMaximum *float64 `json:"exclusiveMaximum,omitempty"`
MinLength *int `json:"minLength,omitempty"`
MaxLength *int `json:"maxLength,omitempty"`
Nullable bool `json:"-"` // folded into Type as ["<type>", "null"] at marshal time
}
Schema is an OpenAPI 3.1 (JSON Schema 2020-12) schema object. Only the subset of keywords the reflection engine in schema.go actually produces is represented.
func (*Schema) MarshalJSON ¶
MarshalJSON folds Nullable into the "type" keyword as ["<type>", "null"], per JSON Schema 2020-12 (OpenAPI 3.1). A nullable $ref can't express that the same way — a sibling "type" next to "$ref" isn't reliably honored by tooling — so it's wrapped as {"anyOf": [{"$ref": ...}, {"type": "null"}]} instead. Everything else marshals like a plain struct.