REST API Docs

This content was generated with the assistance of AI and should be verified against the official documentation of each tool before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page was written against DocFX 2.81.0 (the latest release, published on 2026-09-25) and checked on 2026-10-26. The examples were run with DocFX 2.81.0 installed as a .NET tool inside the mcr.microsoft.com/dotnet/sdk:10.0 Docker image, so the listings show real output. DocFX is community-maintained under the .NET Foundation; see the DocFX overview for governance. The .NET side of the same site is covered in .NET API Docs; this page is about HTTP APIs described by a Swagger file.

What DocFX does with REST APIs, and why

Besides .NET reference pages, DocFX can render the reference of an HTTP API from its machine-readable description. The official page states that DocFX generates REST API documentation from Swagger 2.0 files, and it names no other format. The idea is the same as for .NET code: the description lives next to the service, is the single source of truth, and the reference page is regenerated on every build instead of being written by hand. Unlike the .NET stage there is no metadata step: a Swagger file is already structured data, so it goes straight into docfx build as content.

Each Swagger file produces one output HTML file. The processor that handles these files is reported in the build log as RestApiDocumentProcessor, and the manifest.json written to the output folder lists the page with the type RestApi.

Adding Swagger files to the build

Include the files in the build.content mapping of docfx.json. The official example uses the pattern **/*.swagger.json. The official page documents only this naming convention, so keep the descriptions under it (see the OpenAPI 3 section below for what happens with other content). The mapping works like any other content mapping; the docfx.json page describes files, src, exclude and dest.

docfx.json
{
  "build": {
    "content": [
      { "files": ["rest/*.swagger.json", "rest/toc.yml", "toc.yml", "index.md"] }
    ],
    "output": "_site",
    "template": ["default", "modern"]
  }
}

The template list is the normal default plus modern combination, which selects the modern look; REST pages are rendered by both templates.

A Swagger 2.0 file for Pocket Units

Pocket Units is the tiny units-conversion library used across this section. To document it as a service, imagine a thin HTTP front end with two endpoints: one converts a length, one lists the supported units, plus a health probe. The file below describes it. The Conversion tag sets x-bookmark-id, the Catalog tag does not, list_units is attached to both tags, and health_check has no tag at all. The host name is a placeholder.

rest/pocket-units.swagger.json
{
  "swagger": "2.0",
  "info": {
    "title": "Pocket Units Service",
    "description": "HTTP front end for the Pocket Units conversion library.",
    "version": "1.0.0"
  },
  "host": "units.example.com",
  "basePath": "/api/v1",
  "schemes": ["https"],
  "consumes": ["application/json"],
  "produces": ["application/json"],
  "tags": [
    { "name": "Conversion", "x-bookmark-id": "conversion", "description": "Convert lengths between units." },
    { "name": "Catalog", "description": "List the supported units." }
  ],
  "paths": {
    "/convert": {
      "get": {
        "operationId": "convert_length",
        "summary": "Convert a length",
        "tags": ["Conversion"],
        "parameters": [
          { "name": "value", "in": "query", "required": true, "type": "number", "description": "Amount to convert." },
          { "name": "from", "in": "query", "required": true, "type": "string", "enum": ["Metre", "Kilometre", "Mile"] },
          { "name": "to", "in": "query", "required": true, "type": "string", "enum": ["Metre", "Kilometre", "Mile"] }
        ],
        "responses": {
          "200": { "description": "Converted value.", "schema": { "$ref": "#/definitions/Conversion" } },
          "400": { "description": "Unknown unit." }
        }
      }
    },
    "/units": {
      "get": {
        "operationId": "list_units",
        "summary": "List units",
        "tags": ["Conversion", "Catalog"],
        "responses": {
          "200": { "description": "Supported units.", "schema": { "type": "array", "items": { "type": "string" } } }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "health_check",
        "summary": "Liveness probe",
        "responses": { "200": { "description": "Service is up." } }
      }
    }
  },
  "definitions": {
    "Conversion": {
      "type": "object",
      "properties": {
        "value": { "type": "number", "description": "Converted amount." },
        "unit": { "type": "string" }
      }
    }
  }
}

Wiring the table of contents

A Swagger file does not appear in the navigation by itself. Add it to a toc.yml as an ordinary item whose href points at the file, and include that toc.yml in the build content (the file mapping above lists rest/toc.yml). The Table of Contents page covers all keys.

rest/toc.yml
- name: Pocket Units Service
  href: pocket-units.swagger.json
toc.yml
- name: Home
  href: index.md
- name: REST API
  href: rest/

If rest/toc.yml is missing from the content mapping, the build still succeeds but warns that it cannot find a toc.yml inside rest/, and the page has no sidebar entry.

Running the build

docfx docfx.json

Output of the run (the first two lines come from the plug-in loader):

7 plug-in(s) loaded.
Building 1 file(s) in RestApiDocumentProcessor(BuildRestApiDocument=>ValidateRestApiDocumentMetadata=>ApplyOverwriteDocumentForRestApi)...
Building 1 file(s) in TocDocumentProcessor(BuildTocDocument)...
Building 1 file(s) in ConceptualDocumentProcessor(BuildConceptualDocument=>ValidateConceptualDocumentMetadata)...
Applying templates to 3 model(s)...
XRef map exported.

Build succeeded.

    0 warning(s)
    0 error(s)

The result is _site/rest/pocket-units.html, next to _site/rest/toc.html and _site/rest/toc.json. The file name comes from the Swagger file name with .swagger.json replaced by .html.

What the generated page contains

For the file above, the rendered article starts with the title from info.title and then shows, in this order, one section per tag (here in the order the tags are declared), then an Other APIs section, then Definitions. Each operation shows its operationId as the heading, its summary, a request line such as GET /convert?value&from&to, a parameter table (required parameters are marked with an asterisk) and a response table with the status code, the type (the $ref target, here Conversion) and the description. The entries under Definitions list the properties of every model with their types and descriptions. The query-parameter names appear in the request line without values.

Organising operations with tags

Operations are organised with Swagger tags. A tag is declared once in the top-level tags array (name and description) and referenced by name in the tags array of an operation. Two rules from the official page, both visible in the run above:

  • An operation can have several tags and then appears once under each of them (list_units shows up under both Conversion and Catalog).

  • An operation without tags goes to the section named Other APIs (the official page spells it "Other apis"; the rendered heading, with the id other-apis, reads Other APIs).

Operations of a Swagger file placed under tag sections of the generated page

The figure shows how the three operations of the example are distributed: the expected layout, which matches the rendered page, is Conversion with convert_length and list_units, Catalog with list_units, and Other APIs with health_check.

The x-bookmark-id extension

The official page says that the vendor extension x-bookmark-id on a tag object "specifies the URL fragment for the tag". It is a Swagger vendor extension, so any other tool reading the file ignores it. In the run above the extension reached DocFX’s model: the raw model exported with --exportRawModel (see Templates and Customization) contains the entry an htmlId property with the value conversion for the Conversion tag and the value Catalog for the tag without the extension, so the tag name is used when no bookmark is given.

However, the headings in the HTML produced by the bundled default and modern templates of 2.81.0 did not use that value. In both builds the tag heading was <h2 id="units_example_com_api_v1_Pocket_Units_Service_1_0_0_tag_Conversion"> and no element had the id conversion. A link such as rest/pocket-units.html#conversion therefore did not reach the tag section in this test. The official documentation does not explain how the fragment is meant to be used, and a custom template can read htmlId from the model, so treat the extension as available data for custom templates rather than as a guaranteed anchor and check the generated HTML before publishing links that depend on it.

Cross-referencing REST pages and operations

Every REST page, tag and operation is registered in _site/xrefmap.yml, so Markdown articles can link to them with the xref syntax described in Links and Cross References. The UIDs are built from the host, the base path, the title and the version of the Swagger file, and the generated anchors replace punctuation with underscores. The entries of the example file, taken from the real map:

### YamlMime:XRefMap
sorted: true
references:
- uid: units.example.com/api/v1/Pocket Units Service/1.0.0
  name: Pocket Units Service
  href: rest/pocket-units.html
- uid: units.example.com/api/v1/Pocket Units Service/1.0.0/convert_length
  name: convert_length
  href: rest/pocket-units.html#units_example_com_api_v1_Pocket_Units_Service_1_0_0_convert_length
- uid: units.example.com/api/v1/Pocket Units Service/1.0.0/tag/Conversion
  name: Conversion
  href: rest/pocket-units.html#units_example_com_api_v1_Pocket_Units_Service_1_0_0_tag_Conversion

The excerpt is real output; the map also holds health_check, list_units and the Catalog tag (the list above is abridged). The UIDs contain spaces whenever the title does, which makes them awkward to write in a link; a title without spaces is easier to reference. This page did not test writing such links in an article. Because the host and version are part of the UID, bumping info.version changes every UID and breaks the links that spelled the old one.

OpenAPI 3 files

Swagger 2.0 is the predecessor of the OpenAPI Specification; version 3 changed the document layout (openapi: 3.x instead of a swagger property with the value 2.0, servers instead of host, basePath and schemes, requestBody and components instead of body parameters and definitions). The official DocFX page does not mention OpenAPI 3, so support for it is outside the official documentation. What was observed with 2.81.0:

  • A file named *.openapi.json matched by the content mapping produced no page, and a toc.yml that pointed to it produced warnings of the form Unable to find file ... for Href referenced by TOC file.

  • An OpenAPI 3 document saved under the name pocket-units.swagger.json is not rendered either. With no TOC pointing at it, the build reports warning: UnknownContentType: Unable to handle following files: rest/pocket-units.swagger.json and the page is not produced.

A practical approach is therefore to keep the Swagger 2.0 description as the file given to DocFX. If the service publishes only OpenAPI 3, a separate step in the pipeline can convert it to Swagger 2.0 before docfx build, or the reference can be generated by a tool that does support OpenAPI 3 (for example the OpenAPI plugin used with Docusaurus, see the Docusaurus section). Conversion between the two versions can lose features that have no Swagger 2.0 counterpart. No converter was run for this page, so the quality of any particular conversion was not tested here.

Pitfalls and version notes

  • Swagger 2.0 only: DocFX 2.81.0 does not build OpenAPI 3 files, and the failure is a skipped file, not a clear error.

  • Name files *.swagger.json and include them (and their toc.yml) in build.content; a pattern that does not match produces no page and no REST-specific message.

  • x-bookmark-id is present in the model but did not change the heading ids of the bundled templates in 2.81.0.

  • Operations without operationId were not tested; give every operation an id, because the id is the heading and part of the cross-reference UID.

  • Writing the service host into the file makes it part of every UID; use a stable placeholder or the production host and keep it unchanged between releases.