Minimal APIs arrived in .NET 6 in 2021. They let you define HTTP endpoints without creating controller classes.
That makes it quick to get an endpoint running. It also makes it tempting to keep adding code to Program.cs until routes, business rules and data access are all mixed together.
Here I’ll compare the two approaches using the same endpoint, then look at how to keep the code organised. This is an introduction, not a performance benchmark.
Where did Minimal APIs come from?
Defining a route and its handler together is familiar from frameworks such as Express on Node.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Create an application, map a route to a handler, and start listening. The route and the response are easy to find in the same place.
Minimal APIs in .NET
With .NET 6, ASP.NET Core introduced its own streamlined approach to defining endpoints. If you know frameworks such as Express or Flask, the shape will feel familiar:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello world");
app.Run();
This is the entire Program.cs for a basic ASP.NET Core application. The builder sets up the application, MapGet connects a GET request to a handler, and Run starts the host.
No controller class is required. That does not mean you are leaving the ASP.NET Core ecosystem behind: routing, middleware, dependency injection, authentication and authorisation remain available.
Minimal APIs can also reduce framework overhead compared with controller-based endpoints. However, the result depends on your runtime version, configuration and workload. A tiny endpoint benchmark does not tell you how an application with database queries, network calls and business logic will behave. Measure your own application before choosing an architecture on performance grounds.
Getting an endpoint up and running
The handler does not have to be a parameterless function returning a string. It can receive route parameters, query values, request bodies, HttpContext and services registered with dependency injection. It can also be asynchronous.
The distinction is how you express the endpoint. Controllers and Minimal APIs share much of ASP.NET Core, although their conventions and feature sets are not identical.
Minimal APIs versus controllers
Let’s compare an endpoint that returns items from a repository. In both versions, assume your application already provides an IRepository interface and a Repository implementation with a GetAll() method. Those domain types are omitted here so we can focus on the API layer.
The controller-based version
First, register your repository and the controller services in Program.cs, then map the controllers:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IRepository, Repository>();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
The endpoint itself lives in a separate controller:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/items")]
public class ItemsController : ControllerBase
{
private readonly IRepository _repository;
public ItemsController(IRepository repository)
{
_repository = repository;
}
[HttpGet]
public IActionResult Get()
{
return Ok(_repository.GetAll());
}
}
There is a clear structure: a controller class, a constructor for the dependency, and an action method for the request. That structure can be useful, especially in a codebase that already follows controller conventions.
The Minimal API version
The same route can be expressed directly in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IRepository, Repository>();
var app = builder.Build();
app.MapGet("/api/items", (IRepository repository) =>
{
return Results.Ok(repository.GetAll());
});
app.Run();
Here, the registered repository is injected into the handler parameter, rather than a controller constructor. There is no controller class to create and no field to initialise.
Both examples expose GET /api/items. The Minimal API version simply needs less scaffolding to do it.
The main pitfall: keeping things organised
One handler in Program.cs is simple. Keep adding endpoints, validation, business rules and data access in the same file, and the application becomes harder to navigate, test and maintain.
Minimal APIs do not require you to put everything in one place. You can move handlers into dedicated files, group related endpoints, and keep business logic outside the HTTP layer.
The principle is simple: minimal ceremony should not mean minimal structure.
Which approach should you choose?
The choice depends on the needs of your application and the conventions of your team.
- Minimal APIs are a good fit when you want a straightforward, low-boilerplate way to define endpoints. They work well for small services and prototypes, and can also support larger applications when you organise them deliberately.
- Controllers provide established conventions and MVC-specific features that may be important to your application. They are also a natural fit when you are extending a controller-based codebase.
- Performance is something to verify against a representative workload, not a universal percentage to use as the deciding factor.
Both approaches can be used in production. Neither replaces the need for testing, validation, appropriate security and a maintainable design.
Less boilerplate is only the beginning
Getting an endpoint running is the easy part. Keeping it easy to find, change and test takes more thought.
The Request-Endpoint-Response (REPR) pattern is one way to organise each operation around its request, endpoint and response. It gives you a structure to work with without putting everything in one file.
For this English edition, I’ve updated the title and wording and replaced code screenshots with text examples. I’ve left out the original benchmark figures: the methodology wasn’t detailed enough to support a general comparison. The date above is the publication date of the English edition.
For further reading, see Microsoft’s Minimal API tutorial and parameter binding documentation.