📖 14 min read (~ 2800 words).

Model definitions

A definitions entry is the most common thing you ask codescan to produce. This page walks the annotations that create and shape one, from the plain swagger:model struct to the per-type overrides. Each pane pairs the annotated Go (left) with the exact fragment the scanner emits (right) — both come from the test-covered docs/examples/concepts/models package.

For the exhaustive rule on any annotation below, follow its link to the Maintainers reference.

swagger:model

swagger:model publishes a Go struct as a definition. Field doc comments become property descriptions; json tags drive the property names; the Go type drives the JSON-Schema type / format. Well-known standard-library types resolve automatically — a time.Time field, for instance, is published as {type: string, format: date-time}.

Annotated Go
// Pet is a single pet in the store.
//
// swagger:model
type Pet struct {
	// ID is the unique identifier.
	ID int64 `json:"id"`

	// Name is the pet's display name.
	Name string `json:"name"`

	// Tags categorise the pet.
	Tags []string `json:"tags,omitempty"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Pet
{
  "type": "object",
  "title": "Pet is a single pet in the store.",
  "properties": {
    "id": {
      "description": "ID is the unique identifier.",
      "type": "integer",
      "format": "int64",
      "x-go-name": "ID"
    },
    "name": {
      "description": "Name is the pet's display name.",
      "type": "string",
      "x-go-name": "Name"
    },
    "tags": {
      "description": "Tags categorise the pet.",
      "type": "array",
      "items": {
        "type": "string"
      },
      "x-go-name": "Tags"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/model.json

A swagger:model that nothing else references appears in definitions only when you scan with Options.ScanModels (the -m flag). See When the scanner emits a type.

swagger:strfmt

swagger:strfmt <name> marks a named string type as a custom format. The type does not become its own definition — instead, every field typed by it renders as {type: string, format: <name>}.

Annotated Go
// MAC is a hardware address rendered as a colon-separated hex string.
//
// swagger:strfmt mac
type MAC string

func (m MAC) MarshalText() ([]byte, error)  { return []byte(m), nil }
func (m *MAC) UnmarshalText(b []byte) error { *m = MAC(b); return nil }

// Device exposes a strfmt-typed field: wherever MAC appears it renders inline
// as {type: string, format: mac}.
//
// swagger:model
type Device struct {
	// Addr is the hardware address.
	Addr MAC `json:"addr"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Device
{
  "description": "Device exposes a strfmt-typed field: wherever MAC appears it renders inline\nas {type: string, format: mac}.",
  "type": "object",
  "properties": {
    "addr": {
      "description": "Addr is the hardware address.",
      "type": "string",
      "format": "mac",
      "x-go-name": "Addr"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/strfmt.json

Add swagger:model to the strfmt type to opt it into a first-class definition ({type: string, format: <name>}) that fields $ref instead of inlining — the general swagger:model ⇒ definition + $ref rule.

swagger:enum

swagger:enum <name> collects the type’s const values. When a model field references the type, the property carries the enum array and an x-go-enum-desc extension built from the per-value doc comments. (The enum type is reachable, and so emitted, only because a model field points at it.) A bare swagger:enum on the type declaration works too — the name is inferred.

Annotated Go
// Priority is the urgency level on a task.
//
// swagger:enum Priority
type Priority string

const (
	// PriorityLow is for tasks that can wait.
	PriorityLow Priority = "low"
	// PriorityMedium is the default.
	PriorityMedium Priority = "medium"
	// PriorityHigh is for tasks that must run soon.
	PriorityHigh Priority = "high"
)

// Task is a unit of work carrying an enum-typed field. Referencing Priority
// from a model is what makes the enum reachable, and so emitted.
//
// swagger:model
type Task struct {
	// Priority is the task's urgency.
	Priority Priority `json:"priority"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Task
{
  "description": "Task is a unit of work carrying an enum-typed field. Referencing Priority\nfrom a model is what makes the enum reachable, and so emitted.",
  "type": "object",
  "properties": {
    "priority": {
      "description": "Priority is the task's urgency.\nlow PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon.",
      "type": "string",
      "enum": [
        "low",
        "medium",
        "high"
      ],
      "x-go-enum-desc": "low PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon.",
      "x-go-name": "Priority"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/enum.json

Here the values inline on the referencing field. Add swagger:model to the enum type to make it a first-class definition (carrying the enum array) that fields $ref — again the swagger:model ⇒ definition + $ref rule.

Enums have more to them than a string const block: iota and computed members, what decides the emitted type / format, and the inline form parameters and headers take. They get their own page — Enumerations.

swagger:allOf

Embedding base types under swagger:allOf composes a schema. Each embedded base becomes a $ref arm of the allOf; the struct’s own (non-embedded) fields form a final inline arm. That last arm is inline rather than a $ref because those fields are new to this type — they belong to no existing definition to point at. Here Dog embeds two bases (Animal, Tagged) and adds breed, producing three arms: two $refs and one inline object.

Annotated Go
// Animal is one abstract base.
//
// swagger:model
type Animal struct {
	// Kind discriminates the animal.
	Kind string `json:"kind"`
}

// Tagged is a second reusable base.
//
// swagger:model
type Tagged struct {
	// Tags label the resource.
	Tags []string `json:"tags"`
}

// Dog composes two base models plus its own fields: each embedded base becomes
// a $ref arm of the allOf, and the struct's own (non-embedded) fields — which
// are new and cannot be a $ref — form the final inline arm.
//
// swagger:model
type Dog struct {
	// swagger:allOf
	Animal

	// swagger:allOf
	Tagged

	// Breed is the dog's breed.
	Breed string `json:"breed"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Dog
{
  "description": "Dog composes two base models plus its own fields: each embedded base becomes\na $ref arm of the allOf, and the struct's own (non-embedded) fields — which\nare new and cannot be a $ref — form the final inline arm.",
  "allOf": [
    {
      "$ref": "#/definitions/Animal"
    },
    {
      "$ref": "#/definitions/Tagged"
    },
    {
      "type": "object",
      "properties": {
        "breed": {
          "description": "Breed is the dog's breed.",
          "type": "string",
          "x-go-name": "Breed"
        }
      }
    }
  ],
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/allof.json

When the base also declares a discriminator, this composition becomes a Swagger 2.0 type hierarchy — see Polymorphic models.

swagger:type

swagger:type <type> overrides the type codescan would infer. Here a [16]byte field is published as a string.

Annotated Go
// ULID is a 128-bit identifier stored as bytes but rendered as a string.
//
// swagger:type string
type ULID [16]byte

// Token carries a field whose inferred type is overridden, inline.
//
// swagger:model
type Token struct {
	// ID renders as a string despite its [16]byte Go type.
	ID ULID `json:"id"`
}


// RawID is a custom 16-byte identifier — an array under the hood, so left to
// itself a field of this type would render as an array of integers.
type RawID [16]byte

// Coupon overrides the type of a single field directly on the field doc — no
// wrapper-type annotation. Code publishes as a bare string while RawID is left
// untouched everywhere else it appears.
//
// swagger:model
type Coupon struct {
	// Code is an opaque identifier published as a string.
	//
	// swagger:type string
	Code RawID `json:"code"`

	// Amount is the discount in cents.
	Amount int64 `json:"amount"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Token
{
  "type": "object",
  "title": "Token carries a field whose inferred type is overridden, inline.",
  "properties": {
    "id": {
      "description": "ID renders as a string despite its [16]byte Go type.",
      "type": "string",
      "x-go-name": "ID"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/type.json

swagger:type is an inline directive: it renders the chosen type in place and never emits a $ref. The value is a scalar type (string, integer, number, boolean, object, or a Go builtin like int64), []T for an array of an inlined type, inline to expand the field’s own Go type, or a known type name to inline that type. array is deprecated in favour of inline / []T, and file is rejected (use swagger:file). When combined with swagger:strfmt, the type wins and the format is kept only if compatible — see the reference.

The override also works on an individual field doc — no wrapper-type annotation. Here Code is published as a string while its RawID type is left untouched everywhere else:

Annotated Go
// RawID is a custom 16-byte identifier — an array under the hood, so left to
// itself a field of this type would render as an array of integers.
type RawID [16]byte

// Coupon overrides the type of a single field directly on the field doc — no
// wrapper-type annotation. Code publishes as a bare string while RawID is left
// untouched everywhere else it appears.
//
// swagger:model
type Coupon struct {
	// Code is an opaque identifier published as a string.
	//
	// swagger:type string
	Code RawID `json:"code"`

	// Amount is the discount in cents.
	Amount int64 `json:"amount"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Coupon
{
  "description": "Coupon overrides the type of a single field directly on the field doc — no\nwrapper-type annotation. Code publishes as a bare string while RawID is left\nuntouched everywhere else it appears.",
  "type": "object",
  "properties": {
    "amount": {
      "description": "Amount is the discount in cents.",
      "type": "integer",
      "format": "int64",
      "x-go-name": "Amount"
    },
    "code": {
      "description": "Code is an opaque identifier published as a string.",
      "type": "string",
      "x-go-name": "Code"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/typefield.json

swagger:name

swagger:name <name> overrides the JSON property name a field or method renders as. It works on a struct field (overriding the json: tag / Go field name) and on an interface method. It is most useful on interface methods, which publish one property per nullary method and cannot carry a json tag: below, StructType() would default to structType, and the annotation publishes it as jsonClass instead.

Note

swagger:name is the legacy annotation form. The name: keyword is the canonical, universal equivalent — it renames a property here exactly the same way, and is the only form that also works on parameters and response headers. Precedence: name: > swagger:name > json: tag > Go field name.

Annotated Go
// Car is exposed as a schema via its method set. Interface methods cannot carry
// a json tag, so by default each property takes the camelCased method name;
// swagger:name overrides that where the default is not what you want.
//
// swagger:model
type Car interface {
	// Maker is the manufacturer. With no override the property is the
	// camelCased method name, "maker".
	Maker() string

	// StructType is the polymorphic class. Without the override the property
	// would be "structType"; swagger:name publishes it as "jsonClass".
	//
	// swagger:name jsonClass
	StructType() string
}


// Account shows the universal name: keyword renaming model struct fields. The
// same keyword used on parameters and response headers also sets a property key
// here, winning over a json tag, the legacy swagger:name annotation, and the Go
// field name.
//
// swagger:model
type Account struct {
	// Bal has no json tag; the keyword sets the property key directly.
	//
	// name: balance
	Bal float64

	// Currency carries both naming forms; the keyword wins over the
	// legacy annotation and the json tag.
	//
	// name: currencyCode
	// swagger:name legacyCurrency
	Currency string `json:"currency"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Car
{
  "description": "Car is exposed as a schema via its method set. Interface methods cannot carry\na json tag, so by default each property takes the camelCased method name;",
  "type": "object",
  "properties": {
    "jsonClass": {
      "description": "StructType is the polymorphic class. Without the override the property\nwould be \"structType\"; swagger:name publishes it as \"jsonClass\".",
      "type": "string",
      "x-go-name": "StructType"
    },
    "maker": {
      "description": "Maker is the manufacturer. With no override the property is the\ncamelCased method name, \"maker\".",
      "type": "string",
      "x-go-name": "Maker"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/name.json

The name: keyword does the same on a model property — and, being the universal form, with the same syntax you would use on a parameter or header. Here it names a tag-less field directly, and on a field that also carries a json: tag and a legacy swagger:name, the keyword wins (name: > swagger:name > json: tag > Go field name):

Annotated Go
// Account shows the universal name: keyword renaming model struct fields. The
// same keyword used on parameters and response headers also sets a property key
// here, winning over a json tag, the legacy swagger:name annotation, and the Go
// field name.
//
// swagger:model
type Account struct {
	// Bal has no json tag; the keyword sets the property key directly.
	//
	// name: balance
	Bal float64

	// Currency carries both naming forms; the keyword wins over the
	// legacy annotation and the json tag.
	//
	// name: currencyCode
	// swagger:name legacyCurrency
	Currency string `json:"currency"`
}

Full source: docs/examples/concepts/models/models.go

#/definitions/Account
{
  "description": "Account shows the universal name: keyword renaming model struct fields. The\nsame keyword used on parameters and response headers also sets a property key\nhere, winning over a json tag, the legacy swagger:name annotation, and the Go\nfield name.",
  "type": "object",
  "properties": {
    "balance": {
      "description": "Bal has no json tag; the keyword sets the property key directly.",
      "type": "number",
      "format": "double",
      "x-go-name": "Bal"
    },
    "currencyCode": {
      "description": "Currency carries both naming forms; the keyword wins over the\nlegacy annotation and the json tag.",
      "type": "string",
      "x-go-name": "Currency"
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"
}

Full source: docs/examples/concepts/models/testdata/namekeyword.json

swagger:ignore

swagger:ignore drops a declaration from the output. The scanner sees Secret, classifies it, then excludes it — so it never reaches the definitions (a fact the example’s TestIgnoreOmitsType asserts). It also works on a single struct field — placed on the field’s doc comment, it drops just that property from the model.

// Secret never reaches the spec.
//
// swagger:ignore
type Secret struct {
	// Token is internal.
	Token string `json:"token"`
}

Full source: docs/examples/concepts/models/models.go

Decorating a $ref

When a field’s type resolves to a named model, the property is a $ref — and a bare $ref cannot carry sibling keywords (a JSON Schema draft-4 rule). So a field that is both a reference and decorated (a description, a vendor extension, a default/example, a validation override) would lose its decorations.

codescan avoids that by wrapping the $ref as a member of an allOf. The property is then an ordinary schema object, free to carry the decorations alongside the reference:

  • the description and any x-* extensions sit on the property itself;
  • a value override (default, example) rides a second allOf member;
  • required rides the parent model’s required list.
Annotated Go
// Address is a referenced model.
//
// swagger:model
type Address struct {
	// Street is the street line.
	Street string `json:"street"`
}

// Person references Address through a field that also carries a description and
// a vendor extension. A bare $ref cannot hold sibling keywords, so codescan
// wraps the reference as a member of an allOf — the property is then a normal
// schema that keeps the description and the x-* extension, and required goes to
// the parent. Nothing is dropped.
//
// swagger:model
type Person struct {
	// Home is where the person lives.
	//
	// required: true
	// extensions:
	//   x-ui-order: 3
	Home Address `json:"home"`
}

Full source: docs/examples/concepts/refoverride/refoverride.go

#/definitions/Person
{
  "description": "Person references Address through a field that also carries a description and\na vendor extension. A bare $ref cannot hold sibling keywords, so codescan\nwraps the reference as a member of an allOf — the property is then a normal\nschema that keeps the description and the x-* extension, and required goes to\nthe parent. Nothing is dropped.",
  "type": "object",
  "required": [
    "home"
  ],
  "properties": {
    "home": {
      "description": "Home is where the person lives.",
      "allOf": [
        {
          "$ref": "#/definitions/Address"
        }
      ],
      "x-go-name": "Home",
      "x-ui-order": 3
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/refoverride"
}

Full source: docs/examples/concepts/refoverride/testdata/refoverride.json

Nothing is dropped. The description-only case is the exception — it is governed by the DescWithRef option (see Descriptions beside a $ref) — and a default/example on a $ref’d field is shown in Examples & defaults.

Info

Same-name collisions. Two structs that share the same short name in different packages stay distinct: codescan keys each by a compiler-unique identity and qualifies the colliding ones with a package segment (billing.Account / identity.AccountBillingAccount / IdentityAccount), deterministically. Pin the names in your public contract with an explicit swagger:model <Name>, and let auto-resolution handle the rest — see Resolving $ref name conflicts.

What’s next