Isaac.

API Documentation with Swagger

Generate interactive API documentation with Swagger/OpenAPI.

By EMEPublished: February 20, 2025
swaggeropenapiapi documentationaspnet core

A Simple Analogy

Swagger is like a living user manual for your API. It documents endpoints, request/response formats, and you can test the API right from the documentation.


Why Swagger?

  • Self-documenting: Generate docs from code
  • Interactive: Test endpoints from browser
  • Standardized: OpenAPI specification
  • Client generation: Auto-generate SDKs
  • Contract: API contract for frontend/backend

Setup

// Program.cs
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        options.RoutePrefix = string.Empty;  // Show at root
    });
}

app.Run();

Endpoint Documentation

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    /// <summary>
    /// Get order by ID
    /// </summary>
    /// <param name="id">Order ID</param>
    /// <returns>Order details</returns>
    [HttpGet("{id}")]
    [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OrderDto))]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<OrderDto>> GetOrder(int id)
    {
        var order = await _orderService.GetAsync(id);
        if (order == null) return NotFound();
        return Ok(order);
    }

    /// <summary>
    /// Create new order
    /// </summary>
    /// <remarks>
    /// Sample request:
    /// 
    ///     POST /api/orders
    ///     {
    ///         "customerId": "cust-123",
    ///         "items": [
    ///             {
    ///                 "productId": "p1",
    ///                 "quantity": 2,
    ///                 "price": 29.99
    ///             }
    ///         ]
    ///     }
    /// </remarks>
    [HttpPost]
    [ProducesResponseType(StatusCodes.Status201Created, Type = typeof(OrderDto))]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<OrderDto>> CreateOrder(CreateOrderRequest request)
    {
        if (!ModelState.IsValid) return BadRequest(ModelState);
        
        var order = await _orderService.CreateAsync(request);
        return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
    }
}

Model Documentation

public class OrderDto
{
    /// <summary>
    /// Unique order identifier
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Customer ID who placed the order
    /// </summary>
    [Required]
    public string CustomerId { get; set; }

    /// <summary>
    /// Order items
    /// </summary>
    [MinLength(1)]
    public List<OrderItemDto> Items { get; set; }

    /// <summary>
    /// Total order amount in USD
    /// </summary>
    [Range(0.01, double.MaxValue)]
    public decimal Total { get; set; }

    /// <summary>
    /// Order status (Pending, Confirmed, Shipped, Delivered, Cancelled)
    /// </summary>
    public OrderStatus Status { get; set; }
}

Advanced Configuration

builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "Order API",
        Version = "v1",
        Description = "API for managing orders",
        Contact = new OpenApiContact
        {
            Name = "Support",
            Email = "support@example.com"
        }
    });

    // Include XML comments
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    options.IncludeXmlComments(xmlPath);

    // Add security definition
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });

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

Custom Swagger UI

app.UseSwaggerUI(options =>
{
    options.DocumentTitle = "Order API Documentation";
    options.DefaultModelsExpandDepth(0);  // Don't expand schemas by default
    options.DefaultModelExpandDepth(2);   // But expand specific ones
    
    // Custom CSS
    options.InjectStylesheet("/swagger-ui.css");
    
    // Multiple API versions
    options.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");
    options.SwaggerEndpoint("/swagger/v2/swagger.json", "API V2");
});

Exclude from Documentation

[ApiExplorerSettings(IgnoreApi = true)]
[HttpPost("internal/process")]
public async Task<IActionResult> InternalProcess()
{
    // Not shown in Swagger
}

Best Practices

  1. Document thoroughly: Include examples
  2. Use response types: Specify all outcomes
  3. Validate inputs: Annotate constraints
  4. Version APIs: Support multiple versions
  5. Keep updated: Docs should match code

Related Concepts

  • API versioning strategies
  • OpenAPI code generation
  • API gateway documentation
  • GraphQL schema

Summary

Swagger transforms code documentation into interactive API references. Use XML comments and response type annotations to auto-generate comprehensive, testable documentation.