.NET 9 minimal APIs in practice — Patterns & Anti‑Patterns — Practical Guide (May 2, 2026)
.NET 9 minimal APIs in practice — Patterns & Anti‑Patterns
Level: Intermediate
As of May 2, 2026, targeting .NET 9 (officially released November 2025).
Introduction
Minimal APIs introduced in .NET 6 revolutionised how developers build lightweight HTTP APIs with less ceremony and fewer files. By .NET 9, these APIs are well-established for both prototypes and production-grade services. This article explores practical patterns and anti-patterns to help intermediate developers harness minimal APIs effectively, balancing simplicity with maintainability, performance, and scale.
Prerequisites
- Familiarity with C# 11 or newer features (targeting .NET 9 runtime).
- Basic understanding of ASP.NET Core web applications.
- Visual Studio 2022+ or VS Code with C# extension.
- .NET 9 SDK installed (from official Microsoft sources).
Hands-on steps
Basic Minimal API Setup
The core structure uses the WebApplication builder designed to define routes directly and concisely.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/hello", () => "Hello from .NET 9 minimal API!");
app.Run();
This trivial example establishes an HTTP GET endpoint at /hello.
Pattern: Use Extension Methods for Route Grouping and Organisation
One common pattern is to organise routes via extension methods to avoid cluttering Program.cs. This approach scales well without losing the simplicity minimal APIs aim for:
public static class ProductApi
{
public static void RegisterProductEndpoints(this WebApplication app)
{
var products = new List { "apple", "banana", "cherry" };
app.MapGet("/products", () => products);
app.MapGet("/products/{id:int}", (int id) =>
{
if (id = products.Count) return Results.NotFound();
return Results.Ok(products[id]);
});
}
}
// In Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.RegisterProductEndpoints();
app.Run();
This pattern enables modular endpoint registration across files, enhancing maintainability.
Pattern: Use Route Groups and Filters for Cross-Cutting Concerns
.NET 9 improves minimal APIs by allowing route groups, which can share metadata and filters such as authorization or validation:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var adminGroup = app.MapGroup("/admin")
.RequireAuthorization()
.Add(endpoint =>
{
endpoint.WithTags("Admin");
});
adminGroup.MapGet("/stats", () => "Sensitive stats");
app.Run();
This approach clusters related routes and applies shared middleware concisely. Use this pattern when endpoints share policies or metadata.
Anti-Pattern: Putting All Logic Inline in Program.cs
Although minimal APIs encourage brevity, stuffing complex business logic directly inside route handlers harms testability and clarity. Avoid writing more than a few statements inline:
// Anti-pattern: Large logic inside endpoint
app.MapPost("/order", async (HttpRequest req) =>
{
var order = await req.ReadFromJsonAsync();
// Extensive validation and processing done here — difficult to test
if (order == null || order.Items.Count == 0) return Results.BadRequest("Invalid order");
// More business logic...
return Results.Ok();
});
Instead, delegate to services and keep handlers thin, improving separation of concerns and unit testing.
Common pitfalls
Overusing Anonymous Functions
Inlining anonymous functions (lambdas) everywhere can cause surprise performance costs and reduced readability. Consider named methods or delegates if an endpoint’s logic grows beyond 10–15 lines.
Ignoring Dependency Injection Scopes
Minimal APIs can inject services directly into endpoints. Be cautious with lifetimes — injecting scoped services into singleton handlers or caching data incorrectly can cause unexpected issues.
Not Checking Nullable Context or Request Bodies
Always validate incoming data, especially when deserialising JSON. Minimal APIs will allow nulls if not carefully guarded, which can cause runtime errors.
Validation
.NET 9 minimal APIs integrate well with validation libraries like FluentValidation or data annotations via attributes:
app.MapPost("/users", (User user, IValidator<User> validator) =>
{
var result = validator.Validate(user);
if (!result.IsValid)
return Results.ValidationProblem(result.ToDictionary());
// Continue processing
return Results.Created($"/users/{user.Id}", user);
});
This gives early and structured feedback. Another modern alternative is to use built-in source-generators or custom middleware for validation, but they require additional setup and may introduce complexity.
Checklist / TL;DR
- Use minimal APIs for simple services or microservices with straightforward endpoints.
- Extract business logic to services; keep handlers as orchestration points.
- Group routes using extension methods or
MapGroupto organise code and share concerns. - Apply validation early and consistently using libraries or middleware.
- Avoid overly complex or lengthy inlined lambdas; favour named methods.
- Ensure correct DI lifetimes and avoid holding scoped services in longer-lived objects.
- Prefer stable features and track .NET versions for breaking changes (minimal APIs stable since .NET 6).
When to choose Minimal APIs vs Controllers
Minimal APIs excel at small, straightforward APIs, microservices, or prototypes. They reduce ceremony and improve startup time. However, if you require:
- Complex request pipelines
- Filters, Model Binding customization
- Rich conventions and attributes
- Extensive support for legacy patterns
then traditional MVC controllers remain a valid choice. You can mix minimal APIs and controllers in the same application for a hybrid approach.