Skip to content

danielmackay/dotnet-fast-endpoints

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

17 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

FastEndpoints Web API

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.

πŸ—οΈ Architecture

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)

Key Design Patterns

  • 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

πŸš€ Getting Started

Prerequisites

  • .NET 10 SDK or later
  • Any IDE (Visual Studio 2022, VS Code, Rider)

Installation

  1. Clone the repository:
git clone <repository-url>
cd FastEndpoints
  1. Restore dependencies:
dotnet restore
  1. Run the application:
dotnet run --project FastWebApi/FastWebApi.csproj

The API will start on:

  • HTTPS: https://localhost:7038
  • HTTP: http://localhost:5128
  1. Open Swagger UI in your browser:
https://localhost:7038/swagger

πŸ“‘ API Endpoints

Monkeys API

All monkey endpoints are grouped under /api/monkeys:

Get All Monkeys

GET /api/monkeys

Response (200 OK):

{
  "monkeys": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Bubbles",
      "age": 4,
      "temperament": "Playful"
    }
  ]
}

Create Monkey

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 successfully
  • 400 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 Monkey

DELETE /api/monkeys/{id}

Responses:

  • 204 No Content - Monkey deleted successfully
  • 404 Not Found - Monkey not found

Example with cURL:

curl -X DELETE https://localhost:7038/api/monkeys/3fa85f64-5717-4562-b3fc-2c963f66afa6

πŸ§ͺ Testing the API

Using Swagger UI

  1. Navigate to https://localhost:7038/swagger
  2. Expand any endpoint
  3. Click "Try it out"
  4. Fill in parameters and click "Execute"

Using HTTP Files (VS Code/Rider)

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

πŸ›οΈ Project Structure

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

Application Settings

Configuration is managed through appsettings.json and appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

Launch Profiles

Edit Properties/launchSettings.json to customize:

  • Port numbers
  • Environment variables
  • Launch behavior (browser, URLs, etc.)

πŸ“¦ Dependencies

  • FastEndpoints (7.0.1) - High-performance endpoint library
  • FastEndpoints.Swagger (7.0.1) - OpenAPI/Swagger integration
  • .NET 10 - Target framework

🎯 Features

  • βœ… 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

πŸ› οΈ Development Guidelines

Adding a New Endpoint

  1. 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
    }
}
  1. Create validator (optional):
public class MyRequestValidator : Validator<TRequest>
{
    public MyRequestValidator()
    {
        RuleFor(x => x.Property).NotEmpty();
    }
}
  1. Endpoint auto-discovered on startup - no registration needed!

Adding a Command

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.)
    }
}

Publishing Events

await new MyEvent(data).PublishAsync(Mode.WaitForAll, ct);

πŸ“ Additional Resources

🀝 Contributing

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

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages