4 min readBy Muhammad Shahid
ASP.NET Core Minimal APIs: When to Use Them (and When Not To)
ASP.NET Core Minimal APIs for production — MapGet/MapPost, DI, validation, OpenAPI, auth, and when controllers still win for Angular-backed products.
Part of Architecture
ASP.NET Core Minimal APIs are not a toy for demos. They are a first-class way to ship HTTP endpoints with less ceremony — and a common source of spaghetti when teams dump business logic into Program.cs.
I use Minimal APIs for thin gateways, internal tools, and vertical slices. I keep controllers (or carefully organized endpoint classes) when Angular admin surfaces need many actions, filters, and shared conventions.
What Minimal APIs give you
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<AppDbContext>(...);
builder.Services.AddScoped<IOrderService, OrderService>();
var app = builder.Build();
app.MapGet("/api/health", () => Results.Ok(new { status = "ok" }));
app.MapGet("/api/orders/{id:guid}", async (
Guid id,
IOrderService orders,
CancellationToken ct) =>
{
var order = await orders.GetAsync(id, ct);
return order is null ? Results.NotFound() : Results.Ok(order);
});
app.Run();
DI parameters bind automatically. Return IResult helpers (Results.Ok, Results.NotFound, Results.ValidationProblem).
Organize so Program.cs does not become a landfill
// Endpoints/OrderEndpoints.cs
public static class OrderEndpoints
{
public static RouteGroupBuilder MapOrders(this WebApplication app)
{
var group = app.MapGroup("/api/orders").RequireAuthorization();
group.MapGet("/{id:guid}", GetByIdAsync);
group.MapPost("/", CreateAsync);
return group;
}
private static async Task<IResult> GetByIdAsync(
Guid id,
IOrderService orders,
CancellationToken ct)
{
var order = await orders.GetAsync(id, ct);
return order is null ? Results.NotFound() : Results.Ok(order);
}
}
app.MapOrders();
Same SRP rule as controllers: endpoints are HTTP adapters. Pricing and persistence stay in services/handlers.
Validation without MVC attributes everywhere
Options that work well:
- FluentValidation filters / endpoint filters
- DataAnnotations +
MiniValidator/ built-in validation where available - Explicit guard clauses for tiny endpoints
app.MapPost("/api/orders", async (
CreateOrderRequest request,
ICreateOrderHandler handler,
CancellationToken ct) =>
{
if (string.IsNullOrWhiteSpace(request.Sku))
return Results.ValidationProblem(new Dictionary<string, string[]>
{
["sku"] = ["SKU is required."]
});
var created = await handler.HandleAsync(request, ct);
return Results.Created($"/api/orders/{created.Id}", created);
});
Angular forms need stable problem details — same as MVC. See API validation envelopes.
Auth, OpenAPI, and versioning
builder.Services.AddAuthentication().AddJwtBearer(...);
builder.Services.AddAuthorization();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
app.UseAuthentication();
app.UseAuthorization();
var orders = app.MapGroup("/api/v1/orders").RequireAuthorization("ManageOrders");
Minimal APIs play fine with JWT policies you already use for Angular SPAs. Document claims the same way you would on controllers.
Controllers vs Minimal APIs — honest tradeoffs
| Prefer Minimal APIs when… | Prefer Controllers when… |
|---|---|
| Few endpoints, clear vertical slices | Large admin API surface |
| Gateway / BFF / health / webhooks | Heavy filter pipelines and conventions already exist |
| Team likes function-style composition | Existing MVC codebase — rewrite cost > benefit |
| Prototypes that might stay small | Many shared base controller behaviors |
Ecom_NET10-style catalogs with dozens of merchandising actions still felt clearer as controllers. A CarBazaar webhook receiver was cleaner as Minimal API groups.
Performance note
Minimal APIs are lightweight, but your EF queries and auth dominate latency. Switching endpoint style will not fix N+1 SQL. Measure before celebrating.
Failure story: everything in Program.cs
A team put 40 Map* calls, DTO mapping, and EF queries in one file “because Minimal APIs are simple.” Code review became impossible; Angular contract changes took hours to find. We extracted endpoint classes and handlers — Minimal APIs stayed, the landfill left.
Delivery checklist
- Endpoints live in groups/classes — not a 2,000-line
Program.cs - Business logic in services/handlers, not lambdas
- JWT / policies applied via
RequireAuthorization - Validation returns consistent problem details for Angular
- OpenAPI covers the routes the SPA consumes
- Async +
CancellationTokenend to end - Decide controller vs Minimal API per bounded context, not as ideology
Related reading
- C# Async and Await in ASP.NET Core
- Clean Architecture in ASP.NET Core
- ASP.NET Core JWT Auth checklist
Need help choosing Minimal APIs vs controllers for a new .NET + Angular slice? Contact me with the endpoint list and we can sketch the shape before the folder structure locks in.