MinimalAPIsTemplate
Complete ASP.NET Core template ready-to-go with: OpenAI, OAuth 2.0, JSON Web Algorithms and Bearer Tokens (JOSE: JWT, JWS, JWE), HMAC symmetric key, RSA X509 certificate asymmetric key, AES encryption, CQRS, DDD, MediatR, Dapper, Docker, Entity Framework, Fluent Validation, GZip, Hangfire, NLog, REST CRUD, Swagger, Scalar...
Install / Use
/learn @RobertoFalconi/MinimalAPIsTemplateREADME
MinimalAPIs Template
A production-ready template for building .NET 10 Minimal APIs following Clean Architecture, Domain-Driven Design (DDD), and CQRS — without any external mediator libraries.
Table of Contents
- Overview
- Architecture
- Project Structure
- Design Patterns
- Technologies
- Prerequisites
- Getting Started
- API Endpoints
- Authentication
- Adding a New Use Case
- License
Overview
This repository provides a clean, minimal, and opinionated starting point for ASP.NET Core APIs. It demonstrates how to combine Minimal APIs with architectural best practices — keeping the codebase simple, testable, and easy to extend — without relying on third-party mediator packages.
Architecture
The solution is organized into 3 layers, each with a strict dependency rule: outer layers depend on inner layers, never the other way around.
The Api project has dependencies on both Infrastructure and Core.
The Infrastructure project has dependencies on Core.
The Core project has zero dependencies on Infrastructure or any framework-specific package.
Design Patterns
Clean Architecture
Each layer only references layers closer to the center. Concrete types (e.g., AppDbContext, WeatherForecastRepository) are never referenced by the Core project or the endpoint definitions — they are resolved through DI at startup.
Domain-Driven Design (DDD)
- Encapsulated entities:
WeatherForecastexposesprivate setproperties and a staticCreate(...)factory method that enforces invariants at construction time. - Repository interface in the Domain:
IWeatherForecastRepositoryis owned by the domain layer, not by Infrastructure. - No anemic model: entity creation logic lives inside the entity itself, not in a service.
CQRS (without MediatR)
Commands and queries are fully separated using two lightweight generic interfaces defined in Application/Abstractions:
public interface IQueryHandler<TQuery, TResult> { Task<TResult> HandleAsync(TQuery query); }
public interface ICommandHandler<TCommand, TResult> { Task<TResult> HandleAsync(TCommand command); }
Each use case is a single, focused class. Endpoints inject only the handler they need:
// Query
app.MapGet("/weatherforecast", async (IQueryHandler<GetAllWeatherForecastsQuery, IEnumerable<WeatherForecast>> handler) => Results.Ok(await handler.HandleAsync(new GetAllWeatherForecastsQuery())));
// Command
app.MapPost("/weatherforecast", async (CreateWeatherForecastCommand command, ICommandHandler<CreateWeatherForecastCommand, WeatherForecast> handler) => { var created = await handler.HandleAsync(command); return Results.Created($"/weatherforecast/{created.Id}", created); });
Dependency Injection via Extension Methods
Each layer registers its own services through dedicated extension methods, keeping Program.cs clean:
builder.Services.AddApplication(); builder.Services.AddInfrastructure(builder.Configuration);
Technologies
| Technology | Version | Purpose |
| .NET | 10 | Runtime & SDK |
| ASP.NET Core Minimal APIs | 10 | HTTP layer |
| Entity Framework Core | 10 | ORM |
| SQL Server | — | Relational database |
| Scalar | – | API documentation |
Prerequisites
- .NET 10 SDK
- A running SQL Server instance (local or remote)
- EF Core CLI tools dotnet tool install --global dotnet-ef
Getting Started
1. Clone the repository
git clone https://github.com/RobertoFalconi/MinimalAPIsTemplate.git
2. Configure the connection string
This is a project with a code-first approach. DB should be created on the first run. You can edit MinimalAPIsAndCleanArchitecture/appsettings.json to change the ConnectionString:
{ "ConnectionStrings": { "DefaultConnection": "Server=YOUR_SERVER;Database=WeatherDb;Trusted_Connection=True;TrustServerCertificate=True;" } }
3. Apply database migrations
dotnet ef database update --project MinimalAPIsAndCleanArchitecture.Infrastructure --startup-project MinimalAPIsAndCleanArchitecture
4. Run the application
dotnet run --project MinimalAPIsAndCleanArchitecture
The Scalar API Reference opens automatically at http://localhost:5179/scalar/v1 in the browser on startup (development mode).
API Endpoints
| Method | Route | Auth required | Description | Request body |
|--------|-------|:---:|-------------|--------------|
| POST | /auth/token | ✗ | Returns a JWT Bearer token | { "username": "string", "password": "string" } |
| GET | /weatherforecast | ✔ | Returns all weather forecasts | — |
| POST | /weatherforecast | ✔ | Creates a new weather forecast | { "date": "2026-03-04", "temperatureC": 22, "summary": "Warm" } |
Example — POST /weatherforecast
Request
{ "date": "2026-03-04", "temperatureC": 22, "summary": "Warm" }
Response 201 Created
{ "id": 1, "date": "2026-03-04", "temperatureC": 22, "temperatureF": 71, "summary": "Warm" }
Authentication
The API uses JWT Bearer authentication. Protected endpoints require a valid token in the Authorization header.
1. Obtain a token
Call POST /auth/token with valid credentials:
POST /auth/token
Content-Type: application/json
{
"username": "",
"password": ""
}
Response 200 OK
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
⚠️ Demo credentials are
/. ReplaceGenerateTokenCommandHandlerwith a real user store before going to production.
2. Call a protected endpoint
Add the token to every subsequent request via the Authorization header:
GET /weatherforecast
Authorization: Bearer <token>
3. Using Scalar
- Open
http://localhost:5179/scalar/v1 - Add "Authorization" in the headers with "Authorization" as key and paste the token in the value field with "Bearer " prefix
Adding a New Use Case
The CQRS structure makes it straightforward to add new features without touching existing code (Open/Closed Principle).
Example: delete a forecast by ID
1. Create the command
// Core/Application/Commands/DeleteWeatherForecastCommand.cs
public record DeleteWeatherForecastCommand(int Id);
2. Extend the repository interface
// Core/Domain/Interfaces/IWeatherForecastRepository.cs
Task DeleteAsync(int id);
3. Implement the handler
// Core/Application/Commands/DeleteWeatherForecastCommandHandler.cs
public class DeleteWeatherForecastCommandHandler(IWeatherForecastRepository repository) : ICommandHandler<DeleteWeatherForecastCommand, bool> { public async Task<bool> HandleAsync(DeleteWeatherForecastCommand command) { await repository.DeleteAsync(command.Id); return true; } }
4. Register in DI
// Core/DependencyInjection.cs
services.AddScoped< ICommandHandler<DeleteWeatherForecastCommand, bool>, DeleteWeatherForecastCommandHandler>();
5. Map the endpoint
// Endpoints/WeatherForecastEndpoint.cs
app.MapDelete("/weatherforecast/{id:int}", async (int id, ICommandHandler<DeleteWeatherForecastCommand, bool> handler) => { await handler.HandleAsync(new DeleteWeatherForecastCommand(id)); return Results.NoContent(); }).WithName("DeleteWeatherForecast");
License
This project is licensed under the MIT License.
Related Skills
node-connect
351.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
110.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
351.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
351.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
