| name | dck-openapi |
| description | Use when changing AHKFlowApp OpenAPI, Swagger, API docs, response annotations, XML comments, or security schemes. |
OpenAPI
Core Principles
- Swagger UI for API documentation — AHKFlowApp uses
Swashbuckle.AspNetCore (AddSwaggerGen + UseSwagger + UseSwaggerUI). Available in development at /swagger.
[ProducesResponseType] on every controller action — Explicit annotations drive the OpenAPI schema. Don't rely on inference alone.
- XML documentation comments — Enable
<GenerateDocumentationFile>true</GenerateDocumentationFile> in the API project for summary/description in Swagger UI.
- Controller-based metadata —
[ApiController], [Route], [HttpGet], [ProducesResponseType] are the primary metadata sources. No .WithName() or .WithSummary() (those are Minimal API patterns).
Patterns
Basic Setup (Program.cs)
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "AHKFlowApp API",
Version = "v1",
Description = "API for managing AutoHotkey hotstrings and hotkeys."
});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "AHKFlowApp API v1");
options.RoutePrefix = "swagger";
});
}
ProducesResponseType on Controller Actions
Every action must declare all possible response types.
[ApiController]
[Route("api/v1/[controller]")]
public sealed class HotstringsController(
IUseCase<CreateHotstringCommand, Result<HotstringDto>> createHotstring,
IUseCase<GetHotstringQuery, Result<HotstringDto>> getHotstring,
IUseCase<ListHotstringsQuery, Result<IReadOnlyList<HotstringDto>>> listHotstrings)
: ControllerBase
{
[HttpPost]
[ProducesResponseType<HotstringDto>(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<IActionResult> Create(CreateHotstringDto dto, CancellationToken ct)
{
var result = await createHotstring.ExecuteAsync(new CreateHotstringCommand(dto.Trigger, dto.Replacement), ct);
return result.ToActionResult(this);
}
[HttpGet("{id:int}")]
[ProducesResponseType<HotstringDto>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetById(int id, CancellationToken ct)
{
var result = await getHotstring.ExecuteAsync(new GetHotstringQuery(id), ct);
return result.ToActionResult(this);
}
[HttpGet]
[ProducesResponseType<IReadOnlyList<HotstringDto>>(StatusCodes.Status200OK)]
public async Task<IActionResult> List(CancellationToken ct)
{
var result = await listHotstrings.ExecuteAsync(new ListHotstringsQuery(), ct);
return result.ToActionResult(this);
}
}
Enable XML Documentation
In AHKFlowApp.API.csproj:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
Bearer Token Security Scheme
For Azure AD (MSAL) authentication in Swagger UI:
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "Enter JWT token"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
[]
}
});
});
ProblemDetails Schema
Register AddProblemDetails() to ensure ProblemDetails appears correctly in the OpenAPI schema:
builder.Services.AddProblemDetails();
Anti-patterns
Missing ProducesResponseType
[HttpGet("{id:int}")]
public async Task<IActionResult> GetById(int id, CancellationToken ct) { ... }
[HttpGet("{id:int}")]
[ProducesResponseType<HotstringDto>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetById(int id, CancellationToken ct) { ... }
Minimal API OpenAPI Patterns in Controllers
[HttpGet]
public IActionResult List()
{
}
[HttpGet]
[ProducesResponseType<IReadOnlyList<HotstringDto>>(StatusCodes.Status200OK)]
public async Task<IActionResult> List(CancellationToken ct) { ... }
Exposing Swagger in Production
app.UseSwagger();
app.UseSwaggerUI();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
Decision Guide
| Scenario | Recommendation |
|---|
| API documentation UI | Swagger UI at /swagger (development only) |
| Response documentation | [ProducesResponseType<T>(statusCode)] on every action |
| Method/param descriptions | XML doc comments (<summary>, <param>, <response>) |
| Security scheme in docs | AddSecurityDefinition + AddSecurityRequirement in AddSwaggerGen |
| ProblemDetails schema | builder.Services.AddProblemDetails() |
| Generate OpenAPI spec at build | Microsoft.Extensions.ApiDescription.Server package |