ASP.NET Core Minimal APIs
This skill provides guidance for building Minimal APIs in ASP.NET Core .NET 10. Minimal APIs provide a simplified approach to building HTTP APIs with minimal dependencies and boilerplate.
Table of Contents
- •Overview
- •Project Structure
- •Basic Endpoints
- •Route Groups
- •Parameter Binding
- •OpenAPI Integration
- •Best Practices
- •Quick Reference
Overview
What are Minimal APIs?
Minimal APIs are a simplified way to create HTTP APIs in ASP.NET Core without controllers. They provide:
- •Less code: No controllers, no attribute routing
- •Fast development: Define endpoints inline
- •Performance: Lower overhead than MVC controllers
- •Ideal for: Microservices, simple APIs, cloud-native apps
When to Use Minimal APIs
Use Minimal APIs when:
- •Building microservices
- •Creating simple REST APIs
- •Prototyping quickly
- •Prioritizing performance
Use MVC Controllers when:
- •Complex validation logic
- •Heavy use of filters/middleware per endpoint
- •Team prefers OOP patterns
- •Large enterprise applications
Project Structure
ClaudeStack.API Project
This repository includes src/ClaudeStack.API demonstrating minimal API patterns.
Current Program.cs structure:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure middleware
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
// Define endpoints
app.MapGet("/weatherforecast", () => { /* ... */ })
.WithName("GetWeatherForecast");
app.Run();
Key components:
- •
WebApplication.CreateBuilder(args)- creates builder - •
builder.Services- dependency injection - •
app.Build()- builds application - •
app.Map*()- defines endpoints - •
app.Run()- starts server
Basic Endpoints
MapGet
Simple GET endpoint:
app.MapGet("/hello", () => "Hello World!");
With route parameters:
app.MapGet("/users/{id}", (int id) => $"User {id}");
With query parameters:
app.MapGet("/search", (string? query) => $"Searching for: {query}");
Async with return type:
app.MapGet("/users/{id}", async (int id, UserService service) =>
{
var user = await service.GetUserAsync(id);
return user is not null ? Results.Ok(user) : Results.NotFound();
});
MapPost, MapPut, MapDelete
app.MapPost("/users", (User user) => Results.Created($"/users/{user.Id}", user));
app.MapPut("/users/{id}", (int id, User user) => Results.NoContent());
app.MapDelete("/users/{id}", (int id) => Results.NoContent());
Example from Project
From src/ClaudeStack.API/Program.cs:
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
Key points:
- •Inline lambda
- •Returns typed data (serialized as JSON)
- •
.WithName()for OpenAPI
Route Groups
Basic Route Group
Organize related endpoints:
var users = app.MapGroup("/users");
users.MapGet("/", () => "All users");
users.MapGet("/{id}", (int id) => $"User {id}");
users.MapPost("/", (User user) => Results.Created($"/users/{user.Id}", user));
Route Group with Prefix
var api = app.MapGroup("/api/v1");
api.MapGet("/users", () => "All users");
api.MapGet("/products", () => "All products");
Route Group with Filters
var admin = app.MapGroup("/admin")
.RequireAuthorization("AdminPolicy");
admin.MapGet("/users", () => "Admin: All users");
admin.MapDelete("/users/{id}", (int id) => Results.NoContent());
Parameter Binding
Binding Examples
// Route parameters
app.MapGet("/users/{id}", (int id) => $"User {id}");
// Query string
app.MapGet("/search", (string? q, int page = 1) => $"{q}, Page {page}");
// Request body (JSON)
app.MapPost("/users", (User user) => Results.Created($"/users/{user.Id}", user));
// Dependency injection
app.MapGet("/users", (UserService service) => service.GetAllUsers());
// Register: builder.Services.AddScoped<UserService>();
// Multiple sources
app.MapPost("/users/{id}", (int id, User user, UserService service) =>
service.UpdateUser(id, user));
OpenAPI Integration
Adding OpenAPI
This project uses OpenAPI (configured in Program.cs):
// Add service
builder.Services.AddOpenApi();
// Map OpenAPI endpoint (development only)
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
Access OpenAPI spec: https://localhost:5001/openapi/v1.json
Endpoint Metadata
Naming endpoints:
app.MapGet("/users", () => "All users")
.WithName("GetAllUsers");
Adding descriptions:
app.MapGet("/users/{id}", (int id) => $"User {id}")
.WithName("GetUser")
.WithSummary("Get user by ID")
.WithDescription("Returns a single user by their unique identifier");
Adding tags:
app.MapGet("/users", () => "All users")
.WithTags("Users");
app.MapGet("/products", () => "All products")
.WithTags("Products");
Response Types
app.MapGet("/users/{id}", (int id) => Results.Ok(new User()))
.Produces<User>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
Best Practices
1. Use Typed Results
Prefer:
app.MapGet("/users/{id}", (int id) =>
Results.Ok(user)
// or Results.NotFound()
);
Avoid:
app.MapGet("/users/{id}", (int id) => user); // Implicit 200 OK
2. Name All Endpoints
app.MapGet("/users", () => "All users")
.WithName("GetAllUsers");
Required for:
- •URL generation
- •OpenAPI documentation
- •Testing
3. Organize with Route Groups
var users = app.MapGroup("/users").WithTags("Users");
users.MapGet("/", GetAllUsers).WithName("GetAllUsers");
users.MapGet("/{id}", GetUser).WithName("GetUser");
users.MapPost("/", CreateUser).WithName("CreateUser");
4. Extract Handler Methods
Instead of inline:
app.MapGet("/users", () => { /* 50 lines */ });
Use local functions:
app.MapGet("/users", GetAllUsers);
static IResult GetAllUsers(UserService service)
{
var users = service.GetAll();
return Results.Ok(users);
}
5. Use Record Types
Perfect for DTOs:
record User(int Id, string Name, string Email); record CreateUserRequest(string Name, string Email);
ClaudeStack.API uses this pattern:
record WeatherForecast(DateOnly Date, int TemperatureC, string Summary);
6. Explicit Using Statements
This project has ImplicitUsings disabled. Always include:
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting;
7. Validate Input
app.MapPost("/users", (CreateUserRequest request) =>
{
if (string.IsNullOrEmpty(request.Name))
return Results.BadRequest("Name is required");
if (string.IsNullOrEmpty(request.Email))
return Results.BadRequest("Email is required");
// Create user
return Results.Created("/users/1", user);
});
8. Use Filters for Cross-Cutting Concerns
app.MapGet("/admin/users", () => "Admin users")
.RequireAuthorization("AdminPolicy");
Quick Reference
Endpoint Methods
app.MapGet("/path", handler) // GET
app.MapPost("/path", handler) // POST
app.MapPut("/path", handler) // PUT
app.MapPatch("/path", handler) // PATCH
app.MapDelete("/path", handler) // DELETE
app.MapMethods("/path", ["GET", "POST"], handler) // Custom
Results Helpers
Results.Ok(value) // 200 OK
Results.Created("/path", value) // 201 Created
Results.NoContent() // 204 No Content
Results.BadRequest(error) // 400 Bad Request
Results.NotFound() // 404 Not Found
Results.Problem(details) // 500 Internal Server Error
Endpoint Configuration
.WithName("EndpointName")
.WithSummary("Short summary")
.WithDescription("Longer description")
.WithTags("Tag1", "Tag2")
.Produces<Type>(statusCode)
.RequireAuthorization(policy)
Route Groups
var group = app.MapGroup("/prefix");
group.MapGet("/path", handler);
Parameter Binding Sources
(int id) // Route parameter (string? query) // Query string (User user) // Request body (JSON) (HttpRequest request) // HTTP request (HttpContext context) // HTTP context (IService service) // DI service
Related Skills
- •dotnet-cli-essentials: Running and building API projects
- •aspnet-configuration: Configuring appsettings for APIs
- •mstest-testing-platform: Testing minimal APIs
Additional Resources
Version Information
- •.NET: 10.0 RC 2
- •ASP.NET Core: 10.0.0-rc.2.25502.107
- •OpenAPI Package: Microsoft.AspNetCore.OpenApi 10.0.0-rc.2.25502.107
Minimal APIs are a stable feature as of .NET 6.0+. This project uses .NET 10 RC 2 patterns.