When the scanner emits a type
codescan does not emit a definition for every type it can see. A named type reaches the spec when either of these holds:
- it is reachable — referenced (directly or transitively) from an operation, parameter, response, or another emitted model; or
- it is registered — annotated
swagger:model, which (withOptions.ScanModels) publishes it even when nothing references it; or - it is a subtype of an emitted discriminated base — a
swagger:modelthat composes that base withswagger:allOf. This one runs against the reference direction (a subtype$refs its base, never the reverse), so it is the one case where a definition arrives without anything referencing it. See Polymorphic models.
A type that is neither reachable nor registered is simply absent — the scanner never invents it. The package below has one of each case:
// Order is reached only through Cart below. A referenced named type is emitted
// as a $ref target even without swagger:model.
type Order struct {
// ID is the order identifier.
ID string `json:"id"`
}
// Cart references Order, so Order gets a definition and the field a $ref.
//
// swagger:model
type Cart struct {
// Order is the referenced (and therefore emitted) nested model.
Order Order `json:"order"`
}
// Standalone is never referenced, but swagger:model together with ScanModels
// publishes it anyway.
//
// swagger:model
type Standalone struct {
// Label is a free-text label.
Label string `json:"label"`
}
// Orphan is never referenced and carries no swagger:model — the scanner does
// not invent it, so it never reaches the spec.
type Orphan struct {
// Secret is internal.
Secret string `json:"secret"`
}Full source: docs/examples/shaping/discovery/discovery.go
Scanned with ScanModels: true, the definitions are:
{
"Cart": {
"type": "object",
"title": "Cart references Order, so Order gets a definition and the field a $ref.",
"properties": {
"order": {
"$ref": "#/definitions/Order"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/discovery"
},
"Order": {
"description": "Order is reached only through Cart below. A referenced named type is emitted\nas a $ref target even without swagger:model.",
"type": "object",
"properties": {
"id": {
"description": "ID is the order identifier.",
"type": "string",
"x-go-name": "ID"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/discovery"
},
"Standalone": {
"description": "Standalone is never referenced, but swagger:model together with ScanModels\npublishes it anyway.",
"type": "object",
"properties": {
"label": {
"description": "Label is a free-text label.",
"type": "string",
"x-go-name": "Label"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/discovery"
}
}
Full source: docs/examples/shaping/discovery/testdata/definitions.json
Cart— aswagger:modelroot.Order— has noswagger:model, yet it is emitted (as a$reftarget) becauseCartreferences it. You do not need to annotate every nested type.Standalone— aswagger:modelthat nothing references;ScanModelspublishes it anyway.Orphan— neither referenced nor annotated, so it never appears.
Info
If a model is missing from your spec, it is almost always unreachable: no
operation/parameter/response/model leads to it. Either reference it, or annotate
it swagger:model and scan with ScanModels. For the opposite problem — a
ScanModels scan that pulls in models you do not want, like Standalone —
see Pruning unused models.
Generic and embedded types
codescan resolves types through go/packages type information, so two forms
that look tricky still work:
- Generics. An instantiated generic —
WrappedRequest[Order], whether annotatedswagger:parametersorswagger:model— emits the concrete type: the type argument is substituted, so aT-typed field becomes a$refto the argument’s definition. The generic’s declaration may live in a different file from its instantiation. A free (un-instantiated) type parameter is skipped with a warning. - Embedded fields, including those from an external package or a type
declared in another source file, are promoted into the embedding type (only
exported members, recursively), and a custom field type resolves to its
underlying type. An
in:/required:annotation written on the embedded field itself applies to all the members it promotes. An embed that carries ajson:tag (Base \json:“base”`) is **not** promoted — it nests as a single property of that name (a$ref` to the embedded type), matching Go’s own JSON encoding. - A type the scanner cannot model — e.g. a bare function type — is skipped with a warning rather than failing the whole scan; the annotated models around it are still emitted.