-
Notifications
You must be signed in to change notification settings - Fork 0
Documenting a Route
type OpenAPIRoute interface {
OperationID() string
Summary() string
Description() string
Tags() []string
}A route that does not implement it is excluded from the document. That is how an operational endpoint stays private without configuration.
type CreateUser struct{ rxroute.Route }
func (r *CreateUser) OperationID() string { return "createUser" }
func (r *CreateUser) Summary() string { return "Create a user" }
func (r *CreateUser) Description() string { return "Creates a user account and returns the created record." }
func (r *CreateUser) Tags() []string { return []string{"users"} }
app.RegisterRoute(&CreateUser{Route: rxroute.New("POST", "/users", createUser)})⚠ Pointer receiver → register as a pointer, or the assertion fails and the route is silently undocumented.
OperationID must be unique across the whole document. Client generators
use it as a method name, so createUser becomes client.CreateUser(...).
Duplicates produce a document that generates broken clients.
Picked up from the same interface the validation extension reads:
func (r *CreateUser) RequestBody() rextension.BodySchema {
return rextension.Scalar(CreateUserRequest{})
}
func (r *CreateUser) Responses() map[int]rextension.BodySchema {
return map[int]rextension.BodySchema{
201: rextension.Scalar(UserResponse{}),
422: rextension.Scalar(rextension.Problem{}),
}
}Register both extensions and one declaration produces validation and documentation. See Schemas.
By default each response entry is described as "Response for status N", which
is true and useless.
func (r *CreateUser) ResponseDescriptions() map[int]string {
return map[int]string{
201: "The user was created",
409: "A user with that email already exists",
422: "The request body failed validation",
}
}Examples are the difference between a document a client can read and one they can use.
func (r *CreatePayment) RequestBodyExamples() map[string]openapi.ExampleObject {
return map[string]openapi.ExampleObject{
"usd-card": {
Summary: "Card payment in USD",
Value: PaymentRequest{Amount: 1000, Currency: "USD", Method: "card"},
},
"eur-transfer": {
Summary: "Bank transfer in EUR",
Value: PaymentRequest{Amount: 2500, Currency: "EUR", Method: "transfer"},
},
}
}
func (r *CreatePayment) ResponseExamples() map[int]map[string]openapi.ExampleObject {
return map[int]map[string]openapi.ExampleObject{
201: {"settled": {Summary: "Settled immediately", Value: PaymentResponse{Status: "settled"}}},
422: {"bad-currency": {Summary: "Unsupported currency", Value: rextension.Problem{
Status: 422, Title: "Unprocessable Entity",
}}},
}
}Both use the OpenAPI 3.1 examples map — the deprecated singular example
property is not emitted.
Operation tags group endpoints in a UI. Describe them once at the top level:
openapi.NewConfig(
openapi.WithTags(
openapi.Tag{Name: "users", Description: "User accounts and profiles"},
openapi.Tag{
Name: "payments",
Description: "Charges, refunds and settlement",
ExternalDocs: &openapi.ExternalDocs{
Description: "Payment guide",
URL: "https://docs.example.com/payments",
},
},
),
)WithTags merges with previously registered tags; a duplicate name is
overwritten by the last entry.
Path parameters are derived from the route pattern — /users/{id} produces an
id path parameter automatically. There is nothing to declare.
Implementing four methods per route type gets old. Wrap:
type doc struct {
rxroute.Route
id, summary, description string
tags []string
}
func (d *doc) OperationID() string { return d.id }
func (d *doc) Summary() string { return d.summary }
func (d *doc) Description() string { return d.description }
func (d *doc) Tags() []string { return d.tags }
func documented(rt rxroute.Route, id, summary, description string, tags ...string) rxroute.Route {
return &doc{Route: rt, id: id, summary: summary, description: description, tags: tags}
}Embed the schema and security wrappers on the same type when you need them — the interfaces are independent, so one route value can satisfy all of them.
rextension-openapi — OpenAPI 3.1 generation for Rex · MIT · © 2026 Kryovyx
Ecosystem