.NET Minimal APIs Explained: A Guide to Concise Web APIs
An introduction to Minimal APIs, the new, streamlined way to build web APIs in .NET 6. Learn how to create a fully functional API with just a few lines of code, and see how it compares to the traditional controller-based approach.
With the release of .NET 6, Microsoft introduced a new, streamlined way to build web APIs: Minimal APIs. This feature is designed to reduce the ceremony and boilerplate code traditionally associated with creating APIs in ASP.NET Core, allowing you to build a fully functional HTTP endpoint in just a few lines of code.
It's a fantastic way to build small microservices, simple HTTP triggers, or to quickly prototype an idea.
The Traditional Way: Controllers
Before Minimal APIs, if you wanted to create a web API, you would use the controller-based approach. This involved creating a controller class that inherits from ControllerBase
and decorating action methods with attributes like [HttpGet]
and [HttpPost]
.
Example (Controller-based):
// In a file like Controllers/MyController.cs
[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
[HttpGet("/{id}")]
public IActionResult Get(int id)
{
return Ok(new { Message = $"Hello, item {id}!" });
}
}
This approach is powerful and great for large, complex APIs, but for a simple endpoint, it involves a lot of boilerplate.
The New Way: Minimal APIs
Minimal APIs allow you to define your endpoints directly in your Program.cs
file using simple lambda expressions. It leverages new C# 10 features like top-level statements and global usings to create an incredibly concise syntax.
Example (Minimal API):
// In Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/{id}", (int id) => $"Hello, item {id}!");
app.Run();
That's it! This code creates a web server with a single GET endpoint that does the same thing as the controller example above. It's clean, simple, and easy to read.
How it Works
The magic of Minimal APIs comes from a set of new Map
extension methods on WebApplication
:
app.MapGet()
app.MapPost()
app.MapPut()
app.MapDelete()
Each of these methods takes a route template and a Delegate
(usually a lambda) that handles the request.
Dependency Injection and Parameter Binding
Minimal APIs have full support for dependency injection and intelligent parameter binding. The framework is smart enough to figure out where to get the parameters for your lambda from.
- Parameters from the route are automatically bound.
- Services registered in the DI container can be injected as parameters.
- The request body can be bound to a parameter.
Example with Dependency Injection and Body Binding:
// Program.cs
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Register a DbContext for dependency injection
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
// GET all todos
app.MapGet("/todos", async (TodoDb db) =>
await db.Todos.ToListAsync());
// POST a new todo
app.MapPost("/todos", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todos/{todo.Id}", todo);
});
app.Run();
// --- Data Model and DbContext ---
class Todo
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
class TodoDb : DbContext
{
public TodoDb(DbContextOptions<TodoDb> options) : base(options) { }
public DbSet<Todo> Todos => Set<Todo>();
}
In this example:
- In the
GET
endpoint,TodoDb db
is automatically resolved from the DI container. - In the
POST
endpoint,Todo todo
is automatically deserialized from the JSON request body, andTodoDb db
is injected.
Returning Results
Instead of returning strings directly, you can use the static Results
class to produce more complex responses, just like Ok()
, NotFound()
, and BadRequest()
in controllers.
app.MapGet("/users/{id}", (int id, UserDb db) =>
{
var user = db.Users.Find(id);
return user is not null
? Results.Ok(user)
: Results.NotFound();
});
When to Use Minimal APIs vs. Controllers
Use Minimal APIs for:
- Small microservices with only a few endpoints.
- Simple HTTP-triggered functions (e.g., in an Azure Functions or AWS Lambda context).
- Rapidly prototyping a new API.
- Learning ASP.NET Core, as it has a much lower barrier to entry.
Use Controllers for:
- Large, complex APIs with many endpoints and complex logic.
- When you need the full feature set of MVC, such as filters, model binding from forms, or views.
- When you are working on a large team that needs the structure and organization that controllers provide.
It's also important to note that you can mix and match. A single application can use both Minimal APIs and controllers.
Conclusion
Minimal APIs are a fantastic addition to the .NET ecosystem. They provide a modern, concise, and highly performant way to build APIs, dramatically reducing the amount of code needed to get started. While they may not replace controllers for every use case, they are an excellent tool for a wide range of scenarios and a great new entry point for developers coming to ASP.NET Core.