Minimal APIs have become a popular choice for building lightweight HTTP services in ASP.NET Core. By reducing boilerplate code and simplifying endpoint definitions, they enable developers to create fast, maintainable APIs with minimal configuration.
However, simply using Minimal APIs doesn't automatically guarantee high performance. Efficient routing, optimized JSON serialization, proper dependency injection, caching, and asynchronous programming all play an important role in building scalable production applications.
In this article, you'll learn practical techniques for optimizing Minimal APIs in ASP.NET Core 11, explore common performance pitfalls, and understand how to evaluate improvements using a structured benchmarking methodology.
Note: This article focuses on optimization techniques and benchmark methodology. It intentionally avoids presenting fabricated benchmark results.
Why Performance Optimization Matters
Every HTTP request consumes CPU, memory, network bandwidth, and database resources.
Poorly optimized APIs can lead to:
Higher response latency
Increased memory allocations
Reduced throughput
Database bottlenecks
Higher cloud infrastructure costs
Small optimizations become increasingly valuable as request volume grows.
What Are Minimal APIs?
Minimal APIs allow endpoints to be defined without controllers.
Traditional controller:
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok();
}
}
Minimal API equivalent:
var app = builder.Build();
app.MapGet("/products", () =>
{
return Results.Ok();
});
app.Run();
The reduced abstraction makes applications easier to understand while lowering framework overhead.
Return Typed Results
Instead of returning anonymous objects directly, use typed results.
app.MapGet("/products/{id}", (int id) =>
{
return Results.Ok(new
{
Id = id
});
});
Benefits include:
Better OpenAPI metadata
Stronger typing
Improved readability
Use Asynchronous Endpoints
Avoid synchronous database operations.
app.MapGet("/products", async (
AppDbContext db) =>
{
return await db.Products
.AsNoTracking()
.ToListAsync();
});
Asynchronous I/O improves scalability by preventing request threads from blocking while waiting for database or network operations.
Disable Tracking for Read Operations
Read-only endpoints should avoid change tracking.
var products = await db.Products
.AsNoTracking()
.ToListAsync();
This reduces memory usage and CPU overhead.
Return Only Required Data
Avoid returning entire entities.
Instead of:
return await db.Products.ToListAsync();
Project only the required fields.
return await db.Products
.Select(p => new
{
p.Id,
p.Name,
p.Price
})
.ToListAsync();
Smaller payloads reduce serialization time and network traffic.
Use Route Groups
Route groups simplify endpoint organization.
var products = app.MapGroup("/products");
products.MapGet("/", GetProducts);
products.MapPost("/", CreateProduct);
Grouping endpoints improves maintainability without affecting request routing performance.
Inject Dependencies Efficiently
Inject only the services required by an endpoint.
app.MapGet("/products",
async (
AppDbContext db,
ILogger<Program> logger) =>
{
logger.LogInformation("Fetching products");
return await db.Products.ToListAsync();
});
Avoid resolving unnecessary services on every request.
Add Response Compression
Enable response compression for large payloads.
builder.Services.AddResponseCompression();
var app = builder.Build();
app.UseResponseCompression();
Compression reduces bandwidth usage, particularly for JSON responses.
Cache Frequently Requested Data
Use distributed or in-memory caching for data that changes infrequently.
app.MapGet("/categories",
async (
IDistributedCache cache) =>
{
var json =
await cache.GetStringAsync("categories");
return Results.Ok(json);
});
Caching reduces database load and improves response times.
Apply Rate Limiting
Protect endpoints from excessive traffic.
app.MapGet("/products", GetProducts)
.RequireRateLimiting("api");
Rate limiting improves application stability during traffic spikes.
Configure JSON Serialization
ASP.NET Core uses System.Text.Json by default.
Customize serialization options when appropriate.
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.WriteIndented = false;
});
Avoid unnecessary formatting in production responses to reduce payload size.
End-to-End Request Flow
An optimized request typically follows these steps:
Client sends a request.
Routing selects the endpoint.
Rate limiting validates the request.
Cache is checked.
Database is queried if necessary.
Data is projected into lightweight objects.
JSON is serialized.
Response compression is applied.
Response is returned.
Each optimization contributes to overall application performance.
Join the conversation! Your thoughts help the community grow.