A modern, high-performance ASP.NET Core API built with FastEndpoints following vertical slice architecture principles. This project demonstrates a clean, maintainable approach to building REST APIs with minimal ceremony and maximum productivity.
This project follows Vertical Slice Architecture, organizing code by feature rather than technical layers:
- Features/ - Domain-driven feature folders (e.g.,
Features/Monkeys/
) containing all related endpoints - Common/ - Shared infrastructure including:
- Domain models with rich behavior
- Commands for operations with side effects
- Events for loosely-coupled notifications
- Middleware and preprocessors
- Persistence layer (in-memory repository)
- Endpoint Groups: Define route prefixes and shared configuration (
MonkeyGroup
β/api/monkeys
) - Rich Domain Models: Private setters with static factory methods prevent invalid state
- CQRS-lite: Commands and events via FastEndpoints' built-in mediator
- Request/Response DTOs: Immutable records for clear contracts
- FluentValidation: Auto-discovered validators with detailed error messages
- .NET 10 SDK or later
- Any IDE (Visual Studio 2022, VS Code, Rider)
- Clone the repository:
git clone <repository-url>
cd FastEndpoints
- Restore dependencies:
dotnet restore
- Run the application:
dotnet run --project FastWebApi/FastWebApi.csproj
The API will start on:
- HTTPS:
https://localhost:7038
- HTTP:
http://localhost:5128
- Open Swagger UI in your browser:
https://localhost:7038/swagger
All monkey endpoints are grouped under /api/monkeys
:
GET /api/monkeys
Response (200 OK):
{
"monkeys": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Bubbles",
"age": 4,
"temperament": "Playful"
}
]
}
POST /api/monkeys
Content-Type: application/json
{
"name": "Bubbles",
"age": 4,
"temperament": "Playful"
}
Valid Temperament Values:
Calm
Playful
Aggressive
Responses:
204 No Content
- Monkey created successfully400 Bad Request
- Validation failed or monkey limit reached (max 5 monkeys)
Example with cURL:
curl -X POST https://localhost:7038/api/monkeys \
-H "Content-Type: application/json" \
-d '{"name":"Bubbles","age":4,"temperament":"Playful"}'
DELETE /api/monkeys/{id}
Responses:
204 No Content
- Monkey deleted successfully404 Not Found
- Monkey not found
Example with cURL:
curl -X DELETE https://localhost:7038/api/monkeys/3fa85f64-5717-4562-b3fc-2c963f66afa6
- Navigate to
https://localhost:7038/swagger
- Expand any endpoint
- Click "Try it out"
- Fill in parameters and click "Execute"
Create a file named test.http
:
### Get all monkeys
GET https://localhost:7038/api/monkeys
### Create a monkey
POST https://localhost:7038/api/monkeys
Content-Type: application/json
{
"name": "George",
"age": 3,
"temperament": "Calm"
}
### Delete a monkey (replace with actual ID)
DELETE https://localhost:7038/api/monkeys/3fa85f64-5717-4562-b3fc-2c963f66afa6
FastWebApi/
βββ Features/
β βββ Monkeys/
β βββ CreateMonkeyEndpoint.cs # POST endpoint with validation
β βββ GetMonkeysEndpoint.cs # GET endpoint
β βββ DeleteMonkeyEndpoint.cs # DELETE endpoint
β βββ MonkeyGroup.cs # Route group configuration
βββ Common/
β βββ Domain/ # Rich domain models
β βββ Commands/ # Command pattern implementations
β βββ Events/ # Event publishing/handling
β βββ Middleware/ # Custom middleware
β βββ Preprocessors/ # Request preprocessors
β βββ Persistence/ # Repository implementations
βββ Program.cs # Application entry point
βββ GlobalUsings.cs # Global namespace imports
Configuration is managed through appsettings.json
and appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Edit Properties/launchSettings.json
to customize:
- Port numbers
- Environment variables
- Launch behavior (browser, URLs, etc.)
- FastEndpoints (7.0.1) - High-performance endpoint library
- FastEndpoints.Swagger (7.0.1) - OpenAPI/Swagger integration
- .NET 10 - Target framework
- β High Performance: FastEndpoints eliminates controller overhead
- β Auto-Validation: FluentValidation with automatic error responses
- β OpenAPI/Swagger: Interactive API documentation
- β Request Logging: Global preprocessor logs all requests
- β Command Pattern: Separate side effects from core logic
- β Event Publishing: Loosely-coupled event handlers
- β Rich Domain Models: Encapsulated business logic
- β Nullable Reference Types: Improved null safety
- Create endpoint class in appropriate feature folder:
public class MyEndpoint(Dependencies deps) : Endpoint<TRequest, TResponse>
{
public override void Configure()
{
Post("/route");
Group<MyGroup>();
}
public override async Task HandleAsync(TRequest req, CancellationToken ct)
{
// Your logic here
}
}
- Create validator (optional):
public class MyRequestValidator : Validator<TRequest>
{
public MyRequestValidator()
{
RuleFor(x => x.Property).NotEmpty();
}
}
- Endpoint auto-discovered on startup - no registration needed!
public record MyCommand(string Data) : ICommand;
public class MyCommandHandler : ICommandHandler<MyCommand>
{
public async Task ExecuteAsync(MyCommand cmd, CancellationToken ct)
{
// Side effect logic (email, external API, etc.)
}
}
await new MyEvent(data).PublishAsync(Mode.WaitForAll, ct);
When contributing, follow these patterns:
- Keep endpoints focused on single responsibility
- Use rich domain models with private setters
- Separate side effects into commands
- Use events for cross-cutting concerns
- Write validators for all request types
- Include Swagger summaries for documentation