Why you must use Minimal APIs over Controllers for new apps

Published: Monday 24 November 2025

When you start a new ASP.NET Core project with API endpoints, you might be wondering whether to use controllers or Minimal APIs.

The short answer?
You must use Minimal APIs. And here's why.

Cleaner code

One of the biggest advantages of Minimal APIs is how much cleaner and simpler your code looks.

Let’s start with a traditional controller approach. Here's a simple example that returns "Hello world":

// HelloWorldController.cs
[ApiController]
public class HelloWorldController : ControllerBase
{
    [HttpGet("/hello-world")]
    public ActionResult<string> HelloWorld()
    {
        return "Hello world";
    }
}

For this you need a namespace, class, attribute and method for a simple output. 

Now, here's the same endpoint written as a Minimal API:

// Program.cs
app.MapGet("/hello-world", () => "Hello world");

That's it. No attributes or class needed. 

Minimal APIs make your code more concise, easier to read, and quicker to maintain.

Less middleware and dependencies

When you call builder.Services.AddControllers() in your ASP.NET Core application, you're loading a lot more than just controller support.

You’re also pulling in the MVC assemblies and features like the Razor view engine, even if your API doesn't need them.

That extra overhead can add up.

Measuring the difference

For example, this simple "Hello World" endpoint using controllers will register more services in the dependency injection container.

// HelloWorldController.cs
[ApiController]
public class HelloWorldController : ControllerBase
{
    [HttpGet("/hello-world")]
    public ActionResult<string> HelloWorld()
    {
        return "Hello world";
    }
}

This is as a result of calling builder.Services.AddControllers(); from Program.cs. When you initialise an endpoint in a controller for the first time, the memory in your app will likely increase as it initialises those services.

Now let's remove the controller setup and switch to Minimal APIs. In Program.cs, remove these lines of code:

// Program.cs - Remove the below lines.
builder.Services.AddControllers();
app.UseAuthorization();
app.MapControllers();

And then add the Minimal API endpoint for "Hello world":

app.MapGet("/hello-world", () => "Hello world");

The result?

You’ll have fewer registered services, and memory usage drops slightly. You also don't have a memory increase when you initialise the controller for the first time.

New features in .NET 10

Minimal APIs keep evolving with every release of .NET and .NET 10 adds some powerful new features.

One of the most exciting additions is validation using data annotations, something previously only available in controllers.

Here's how it works.

This CreateToDoListTaskDto class has a Task property with three validation attributes assigned to it. It's required, must have a minimum length of 50 characters and a maximum length of 1,000 characters.

// CreateToDoListTaskDto.cs
public class CreateToDoListTaskDto
{
	[Required]
	[MinLength(50)]
	[MaxLength(1000)]
	public string Task { get; init; } = string.Empty;
}

Then you can add it as a parameter in your Minimal API endpoint like this:

// Program.cs
app.MapPost("/todo", (CreateToDoListTaskDto createToDoList) =>
	TypedResults.Created());

By default, this won't trigger validation. But you can enable it by adding this line in Program.cs:

// Program.cs
builder.Services.AddValidation();

Now, invalid requests are automatically validated, just like in MVC.

Even better, this feature also works with record types:

// CreateToDoListTaskDto.cs
public record CreateToDoListTaskDto
{
	[Required]
	[MinLength(50)]
	[MaxLength(1000)]
	public string Task { get; init; } = string.Empty;
}

Organising endpoints into classes

A common concern with Minimal APIs is that they can make your Program.cs file look cluttered. Just look at this:

// Program.cs
app.MapGet("/todo/{id}", 
	Results<Ok<ToDoItemDto>, NotFound> 
	(int id, IToDoService toDoService) =>
{
	var item = toDoService.Get(id);

	if (item == null)
	{
		return TypedResults.NotFound();
	}

	return TypedResults.Ok(item);
});
app.MapPost("/todo", (CreateToDoItemDto createItem, IToDoService toDoService) =>
{
	toDoService.Create(createItem);
	return TypedResults.NoContent();
});
app.MapPut("/todo/{id}", (int id, UpdateToDoItemDto updateItem, IToDoService toDoService) =>
{
	toDoService.Update(id, updateItem);
	return TypedResults.NoContent();
});
app.MapDelete("/todo/{id}", (int id, IToDoService toDoService) =>
{
	toDoService.Delete(id);
	return TypedResults.NoContent();
});

Fortunately, you can easily move endpoints into separate classes, like you would with controllers.

Here, we have moved these endpoints into a static class called ToDoEndpoints. Inside that endpoint, there is a static MapToDoGroup method that contains all the endpoints:

// ToDoEndpoints.cs
public static class ToDoEndpoints
{
	public static void MapToDoGroup(this WebApplication app)
	{
		var toDoGroup = app.MapGroup("/todo");

		toDoGroup.MapGet("/{id}",
			Results<Ok<ToDoItemDto>, NotFound>
			(int id, IToDoService toDoService) =>
			{
				var item = toDoService.Get(id);
				return item == null ? TypedResults.NotFound() : TypedResults.Ok(item);
			});

		toDoGroup.MapPost("/", (CreateToDoItemDto createItem, IToDoService toDoService) =>
		{
			toDoService.Create(createItem);
			return TypedResults.NoContent();
		});

		toDoGroup.MapPut("/{id}", (int id, UpdateToDoItemDto updateItem, IToDoService toDoService) =>
		{
			toDoService.Update(id, updateItem);
			return TypedResults.NoContent();
		});

		toDoGroup.MapDelete("/{id}", (int id, IToDoService toDoService) =>
		{
			toDoService.Delete(id);
			return TypedResults.NoContent();
		});
	}
}

Then, simply call that method from your Program.cs file:

// Program.cs
app.MapToDoGroup();

Another benefit of doing it this way is that you can use it for unit testing your Minimal API endpoint handlers.

Microsoft recommends Minimal APIs

It's not just us advocating for Minimal APIs - Microsoft offically recommends them too.

According to this overview:

"Minimal APIs are the recommended approach for building fast HTTP APIs with ASP.NET Core. They allow you to build fully functioning REST endpoints with minimal code and configuration. Skip traditional scaffolding and avoid unnecessary controllers by fluently declaring API routes and actions."

In other words, if you're starting a new project, Minimal APIs are the modern, efficient way to go.

See Minimal APIs in action

Watch this video to learn more about the points covered in this article so you can see why you should use Minimal APIs over controllers.

Final thoughts

Minimal APIs are simpler, faster, and more lightweight than controllers. They're also gaining new capabilities with every .NET release.

Controllers will still have their place in certain projects, but for most new APIs, Minimal APIs are the future.