What is this about?
When you build a new API in .NET 10 today, you face a decision that didn’t exist before: Minimal API, classic Controllers, or FastEndpoints? All three are fully capable approaches — but they have different strengths, weaknesses, and use cases.
In this post, we take an honest look at all three.
Minimal APIs — lean, modern, fast
Minimal APIs were introduced with .NET 6 and have evolved significantly since then. In .NET 10, they have truly come of age.
The simplest endpoint in the world
app.MapGet("/ping", () => "pong");
That’s a complete API. One line. No controller, no class, no attribute.
What’s new in .NET 10?
Built-in Validation Previously you had to either pull in FluentValidation or write your own checks. Now a simple call is enough:
builder.Services.AddValidation();
Your DTO with DataAnnotations is then automatically validated — and on failure, a clean ProblemDetails response is returned. No more manual if-statement juggling.
Endpoint Filters instead of Action Filters
Instead of Action Filters, Minimal APIs use Endpoint Filters. The concept is similar — logic before or after the handler — but less powerful:
app.MapPost("/orders", Handler)
.AddEndpointFilter<LoggingFilter>()
.AddEndpointFilter<ValidationFilter>();
Filters run from outside in — like middleware onions. For logging, validation, and simple auth checks, this works well. For complex result manipulation, it gets tricky.
The biggest advantage in my opinion: Native AOT
Minimal APIs support Native AOT (Ahead-of-Time Compilation) — Controllers do not. That means: no JIT at runtime, lightning-fast startup, less memory, smaller deployments. A real game-changer for Lambda functions, Kubernetes pods, or edge deployments.
Extracting endpoints — the Extension Method trick
A clean solution for larger projects: move endpoints into their own > classes and register them via Extension Methods:
public static class OrderEndpoints { public static WebApplication MapOrderEndpoints(this WebApplication >app){ app.MapPost("/orders", HandleCreate); app.MapGet("/orders/{id}", HandleGet); return app; } }Program.cs stays clean:
app.MapOrderEndpoints(); app.MapProductEndpoints(); app.MapCustomerEndpoints();
Controllers — proven, structured, complete
Controllers are the classic approach in ASP.NET Core. They’ve been around for years, are well documented, and every .NET developer knows them.
What Controllers do better
Action Filter Pipeline — this is the biggest difference. Controllers have
- OnActionExecuting,
- OnActionExecuted,
- OnResultExecuting
very fine-grained control over different phases of the request lifecycle.
Direct Unit Testability — you can instantiate a controller directly and test it without spinning up the entire HTTP stack:
var controller = new OrdersController(mockService); var result = controller.Create(request);
Proven Structure — one class per resource, methods per action. New team member? They’ll find their way around immediately.
What Controllers cannot do
No Native AOT support — Controllers rely too heavily on Reflection, and Reflection is incompatible with Native AOT. This is a fundamental limitation, not a bug.
FastEndpoints — the best of both worlds
FastEndpoints is an open-source library that builds on top of Minimal APIs — but gives you a clean, structured development experience on top.
The REPR Pattern
The core concept is the REPR Pattern — Request → Endpoint → Response. Each endpoint gets its own class. Not one controller class with 10 methods — but truly one class per endpoint:
public class CreateOrderEndpoint : Endpoint<CreateOrderRequest>
{
public override void Configure(){
Post("/orders");
AllowAnonymous();
}
public override async Task HandleAsync(CreateOrderRequest req,
CancellationToken ct)
{
await SendOkAsync("Order created");
}
}
You can see: it has the structure of Controllers — a class, clear methods — but the performance of Minimal APIs underneath.
What FastEndpoints brings
• Built-in Validation with FluentValidation — directly integrated
• Filter System — similar to Action Filters, cleanly implemented
• Performance on par with Minimal APIs — noticeably faster than Controllers
• Native AOT Support — because it builds on Minimal APIs
The trade-off
FastEndpoints is an external dependency. You’re bringing in a library with its own concepts. New team members need to learn it. The community is smaller than for Controllers or Minimal APIs.
Conclusion — which one should I pick?
The honest answer: it depends. But here’s a simple rule of thumb:
• Minimal API → Small, fast, modern. Perfect for microservices and cloud-native.
• Controllers → Large, proven, structured. Perfect for large teams and complex projects.
• FastEndpoints → The best of both worlds — with an external dependency as the price.
And the great thing: all three can coexist in the same app. You can start with Minimal APIs and migrate to FastEndpoints later — or keep Controllers for complex parts and use Minimal APIs for simple endpoints.
Top comments (0)