Claude-skill-registry dotnet-minimal-apis
Guide for building ASP.NET Core Minimal APIs with OpenAPI/Swagger integration in .NET 10
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dotnet-minimal-apis" ~/.claude/skills/majiayu000-claude-skill-registry-dotnet-minimal-apis && rm -rf "$T"
skills/data/dotnet-minimal-apis/SKILL.mdASP.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:
- creates builderWebApplication.CreateBuilder(args)
- dependency injectionbuilder.Services
- builds applicationapp.Build()
- defines endpointsapp.Map*()
- starts serverapp.Run()
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)
for OpenAPI.WithName()
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.