Backend Development with .NET
Session 08
API Documentation
& Swagger
Eng. Seif Mansour  ·  Andalusia Academy
Week 4  ·  2.5 hours
Session Goals

By the end of this session, you will be able to:

  • Set up Swagger/OpenAPI in a .NET Web API using Swashbuckle
  • Document endpoints with XML doc comments and [ProducesResponseType] decorators
  • Show versioned API groups as separate tabs in Swagger UI
  • Generate a typed TypeScript client for a React frontend from the OpenAPI spec
Session Agenda
Time Segment Type Duration
0:00Why documentation mattersDiscussion10 min
0:10OpenAPI spec overviewTheory15 min
0:25Swashbuckle setupDemo25 min
0:50XML comments & decoratorsDemo20 min
1:10Break10 min
1:20Versioned Swagger UIDemo20 min
1:40Lab — document the Task APILab35 min
2:15Generating a typed clientDemo15 min
Why Documentation Matters
An API without documentation is a contract nobody can read. Every caller — whether a teammate, a React developer, or your future self — must read the source code to understand how to use it. Documentation is not optional; it is part of the API surface.
In this section
  • The hidden cost of undocumented APIs
  • What API consumers actually need
  • Why Swagger is the industry standard
The Hidden Cost of Undocumented APIs
  • Integration time multiplies — every consumer must read and reverse-engineer source code instead of reading a clear contract
  • Mistakes become invisible — callers guess at optional parameters, omit required fields, and only discover errors at runtime
  • Breaking changes go unnoticed — without a spec, renaming a field or changing a response shape has no visible warning for consumers
  • Testing is harder — without documented request and response schemas, writing accurate tests requires digging through controllers and services
  • Frontend becomes tightly coupled to implementation — React developers end up hard-coding request shapes discovered by trial and error
Rule
If a consumer of your API cannot understand how to use an endpoint without reading the implementation, the API is incomplete — regardless of whether the code works.
What API Consumers Actually Need

Every caller of your API needs to know — without reading source code:

  • The HTTP method and URL for each endpoint
  • Which parameters are required vs optional, and their types
  • The exact shape of the request body, if any
  • Every possible response code and the shape of each response body
  • Authentication requirements for the endpoint
Swagger gives all of this
Swagger UI generates an interactive web page from your API's OpenAPI spec. Consumers can read parameter descriptions, see response schemas, and test endpoints directly in the browser — without touching your code.
Relevant to you
As React developers, you have consumed third-party APIs with good documentation. Swagger gives your own API that same professional, self-service interface.
OpenAPI & Swashbuckle
OpenAPI is the specification. Swagger is the tooling built on top of it. Swashbuckle is the .NET library that generates an OpenAPI document from your controllers at runtime — with no manual file maintenance required.
In this section
  • What OpenAPI is and what it describes
  • The structure of an OpenAPI document
  • Installing and configuring Swashbuckle
  • What Swagger UI gives you in the browser
What is OpenAPI?
  • OpenAPI (formerly Swagger Spec) is a language-agnostic standard for describing REST APIs as a structured JSON or YAML document
  • The spec describes endpoints, HTTP methods, parameters, request bodies, response schemas, and authentication in a machine-readable format
  • Tools consume the spec — Swagger UI renders it as an interactive page; Postman imports it; code generators produce typed clients from it
  • In .NET, Swashbuckle inspects your controllers using reflection and generates the OpenAPI document at runtime — no manual editing required
  • The generated spec is served at /swagger/v1/swagger.json and Swagger UI reads it to build the visual interface
Terminology note
"OpenAPI" is the specification standard. "Swagger" originally referred to the spec itself before it was donated to the OpenAPI Initiative — now "Swagger" refers to the suite of tools (Swagger UI, Swagger Editor, Swashbuckle). In .NET projects you will see both names used interchangeably.
Inside an OpenAPI Document

An OpenAPI document is a JSON file with three key sections:

  • info — title, version, and description of the API
  • paths — every endpoint, its HTTP methods, parameters, request bodies, and all possible responses
  • components — reusable schema definitions (your DTOs) referenced throughout paths

Swashbuckle generates this entire document automatically from your controllers. You only annotate your actions — the serialization is handled for you.

GET /swagger/v1/swagger.json
{
  "openapi": "3.0.1",
  "info": {
    "title": "Task Manager API",
    "version": "v1"
  },
  "paths": {
    "/api/tasks/{id}": {
      "get": {
        "summary": "Returns a single task by ID.",
        "parameters": [
          { "name": "id", "in": "path", "required": true }
        ],
        "responses": {
          "200": { "description": "Task found." },
          "404": { "description": "Task not found." }
        }
      }
    }
  }
}
Installing Swashbuckle

One NuGet package brings in everything needed:

dotnet add package Swashbuckle.AspNetCore

This installs three sub-packages:

  • Swashbuckle.AspNetCore.Swagger — serializes the spec to JSON
  • Swashbuckle.AspNetCore.SwaggerGen — reflects over controllers to build the spec
  • Swashbuckle.AspNetCore.SwaggerUI — serves the interactive web interface

Enable XML documentation output so comments appear in the spec:

TaskManagerApi.csproj
<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
  <NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
Warning 1591
This warning fires on every public member that has no XML comment. Suppressing it lets you add documentation incrementally without the build flooding with warnings on undocumented members.
Registering Swagger Services
Program.cs
using System.Reflection;
using Microsoft.OpenApi.Models;

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo
    {
        Title       = "Task Manager API",
        Version     = "v1",
        Description = "RESTful API for managing tasks"
    });

    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
});

IncludeXmlComments wires the XML documentation file into Swashbuckle so your /// <summary> and /// <response> tags appear in Swagger UI.

Wiring Swagger Middleware

Two middleware calls activate Swagger — both gated to development only:

Program.cs
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint(
            "/swagger/v1/swagger.json",
            "Task Manager API v1");
    });
}

What this gives you in development:

  • /swagger/v1/swagger.json — the raw OpenAPI document
  • /swagger — the Swagger UI web page
  • Every controller action listed with its method, path, and description
  • Interactive "Try it out" to call the API directly from the browser
  • Request and response schema panels generated from your DTOs
Production
Never expose Swagger UI in production. The IsDevelopment() guard ensures it only runs locally. Leaking it in production exposes your full API surface to anyone who finds the URL.
XML Comments & Response Decorators
Swashbuckle infers the request shape from your models automatically. The parts it cannot infer — human-readable descriptions and which response codes each action can produce — must be supplied with XML doc comments and ProducesResponseType attributes.
In this section
  • XML summary, param, and response tags
  • ProducesResponseType — linking codes to DTO types
  • Documenting a GET endpoint
  • Documenting a POST endpoint with multiple response codes
XML Documentation Comments

Three XML tags matter for Swagger:

  • <summary> — one-line description shown as the endpoint title in Swagger UI
  • <param name="x"> — description of a parameter, shown in the Parameters panel
  • <response code="N"> — human-readable description of a specific HTTP response code
Triple-slash
XML doc comments begin with ///. Visual Studio and Rider auto-generate the tag scaffolding when you type /// directly above a method signature.
/// <summary>Returns a single task by ID.</summary>
/// <param name="id">The task identifier.</param>
/// <response code="200">Task found and returned.</response>
/// <response code="404">No task with this ID exists.</response>
[HttpGet("{id}")]
public IActionResult GetById(int id)
    => Ok(_taskService.GetById(id));

Without the XML comments, Swagger UI shows only the method and path. With them, it shows a description, parameter labels, and a response code table with meanings.

ProducesResponseType — Linking Codes to Schemas

[ProducesResponseType] tells Swashbuckle which DTO type is returned for each response code. Without it, Swagger only knows the code exists — not the shape of the body.

  • First argument: the DTO type that populates the response schema panel
  • Second argument: the StatusCodes constant for the HTTP code
  • Use ProblemDetails for error responses — it matches the RFC 7807 shape your API already returns from Session 05
/// <summary>Returns a single task by ID.</summary>
/// <param name="id">The task identifier.</param>
/// <response code="200">Task found and returned.</response>
/// <response code="404">No task with this ID exists.</response>
[HttpGet("{id}")]
[ProducesResponseType(typeof(TaskItemDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public IActionResult GetById(int id)
    => Ok(_taskService.GetById(id));
Result
Swagger UI now renders the TaskItemDto schema in the 200 panel and the ProblemDetails schema in the 404 panel — consumers see the exact shape of both outcomes.
Documenting a POST Endpoint
Controllers/TasksController.cs
/// <summary>Creates a new task.</summary>
/// <param name="dto">The task data.</param>
/// <response code="201">Task created successfully.</response>
/// <response code="400">Validation failed — see errors in the response body.</response>
/// <response code="409">A task with this title already exists.</response>
[HttpPost]
[ProducesResponseType(typeof(TaskItemDto), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
public IActionResult Create([FromBody] CreateTaskDto dto)
{
    var created = _taskService.Create(dto);
    return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
}

Use ValidationProblemDetails for 400 responses — it has the same shape as the validation errors ASP.NET Core returns automatically, so the schema in Swagger UI will be accurate.

Versioned Swagger UI & Typed Clients
When an API has multiple versions, Swagger UI can show each version as its own tab with its own spec. After the session, a TypeScript client generated from that spec closes the loop back to your React frontend — turning your documented API into a typed library.
In this section
  • Registering multiple spec documents in SwaggerGen
  • Grouping controller versions with ApiExplorerSettings
  • Generating a TypeScript client with NSwag
  • Using the generated client in React
Registering Multiple API Docs
Program.cs
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Task Manager API", Version = "v1" });
    c.SwaggerDoc("v2", new OpenApiInfo { Title = "Task Manager API", Version = "v2" });

    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
});

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
    c.SwaggerEndpoint("/swagger/v2/swagger.json", "v2");
});

Each SwaggerDoc call creates a separate tab in Swagger UI. The matching SwaggerEndpoint calls tell the UI where to load each spec. Both the name passed to SwaggerDoc and the JSON path must use the same version string.

Grouping Endpoints by Version

Swashbuckle decides which spec document an action belongs to by reading its ApiExplorerSettings.GroupName. The value must match the document name passed to SwaggerDoc.

  • Actions with no GroupName appear in all specs
  • Setting GroupName = "v2" restricts the action to the v2 tab only
  • This works alongside the API versioning from Session 06 — the route template, [ApiVersion], and GroupName all use the same version identifier
Controllers/TasksV2Controller.cs
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/tasks")]
[ApiExplorerSettings(GroupName = "v2")]
public class TasksV2Controller : ControllerBase
{
    // Actions here appear only in the v2 tab
}
Connects to Session 06
The AddApiVersioning setup and route templates from Session 06 are the prerequisite. GroupName is a Swagger-only hint — it does not change how the router dispatches requests.
Generating a Typed TypeScript Client

NSwag reads your OpenAPI spec and generates a fully typed TypeScript client. Every endpoint becomes a method; every DTO becomes an interface.

dotnet tool install --global NSwag.ConsoleCore

nswag openapi2tsclient \
  /input:swagger.json \
  /output:src/api/taskApiClient.ts

Download swagger.json from /swagger/v1/swagger.json while the API is running locally, then run NSwag against that file.

src/components/TaskList.tsx
import { TasksClient } from '../api/taskApiClient';

const client = new TasksClient("https://localhost:5001");

// Autocomplete works for all parameters and return types
const result = await client.getAll(
    1,         // page
    20,        // pageSize
    "meeting"  // search
);

console.log(result.data);        // TaskItemDto[]
console.log(result.totalCount);  // number
The payoff
Rename a field in the API response, re-generate the client, and TypeScript immediately flags every call site in React that accessed the old name. The documentation and the type system enforce the contract together.
Lab — Document the Task API

Add full Swagger documentation to the Task API built in previous sessions. Every action must have XML comments and response type decorators.

1 Install Swashbuckle.AspNetCore and add GenerateDocumentationFile to the .csproj
2 Register AddEndpointsApiExplorer, AddSwaggerGen with OpenApiInfo and IncludeXmlComments in Program.cs
3 Wire UseSwagger and UseSwaggerUI inside the IsDevelopment() guard
4 Add /// <summary>, /// <param>, and /// <response> XML comments to every controller action
5 Add [ProducesResponseType] for every possible response code, linking each to the correct DTO or ProblemDetails type
6 Navigate to /swagger and verify each endpoint shows its description, parameter labels, and response schemas
7 Use "Try it out" to call GET /api/tasks?page=1&pageSize=5 and confirm the paginated response matches the displayed schema
Key Principle
"Documentation is not separate from the API —
it is part of the API contract."
XML comments, ProducesResponseType attributes, and the OpenAPI spec are not optional extras. They are the machine-readable contract that every consumer of your API depends on — the same discipline that makes breaking changes visible before they ship.
Summary
  • OpenAPI is the standard — a language-agnostic, machine-readable description of your API's endpoints, parameters, and response shapes
  • Swashbuckle generates the spec automatically — it reflects over your controllers at runtime; no manual file maintenance required
  • XML doc comments supply what Swashbuckle cannot infer: human-readable summaries, parameter labels, and response code meanings
  • ProducesResponseType links response codes to DTO types — Swagger UI uses this to render accurate schema panels for every possible outcome
  • Multiple API versions appear as separate tabs — each SwaggerDoc call creates a named spec; ApiExplorerSettings routes actions into the correct one
  • NSwag closes the loop to React — a typed TypeScript client generated from the spec enforces the API contract at compile time in the frontend
What's Next

Session 09 — Entity Framework Core Intro

  • Sessions 04–08 built a fully shaped, versioned, and documented HTTP API layer — but the data lives in in-memory lists. Session 09 introduces Entity Framework Core to persist it to a real database
  • You will install EF Core, configure a DbContext, define entity models, and issue your first migration to generate a database schema from C# code
  • The CRUD endpoints you have already built will be wired to real DbSet queries — replacing the in-memory collections entirely
  • After Session 09, the Task API will read and write from a persistent SQL database, and the Swagger UI you built today will be the primary tool for testing every EF Core operation live
Before next session
Complete the assignment. Make sure Swagger UI is running and all endpoints are fully documented before Session 09 — you will use "Try it out" to verify each database operation as you wire it up.
Assignment

Add complete Swagger/OpenAPI documentation to the Task Manager API.

What to build:

  • Install Swashbuckle, enable XML documentation output, and register AddSwaggerGen and the Swagger middleware in Program.cs
  • Add /// <summary>, /// <param>, and /// <response> XML doc comments to every controller action
  • Add [ProducesResponseType] decorators for all possible response codes, linking each to the correct DTO or ProblemDetails type

Acceptance criteria:

  • Swagger UI is accessible at /swagger in development and lists all Task API endpoints
  • Every endpoint has a human-readable summary and all parameters are labeled with descriptions
  • Every possible response code has a schema panel showing the DTO or error body shape
  • The raw spec at /swagger/v1/swagger.json is valid JSON with no missing schema references
Bonus
Add JWT Bearer authentication to Swagger UI using AddSecurityDefinition and AddSecurityRequirement so the "Authorize" button appears and bearer tokens can be passed to protected endpoints directly from the UI. You will use this in Session 14 when JWT auth is implemented.
Questions?
Session 08 — API Documentation & Swagger