Search Results for

    ADR 0012: OpenAPI Spec via Microsoft.AspNetCore.OpenApi; Swagger UI via Swashbuckle

    Status

    Accepted

    Context and Problem Statement

    The API needs machine-readable documentation in two forms: a spec document for tooling, CI drift checks, and the documentation site; and an interactive browser UI for exploratory testing during local development.

    In .NET 9/10, Microsoft.AspNetCore.OpenApi became a first-party built-in that generates a spec document from the application's endpoint metadata at runtime. Swashbuckle has historically handled both spec generation and UI in a single package. The question is which library, or combination, to use.

    Decision Drivers

    • The spec document must be generated from the running application so that it reflects the actual endpoint configuration, not a manually maintained file.
    • Enum values in the schema must appear as strings, consistent with the runtime JsonStringEnumConverter setting (ADR 0003).
    • The interactive UI must only be available in the Development environment. It must not be accessible in production.
    • The committed OpenAPI snapshot (ADR 0013) is generated by starting the API in Development mode and fetching the spec endpoint — the spec endpoint must therefore be reachable in Development.

    Considered Options

    Option A — Microsoft.AspNetCore.OpenApi for spec + Swashbuckle for UI (Selected)

    Microsoft.AspNetCore.OpenApi generates the spec at /openapi/v1.json (Development only). A custom StringEnumSchemaTransformer corrects enum schemas to emit string values. Swashbuckle provides the Swagger UI at /swagger (Development only), consuming its own spec endpoint at /swagger/v1/swagger.json. The two libraries coexist: one for the machine-readable artifact, one for the developer UI.

    Option B — Swashbuckle only

    A single library handles both spec generation and UI. Less configuration, but Swashbuckle's spec generation is not the direction .NET 9/10 is moving. The first-party Microsoft.AspNetCore.OpenApi is the recommended approach for new projects on these runtimes, and Swashbuckle compatibility with .NET 10 Minimal API features is a trailing concern.

    Option C — NSwag

    An alternative that handles both spec generation and UI, with additional code-generation tooling. Heavier than either Swashbuckle or the built-in option for a project that does not need client code generation.

    Decision Outcome

    Chosen option: Option A — Microsoft.AspNetCore.OpenApi for spec + Swashbuckle for UI.

    Why this trade-off makes sense for this project

    • Microsoft.AspNetCore.OpenApi is the .NET 10 native path. It integrates directly with Minimal API endpoint metadata and is maintained by the ASP.NET Core team. Using it for spec generation aligns with the direction of the platform.
    • Swashbuckle fills the UI gap. The built-in library does not ship an interactive UI. Swashbuckle's UI is well-known and adds no meaningful runtime cost since it is gated to Development only.
    • StringEnumSchemaTransformer keeps the schema consistent with the serialiser. Without it, the generated spec would describe enum properties as integers — contradicting the runtime behaviour where JsonStringEnumConverter serialises them as strings. The transformer is a file-scoped class in OpenApi.cs (not a public type) because it is an implementation detail of the composition root.
    • Both endpoints are Development-only. MapOpenApi() and UseSwaggerUI() are both gated by IsDevelopment. The spec endpoint at /openapi/v1.json is reachable in CI because the snapshot script sets DOTNET_ENVIRONMENT=Development. In production, no spec or UI surface is exposed.

    Setup overview

    Development                   Production
    ───────────────────────────   ───────────────
    GET /openapi/v1.json          (not mapped)
      └─ Microsoft.AspNetCore.OpenApi
         └─ StringEnumSchemaTransformer
    
    GET /swagger                  (not mapped)
      └─ Swashbuckle UI
         └─ reads /swagger/v1/swagger.json
    

    Consequences

    Positive Consequences

    • The spec is generated from live endpoint metadata — it cannot drift from the actual API surface without the CI drift check detecting it (ADR 0013).
    • Enum schemas match the runtime serialiser behaviour exactly.
    • The production surface is zero: no spec endpoint, no UI, no additional attack surface.

    Negative Consequences

    • Two libraries serve overlapping purposes (each can generate a spec). This is intentional but can cause confusion: /openapi/v1.json and /swagger/v1/swagger.json are different endpoints backed by different generators. The committed snapshot uses /openapi/v1.json (the built-in).
    • StringEnumSchemaTransformer must be kept in sync with any enum-related serialisation policy changes. If JsonStringEnumConverter is removed or scoped, the transformer becomes incorrect.

    Re-evaluation Triggers

    Revisit when:

    1. Microsoft.AspNetCore.OpenApi ships a built-in Swagger UI integration — the Swashbuckle dependency can then be dropped.
    2. Client code generation is introduced — NSwag or the OpenAPI Generator may be preferred over Swashbuckle at that point.

    Related

    • ADR 0003 – RFC 9457 ProblemDetails (JsonStringEnumConverter context)
    • ADR 0013 – OpenAPI Snapshot as a Committed Artifact with CI Drift Check
    • src/ServiceDeskLite.Api/Composition/OpenApi.cs
    • src/ServiceDeskLite.Api/Program.cs — AddSwaggerGen, UseSwaggerUI
    • Edit this page
    In this article
    Back to top Generated by DocFX