Sharing parameters & responses
When the same header, query parameter or error response appears on many
operations, you don’t have to repeat it. codescan can publish a parameter or
response once into the spec’s top-level parameters / responses maps, then
reference it from each operation as a $ref. This is the OpenAPI 2.0 shared
namespace (#/parameters/{name}, #/responses/{name}).
Each pane below pairs the annotated Go (left) with the exact fragment the scanner
emits (right), from the test-covered
docs/examples/concepts/sharedparams
package. For the per-operation basics this builds on — the plain
swagger:parameters <operationID> and swagger:response <name> forms — see
Routes & operations.
Declaring a shared parameter
swagger:parameters * declares a struct whose fields are registered at the spec
top level, #/parameters/{name}, keyed by each parameter’s resolved name. The
bare * is register-only: it publishes the parameter but does not, by
itself, attach it to any operation.
The convenience form swagger:parameters * <operationID>… does both at once — it
registers the parameter and $refs it into the listed operations, which is
handy for a small spec.
// CommonHeaders registers a reusable header parameter at the spec top
// level, #/parameters/X-Request-ID. The bare `*` target is register-only:
// it publishes the parameter but does not, by itself, attach it to any
// operation.
//
// swagger:parameters *
type CommonHeaders struct {
// RequestID correlates a request across services.
//
// in: header
RequestID string `json:"X-Request-ID"`
}
// AuthHeader registers #/parameters/X-API-Key and, in the same breath,
// $ref's it into the createPet operation — the convenient `* <opid>`
// form for a small spec.
//
// swagger:parameters * createPet
type AuthHeader struct {
// APIKey authorises access.
//
// in: header
// required: true
APIKey string `json:"X-API-Key"`
}
// ErrorResponse is the common error envelope returned by every operation.
//
// swagger:response *
type ErrorResponse struct {
// in: body
Body struct {
// Code is a machine-readable error code.
Code int `json:"code"`
// Message is a human-readable error message.
Message string `json:"message"`
} `json:"body"`
}Full source: docs/examples/concepts/sharedparams/sharedparams.go
Both structs land in the top-level parameters map, each keyed by its parameter
name (the json: tag, or a name: / swagger:name override):
{
"X-API-Key": {
"type": "string",
"x-go-name": "APIKey",
"description": "APIKey authorises access.",
"name": "X-API-Key",
"in": "header",
"required": true
},
"X-Request-ID": {
"type": "string",
"x-go-name": "RequestID",
"description": "RequestID correlates a request across services.",
"name": "X-Request-ID",
"in": "header"
}
}
Full source: docs/examples/concepts/sharedparams/testdata/parameters.json
Referencing a shared parameter
Once a parameter is registered, an operation can pull it in by name. There are two reference channels:
swagger:parameters * <operationID>on the declaring struct — the convenience form above.AuthHeaderuses it to injectX-API-KeyintocreatePet.swagger:parameters <operationID> <name>…as a standalone marker on the operation’s function — the scaling channel: the shared struct need not enumerate every operation that wants the parameter; instead each operation opts in next to its ownswagger:route.
// ListPets lists pets.
//
// The standalone reference marker on the next line pulls the shared
// X-Request-ID parameter into this operation as a $ref. This is the
// scaling channel: the shared struct need not enumerate every operation
// that wants the parameter.
//
// swagger:route GET /pets pets listPets
// swagger:parameters listPets X-Request-ID
// Responses:
//
// default: ErrorResponse
func ListPets() {}
// CreatePet creates a pet. Its X-API-Key comes from AuthHeader
// (#/parameters/X-API-Key, $ref'd via `* createPet`); its body comes from
// the inlined CreatePetParams.
//
// swagger:route POST /pets pets createPet
// Responses:
//
// default: ErrorResponse
func CreatePet() {}Full source: docs/examples/concepts/sharedparams/sharedparams.go
listPets opts in through the standalone marker, so its only parameter is a
$ref to the shared X-Request-ID:
{
"tags": [
"pets"
],
"operationId": "listPets",
"parameters": [
{
"$ref": "#/parameters/X-Request-ID"
}
],
"responses": {
"default": {
"$ref": "#/responses/ErrorResponse"
}
}
}
Full source: docs/examples/concepts/sharedparams/testdata/listpets.json
createPet receives the $ref’d X-API-Key (from * createPet) alongside
its own inlined body parameter — references and inline parameters coexist:
{
"tags": [
"pets"
],
"operationId": "createPet",
"parameters": [
{
"x-go-name": "Body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Pet"
}
},
{
"$ref": "#/parameters/X-API-Key"
}
],
"responses": {
"default": {
"$ref": "#/responses/ErrorResponse"
}
}
}
Full source: docs/examples/concepts/sharedparams/testdata/createpet.json
Path-item parameters
A parameter can also attach to a whole path rather than a single operation.
swagger:parameters /path inlines a struct’s fields into the path-item’s
parameters array, so every operation under that path inherits them.
// TenantHeader inlines a required header into the /pets/{id} path-item
// itself, so every operation under that exact path inherits it. The
// target is a literal path, and matching is exact — OAS2 has no path
// hierarchy, so this does NOT apply to /pets.
//
// swagger:parameters /pets/{id}
type TenantHeader struct {
// Tenant scopes the request to a customer.
//
// in: header
// required: true
Tenant string `json:"X-Tenant"`
}
// GetPet fetches one pet. It declares no header of its own; X-Tenant
// reaches it through the /pets/{id} path-item parameter above.
//
// swagger:route GET /pets/{id} pets getPet
// Responses:
//
// default: ErrorResponse
func GetPet() {}Full source: docs/examples/concepts/sharedparams/sharedparams.go
X-Tenant now rides the /pets/{id} path-item; getPet inherits it without
declaring a header of its own:
{
"get": {
"tags": [
"pets"
],
"operationId": "getPet",
"responses": {
"default": {
"$ref": "#/responses/ErrorResponse"
}
}
},
"parameters": [
{
"type": "string",
"x-go-name": "Tenant",
"description": "Tenant scopes the request to a customer.",
"name": "X-Tenant",
"in": "header",
"required": true
}
]
}
Full source: docs/examples/concepts/sharedparams/testdata/pathitem.json
Warning
Exact path, no hierarchy. OpenAPI 2.0 has no path nesting, so the target is
matched literally: swagger:parameters /pets/{id} applies to /pets/{id} only —
not to /pets. Path-item parameters also co-exist with operation-level
ones rather than replacing them; if an operation declares a parameter with the
same (name, in), the operation’s wins at resolution time per the OAS2 rule.
To $ref an already-registered shared parameter into a path-item (instead of
inlining a new one), use the reference form with a path target:
swagger:parameters /path <name>… — the path-item analogue of the per-operation
marker above.
Shared responses
Responses share the same way. swagger:response * registers a struct at
#/responses/{name} (keyed by the Go type name). The * is a synonym for the
bare/named swagger:response form — its job is to mark the response as a
shared one. Operations then name it in their Responses: block and it is
emitted as a $ref.
// ErrorResponse is the common error envelope returned by every operation.
//
// swagger:response *
type ErrorResponse struct {
// in: body
Body struct {
// Code is a machine-readable error code.
Code int `json:"code"`
// Message is a human-readable error message.
Message string `json:"message"`
} `json:"body"`
}Full source: docs/examples/concepts/sharedparams/sharedparams.go
The shared ErrorResponse lands in the top-level responses map:
{
"ErrorResponse": {
"description": "ErrorResponse is the common error envelope returned by every operation.",
"schema": {
"type": "object",
"properties": {
"code": {
"description": "Code is a machine-readable error code.",
"type": "integer",
"format": "int64",
"x-go-name": "Code"
},
"message": {
"description": "Message is a human-readable error message.",
"type": "string",
"x-go-name": "Message"
}
}
}
}
}
Full source: docs/examples/concepts/sharedparams/testdata/responses.json
Both routes write default: ErrorResponse, which resolves to a single shared
$ref (visible as responses.default.$ref in the operation panes above) —
one error envelope, defined once, referenced everywhere.
Conflicts, duplicates & dangling references
The shared namespace is referenced only by short name, so codescan cannot
silently rename a collision the way it
deconflicts model definitions.
Instead it applies a deterministic, observable policy and reports every
adjustment through Options.OnDiagnostic (the scan never fails on these — it
keeps a valid spec and warns):
| Situation | Policy | Diagnostic |
|---|---|---|
Two swagger:parameters * register the same name | keep-first (sorted by package path then position; never renamed) — later one dropped | scan.shared-parameter-conflict |
Two swagger:response * register the same name | keep-first; later one dropped | scan.shared-response-conflict |
A reference names a parameter no * registered | reference dropped (no dangling $ref emitted) | scan.dangling-parameter-ref |
| An operation names an unregistered shared response | reference dropped | scan.dangling-response-ref |
A * <opid>… marker repeats an operation id | duplicate dropped | scan.duplicate-target |
| A reference repeats a parameter name | collapses to a single $ref | scan.duplicate-ref |
Note
The shared parameters, responses and definitions namespaces are
independent: #/parameters/Status, #/responses/Status and
#/definitions/Status can all coexist. The resolved (name:-overridden) name is
the key, so references must use that name — not the Go field name. An InputSpec
overlay entry seeds the namespace and wins any keep-first conflict.
What’s next
- Routes & operations — the
per-operation
swagger:parameters/swagger:responsebasics. - Pruning unused models — shared parameters and responses count as reachability roots when pruning.
- Keyword reference — the exhaustive
parameters/responsesbody grammars.