aspnet-core-swagger-openapi-documentacion

Documenting an ASP.NET Core API with OpenAPI and Swagger

  • 5 min

OpenAPI is a standard for describing an HTTP API in a structured, tool-readable way.

There’s nothing more frustrating for a frontend or mobile developer than trying to consume an API “blindly.” “Which URL do I call? What JSON do I need to send? What does it return if it fails?”

In the past, the solution was to write an unbearable Word document or PDF that, let’s be honest, became outdated the moment you changed a line of code.

In modern development, documentation must be alive. It should be generated automatically from your code.

That’s where the OpenAPI standard and tools like Swagger UI or Scalar come in.

Today, we’ll see how to make your API explain itself.

OpenAPI vs Swagger: Are they the same?

It’s common to use the terms interchangeably, but there’s a subtle difference:

  1. OpenAPI: is the standard. It’s a JSON or YAML file that describes your API technically: “I have a GET endpoint at /products that returns an array of objects…”. It’s the contract.
  2. Swagger: is a family of tools that work with that standard.
    • Swagger UI: the interactive web page that displays the documentation.
    • Swashbuckle: a widely used library in ASP.NET Core for generating OpenAPI documents and serving Swagger UI.

In modern ASP.NET Core projects, it’s best to separate the ideas: OpenAPI is the document; Swagger UI is just one way to view and test it.

Configuration in ASP.NET Core

The good news is that modern ASP.NET Core includes built-in support for generating OpenAPI documents.

In a modern project, we can register the native generator in Program.cs:

var builder = WebApplication.CreateBuilder(args);

// 1. Add services to generate the OpenAPI document
builder.Services.AddOpenApi();

var app = builder.Build();

// 2. Expose the OpenAPI document
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.Run();
Copied!

With just this, if you start the app and go to /openapi/v1.json, you’ll have your API contract. That’s already useful for generating clients, sharing documentation, or feeding other tools.

What it doesn’t include out-of-the-box is a visual interface. For that, you can add Swagger UI, Scalar, ReDoc, or another OpenAPI-compatible tool.

For example, with Swagger UI:

dotnet add package Swashbuckle.AspNetCore.SwaggerUI
Copied!

And in Program.cs:

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();

    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/openapi/v1.json", "v1");
    });
}
Copied!

Now, when you go to /swagger, you’ll see an interactive list of your endpoints. But… it’s “poor” documentation. Let’s enrich it.

Enriching the Documentation

By default, the generator only knows what it can infer from your routes, parameters, models, and metadata. But it doesn’t always know what your method does or what each parameter means.

To tell it, we use C# XML Comments (///).

Enabling XML Documentation

First, we need to tell the compiler not to discard those comments but to save them in an XML file.

Open your .csproj file and add this inside <PropertyGroup>:

<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
Copied!

Connecting XML with OpenAPI

In ASP.NET Core 10, the native generator incorporates XML comments into the OpenAPI document when the project is configured to generate them.

If you’re using Swashbuckle as the generator instead of AddOpenApi(), install the full Swashbuckle.AspNetCore package and configure AddSwaggerGen():

using System.Reflection;

builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "My Store API",
        Version = "v1",
        Description = "API for managing products and orders."
    });

    // Look for the generated XML file
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);

    // Tell Swashbuckle to use it
    options.IncludeXmlComments(xmlPath);
});
Copied!

Writing Comments

Now, go to your Controller and add summary, param, and returns comments.

/// <summary>
/// Gets a specific product by its ID.
/// </summary>
/// <param name="id">The unique identifier of the product.</param>
/// <returns>The product object if it exists.</returns>
[HttpGet("{id}")]
public IActionResult GetProduct(int id) { ... }
Copied!

When you reload the UI, you’ll see those descriptions next to each endpoint. Now your API speaks human.

Describing Responses with ProducesResponseType

If your method returns IActionResult, the generator might not know what data type you’re returning or what possible error codes exist. By default, it might end up showing overly vague documentation.

We can use the ProducesResponseType attribute to precisely describe these responses.

/// <summary>
/// Creates a new user in the system.
/// </summary>
/// <response code="201">User created successfully.</response>
/// <response code="400">Invalid or incomplete data.</response>
[HttpPost]
[ProducesResponseType(typeof(UserDto), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public IActionResult CreateUser(CreateUserDto dto)
{
    // ...
    return CreatedAtAction(..., user);
}
Copied!

Now the UI will clearly show: “this endpoint can return a 201 with this JSON or a 400”.

Testing the API from the Browser

The best part of an interactive UI like Swagger UI or Scalar is being able to test the API from the browser.

It turns the documentation into an HTTP client (like Postman) integrated into the browser.

  1. Click “Try it out”.
  2. Fill in the form fields.
  3. Click “Execute”.
  4. See the actual response from your server.

This allows the Frontend team to test endpoints even before writing a single line of code in their application.

Authentication in Swagger UI

If your API is protected with JWT (we’ll cover this in the next section), the “Try it out” button will fail because it’s missing the Token.

If you’re using Swashbuckle, you can configure Swagger UI to have an “Authorize” button:

// Advanced configuration to support JWT with Swashbuckle
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
    In = ParameterLocation.Header,
    Description = "Insert the JWT token like this: Bearer {your_token}",
    Name = "Authorization",
    Type = SecuritySchemeType.ApiKey
});

options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
    {
        new OpenApiSecurityScheme
        {
            Reference = new OpenApiReference
            {
                Type = ReferenceType.SecurityScheme,
                Id = "Bearer"
            }
        },
        new string[] { }
    }
});
Copied!