- Published on
Modern API Documentation in .NET with Scalar and OpenAPI
Introduction
.NET 9.0 introduces significant improvements to API documentation with first-class support for OpenAPI. This post will guide you through creating beautiful, interactive API documentation that developers will love to use.
Understanding OpenAPI and Scalar
What is Scalar?
Scalar is a modern API documentation tool that replaces Swashbuckle. It provides:
- 🎨 Modern, responsive UI
- 🚀 Better performance
- 📱 Mobile-friendly interface
- 🔍 Enhanced search capabilities
- 🎭 Dark/light theme support
you can find more info about Scalar here.
Why Choose OpenAPI with Scalar?
- Developer Experience: Interactive documentation with try-it-now functionality
- Code Generation: Automatic client SDK generation
- Testing: Built-in request builder and mock servers
- Standards: Industry-standard API specification format
Implementation Guide
1. Project Setup
Create a new .NET 9.0 project:
dotnet new web -n ModernApiDocs
cd ModernApiDocs
Install required packages:
dotnet add package Microsoft.AspNetCore.OpenApi
dotnet add package Scalar.AspNetCore
2. Basic Configuration
Add this to your Program.cs
:
using Scalar.AspNetCore;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Configure OpenAPI
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, _) =>
{
document.Info = new()
{
Title = "Product Catalog API",
Version = "v1",
Description = """
Modern API for managing product catalogs.
Supports JSON and XML responses.
Rate limited to 1000 requests per hour.
""",
Contact = new()
{
Name = "API Support",
Email = "api@example.com",
Url = new Uri("https://api.example.com/support")
}
};
return Task.CompletedTask;
});
});
var app = builder.Build();
// Enable OpenAPI and Scalar
app.MapOpenApi().CacheOutput();
app.MapScalarApiReference();
// Redirect root to Scalar UI
app.MapGet("/", () => Results.Redirect("/scalar/v1"))
.ExcludeFromDescription();
app.Run();
3. Sample API Endpoints
Here's a practical example with a product catalog API:
// Product model
public record Product(int Id, string Name, decimal Price);
app.MapGet("/products", (int? pageSize, int? page) =>
{
var products = GetProducts(pageSize ?? 10, page ?? 1);
return Results.Ok(products);
})
.Produces<List<Product>>(200)
.Produces(400)
.WithName("GetProducts")
.WithTags("Products")
.WithSummary("Retrieve a list of products")
.WithDescription("""
Returns a paginated list of products.
Default page size is 10.
Use page parameter for pagination.
""");
app.MapPost("/products", (Product product) =>
{
// Add product logic here
return Results.Created($"/products/{product.Id}", product);
})
.Produces<Product>(201)
.Produces(400)
.WithName("CreateProduct")
.WithTags("Products")
.WithSummary("Create a new product")
.WithDescription("Add a new product to the catalog.");
Advanced Features
Custom Response Examples
app.MapGet("/products/{id}", (int id) =>
{
var product = GetProduct(id);
return product is null ? Results.NotFound() : Results.Ok(product);
})
.Produces<Product>(200)
.Produces(404)
.WithOpenApi(operation => {
operation.Responses["200"].Content["application/json"].Example =
new Product(1, "Sample Product", 29.99m);
return operation;
});
Authentication Configuration
For JWT auth with OpenAPI, you need the JWT bearer package:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Update your Program.cs
:
builder
.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = builder.Configuration["Jwt:Authority"];
options.Audience = builder.Configuration["Jwt:Audience"];
options.RequireHttpsMetadata = true;
options.TokenValidationParameters =
new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["Jwt:Authority"],
};
});
builder.Services.AddOpenApi(opt =>
{
opt.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
// Placeholder for missing code snippets
// Add authentication middleware
app.UseAuthentication();
app.UseAuthorization();
internal sealed class BearerSecuritySchemeTransformer(
IAuthenticationSchemeProvider authenticationSchemeProvider
) : IOpenApiDocumentTransformer
{
public async Task TransformAsync(
OpenApiDocument document,
OpenApiDocumentTransformerContext context,
CancellationToken cancellationToken
)
{
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
var requirements = new Dictionary<string, OpenApiSecurityScheme>
{
["Bearer"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer", // "bearer" refers to the header name here
In = ParameterLocation.Header,
BearerFormat = "Json Web Token",
},
};
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes = requirements;
}
}
}
Best Practices
Documentation Organization
- Group related endpoints using tags
- Provide clear summaries and descriptions
- Include request/response examples
Security
- Document authentication requirements
- Specify rate limits
- Include error responses
Performance
- Enable response caching
- Use appropriate HTTP methods
- Document pagination
Testing Your API Documentation
- Run your application:
dotnet run
- Access the documentation:
- Scalar UI:
http://localhost:5175/scalar/v1
- Raw OpenAPI JSON:
http://localhost:5175/openapi/v1.json
- Scalar UI:
Sample GitHub Repository
You can find the complete source code for this guide in the ModernApiDocs repository.
Conclusion
Scalar with OpenAPI provides a modern, developer-friendly way to document your .NET APIs. By following these practices, you'll create documentation that helps developers understand and integrate with your API efficiently.
Resources
Related Posts
Building an AI-Powered .NET API with Ollama and Microsoft.Extensions.AI
Learn how to create an intelligent .NET API using Ollama, Microsoft.Extensions.AI, and implement function calling capabilities with Large Language Models.
Ollama Semantic Kernel Connector With C#: Console App & API Guide
In this post, we will learn how to use the Ollama Semantic Kernel Connector with C#.
ABP-Powered Web App with Inertia.js, React, and Vite
Building a web application with ABP Framework, Inertia.js, React, and Vite.