📖 9 min read (~ 2000 words).

Routes & operations

Routes and operations turn an annotation into an entry in the spec’s paths map, wired to the parameters it accepts and the responses it returns. This page covers the two operation annotations and the companion structs they reference. Each pane pairs the annotated Go (left) with the exact fragment the scanner emits (right), from the test-covered docs/examples/concepts/routes package.

For the exhaustive rule on any annotation below, follow its link to the Maintainers reference; the Parameters: / Responses: body grammars are covered in Sub-languages.

swagger:route

swagger:route <METHOD> <path> [tags] <operationID> declares a path and its operation in one annotation. The body’s responses: block ties status codes to named responses ($ref into the spec’s responses). It lives in a plain comment block — no Go declaration required.

Annotated Go
// swagger:route GET /pets pets listPets
//
// Lists pets in the store, optionally filtered by tag.
//
// responses:
//
//	200: petsResponse
//	default: errorResponse

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

{
  "get": {
    "tags": [
      "pets"
    ],
    "summary": "Lists pets in the store, optionally filtered by tag.",
    "operationId": "listPets",
    "parameters": [
      {
        "type": "string",
        "x-go-name": "Tag",
        "description": "Tag filters pets by tag.",
        "name": "tag",
        "in": "query"
      },
      {
        "maximum": 100,
        "minimum": 1,
        "type": "integer",
        "format": "int32",
        "x-go-name": "Limit",
        "description": "Limit caps the number of results.",
        "name": "limit",
        "in": "query"
      }
    ],
    "responses": {
      "200": {
        "$ref": "#/responses/petsResponse"
      },
      "default": {
        "$ref": "#/responses/errorResponse"
      }
    }
  }
}

Full source: docs/examples/concepts/routes/testdata/route.json

The body can also carry an indented Parameters: block to declare simple parameters (path / query / header) inline — no swagger:parameters struct needed. For body parameters or parameter sets shared across operations, use swagger:parameters instead. The block syntax is covered in sub-languages.

swagger:operation

swagger:operation carries the same header but spells the operation out as a YAML document after a --- fence — useful when you want to author the operation object directly (here a path parameter and an inline $ref response schema).

Annotated Go
// swagger:operation GET /pets/{id} pets getPet
//
// ---
// summary: Get a pet by ID.
// parameters:
//   - name: id
//     in: path
//     required: true
//     type: integer
//     format: int64
// responses:
//   '200':
//     description: the requested pet
//     schema:
//       $ref: '#/definitions/Pet'
//   default:
//     $ref: '#/responses/errorResponse'

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

{
  "get": {
    "tags": [
      "pets"
    ],
    "summary": "Get a pet by ID.",
    "operationId": "getPet",
    "parameters": [
      {
        "type": "integer",
        "format": "int64",
        "name": "id",
        "in": "path",
        "required": true
      }
    ],
    "responses": {
      "200": {
        "description": "the requested pet",
        "schema": {
          "$ref": "#/definitions/Pet"
        }
      },
      "default": {
        "$ref": "#/responses/errorResponse"
      }
    }
  }
}

Full source: docs/examples/concepts/routes/testdata/operation.json

swagger:parameters

swagger:parameters <operationID>… declares a struct whose fields become the parameters of the named operation(s). Field doc comments carry in:, the validations, and the description; the parameters attach to every operation ID listed.

Annotated Go
// ListPetsParams is the parameter set for the listPets operation. Each field
// becomes one parameter; the operation IDs after swagger:parameters name the
// operations the set applies to.
//
// swagger:parameters listPets
type ListPetsParams struct {
	// Tag filters pets by tag.
	//
	// in: query
	Tag string `json:"tag"`

	// Limit caps the number of results.
	//
	// in: query
	// minimum: 1
	// maximum: 100
	Limit int32 `json:"limit"`
}

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

parameters on listPets
[
  {
    "type": "string",
    "x-go-name": "Tag",
    "description": "Tag filters pets by tag.",
    "name": "tag",
    "in": "query"
  },
  {
    "maximum": 100,
    "minimum": 1,
    "type": "integer",
    "format": "int32",
    "x-go-name": "Limit",
    "description": "Limit caps the number of results.",
    "name": "limit",
    "in": "query"
  }
]

Full source: docs/examples/concepts/routes/testdata/parameters.json

A field marked in: body makes its Go type the request body schema — the usual shape for a POST or PUT payload:

Annotated Go
// CreatePetParams is the request parameter set for createPet. A field marked
// `in: body` makes its Go type the request body schema — the usual shape for a
// POST or PUT payload.
//
// swagger:parameters createPet
type CreatePetParams struct {
	// Body is the pet to create.
	//
	// in: body
	// required: true
	Body Pet `json:"body"`
}

// swagger:route POST /pets/import pets createPet
//
// responses:
//
//	200: petsResponse

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

parameters on createPet
[
  {
    "x-go-name": "Body",
    "description": "Body is the pet to create.",
    "name": "body",
    "in": "body",
    "required": true,
    "schema": {
      "$ref": "#/definitions/Pet"
    }
  }
]

Full source: docs/examples/concepts/routes/testdata/bodyparam.json

When a parameter field’s Go type is a struct (or any type that has no simple Swagger representation), it cannot be a query/path/header parameter on its own. A swagger:type override collapses it to a simple parameter — a scalar, or a []-wrapped scalar for an array parameter:

Annotated Go
// Cursor is an opaque pagination token. As a struct it cannot be a query
// parameter on its own; swagger:type publishes it as a simple parameter.
type Cursor struct {
	Page  int
	Token string
}

// FilterPetsParams shows swagger:type on parameter fields: a struct-typed field
// is collapsed to a simple parameter. The override accepts a scalar or a
// []-wrapped scalar — the inline / type-name forms are rejected here (a
// non-body parameter has no schema to inline into).
//
// swagger:parameters filterPets
type FilterPetsParams struct {
	// After is an opaque cursor carried as a plain string query parameter.
	//
	// in: query
	// swagger:type string
	After Cursor `json:"after"`

	// Sort is a list of sort keys carried as an array-of-string query parameter.
	//
	// in: query
	// swagger:type []string
	Sort []Cursor `json:"sort"`
}

// swagger:route GET /pets/filter pets filterPets
//
// Filter pets with cursor pagination.
//
// responses:
//
//	200: description: matched pets

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

parameters on filterPets
[
  {
    "type": "string",
    "x-go-name": "After",
    "description": "After is an opaque cursor carried as a plain string query parameter.",
    "name": "after",
    "in": "query"
  },
  {
    "type": "array",
    "items": {
      "type": "string"
    },
    "x-go-name": "Sort",
    "description": "Sort is a list of sort keys carried as an array-of-string query parameter.",
    "name": "sort",
    "in": "query"
  }
]

Full source: docs/examples/concepts/routes/testdata/paramtype.json

swagger:response

swagger:response <name> declares a struct as a named entry in the spec’s top-level responses. A Body field (or in: body) becomes the response schema; routes reference it by name. Here the body is a []Pet, so the schema is an array of $refs.

Annotated Go
// PetsResponse is the list returned by listPets.
//
// swagger:response petsResponse
type PetsResponse struct {
	// in: body
	Body []Pet
}

// ErrorResponse is the default error payload.
//
// swagger:response errorResponse
type ErrorResponse struct {
	// in: body
	Body struct {
		// Message is a human-readable error message.
		Message string `json:"message"`
	}
}

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

responses[petsResponse]
{
  "description": "PetsResponse is the list returned by listPets.",
  "schema": {
    "type": "array",
    "items": {
      "$ref": "#/definitions/Pet"
    }
  }
}

Full source: docs/examples/concepts/routes/testdata/response.json

swagger:file

swagger:file on a parameter field marks it as a binary upload — the parameter emits as {type: file}. It belongs on a formData field of a swagger:parameters struct.

Annotated Go
// swagger:route POST /pets/{id}/photo pets uploadPetPhoto
//
// responses:
//
//	200: petsResponse

// UploadParams is the multipart upload for the uploadPetPhoto operation.
//
// swagger:parameters uploadPetPhoto
type UploadParams struct {
	// Photo is the image to upload.
	//
	// in: formData
	// swagger:file
	Photo io.ReadCloser `json:"photo"`
}

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

parameters on uploadPetPhoto
[
  {
    "type": "file",
    "x-go-name": "Photo",
    "description": "Photo is the image to upload.",
    "name": "photo",
    "in": "formData"
  }
]

Full source: docs/examples/concepts/routes/testdata/file.json

externalDocs

An externalDocs: block (description + url) links an object out to external documentation. It rides an operation (in a swagger:route or swagger:operation body) and a full schema (a swagger:model). It is a full-Schema-only keyword: on a simple-schema parameter (anything but in: body) it is dropped with a diagnostic. The same ExternalDocs: block on a swagger:meta package populates the spec’s top-level externalDocs (see Document metadata).

Annotated Go
// swagger:route GET /pets/search pets searchPets
//
// Searches pets. The operation links out to external documentation.
//
// externalDocs:
//   description: Search guide
//   url: https://example.com/docs/search
//
// responses:
//
//	200: petsResponse

// CatalogEntry carries externalDocs at the schema level (the link rides the
// definition) and on its fields. (On a simple-schema parameter externalDocs is
// dropped with a diagnostic: it is a full-Schema-only keyword.)
//
// externalDocs: {description: "Catalog schema reference", url: "https://example.com/docs/catalog"}
//
// swagger:model
type CatalogEntry struct {
	// SKU is the catalog identifier.
	SKU string `json:"sku"`

	// Vendor is a plain field: externalDocs attaches directly to the property.
	//
	// externalDocs: {description: "Vendor field docs", url: "https://example.com/docs/vendor"}
	Vendor string `json:"vendor"`

	// Supplier is a $ref'd field: its sibling externalDocs lifts onto the
	// field's allOf compound (a bare $ref cannot carry sibling keywords).
	//
	// externalDocs: {description: "Supplier docs", url: "https://example.com/docs/supplier"}
	Supplier Supplier `json:"supplier"`
}

// Supplier is referenced by CatalogEntry.Supplier.
//
// swagger:model
type Supplier struct {
	// Name is the supplier name.
	Name string `json:"name"`
}

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

operation externalDocs
{
  "description": "Search guide",
  "url": "https://example.com/docs/search"
}

Full source: docs/examples/concepts/routes/testdata/externaldocs.json

On a model the link rides the definition, and it also rides individual struct fields: on a plain field it attaches to the property directly; on a $ref’d field it is lifted onto the field’s allOf compound (a bare $ref cannot carry sibling keywords). The value can be written as the indented block above or as an equivalent inline { description: …, url: … } mapping — which reads better on a single-line doc comment:

Annotated Go
// swagger:route GET /pets/search pets searchPets
//
// Searches pets. The operation links out to external documentation.
//
// externalDocs:
//   description: Search guide
//   url: https://example.com/docs/search
//
// responses:
//
//	200: petsResponse

// CatalogEntry carries externalDocs at the schema level (the link rides the
// definition) and on its fields. (On a simple-schema parameter externalDocs is
// dropped with a diagnostic: it is a full-Schema-only keyword.)
//
// externalDocs: {description: "Catalog schema reference", url: "https://example.com/docs/catalog"}
//
// swagger:model
type CatalogEntry struct {
	// SKU is the catalog identifier.
	SKU string `json:"sku"`

	// Vendor is a plain field: externalDocs attaches directly to the property.
	//
	// externalDocs: {description: "Vendor field docs", url: "https://example.com/docs/vendor"}
	Vendor string `json:"vendor"`

	// Supplier is a $ref'd field: its sibling externalDocs lifts onto the
	// field's allOf compound (a bare $ref cannot carry sibling keywords).
	//
	// externalDocs: {description: "Supplier docs", url: "https://example.com/docs/supplier"}
	Supplier Supplier `json:"supplier"`
}

// Supplier is referenced by CatalogEntry.Supplier.
//
// swagger:model
type Supplier struct {
	// Name is the supplier name.
	Name string `json:"name"`
}

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

#/definitions/CatalogEntry
{
  "description": "CatalogEntry carries externalDocs at the schema level (the link rides the\ndefinition) and on its fields. (On a simple-schema parameter externalDocs is\ndropped with a diagnostic: it is a full-Schema-only keyword.)",
  "type": "object",
  "properties": {
    "sku": {
      "description": "SKU is the catalog identifier.",
      "type": "string",
      "x-go-name": "SKU"
    },
    "supplier": {
      "description": "Supplier is a $ref'd field: its sibling externalDocs lifts onto the\nfield's allOf compound (a bare $ref cannot carry sibling keywords).",
      "allOf": [
        {
          "$ref": "#/definitions/Supplier"
        }
      ],
      "x-go-name": "Supplier",
      "externalDocs": {
        "description": "Supplier docs",
        "url": "https://example.com/docs/supplier"
      }
    },
    "vendor": {
      "description": "Vendor is a plain field: externalDocs attaches directly to the property.",
      "type": "string",
      "x-go-name": "Vendor",
      "externalDocs": {
        "description": "Vendor field docs",
        "url": "https://example.com/docs/vendor"
      }
    }
  },
  "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/routes",
  "externalDocs": {
    "description": "Catalog schema reference",
    "url": "https://example.com/docs/catalog"
  }
}

Full source: docs/examples/concepts/routes/testdata/externaldocs_schema.json

What’s next