FastEndpoints: The REPR Pattern for .NET APIs
Ditch bloated controllers. Build high-performance, maintainable APIs using the Request-Endpoint-Response (REPR) pattern with FastEndpoints.
Introduction
BlueRobin’s API started as a traditional ASP.NET Core controller-based project. Within three months, I had a DocumentsController with 14 action methods, 6 injected dependencies, and over 800 lines of code. Every new endpoint required scrolling past unrelated methods. Switching to FastEndpoints was like going from a cluttered open-plan office to private offices—each endpoint got its own space, its own dependencies, and its own tests.
For years, ASP.NET Core developers have been stuck in the “Controller” trap. You have a DocumentsController that grows to 2,000 lines of code, managing Dependencies for Reading, Writing, Deleting, and Searching documents. It’s a violation of the Single Responsibility Principle
[Clean Architecture]
— Robert C. Martin , 2012 .
FastEndpoints [FastEndpoints Documentation] — Dhananjay Kumar , 2024 implements the REPR Pattern (Request-Endpoint-Response) [REPR Design Pattern] — Steve Smith , 2024 . Every endpoint is a distinct class. It’s like having a Controller with exactly one method. This naturally guides you toward Vertical Slice Architecture [Vertical Slice Architecture] — Jimmy Bogard , 2018 .
Why FastEndpoints Matters:
- Performance: It’s faster than MVC Controllers and uses less memory [ASP.NET Core Performance Benchmarks] — TechEmpower , 2024 .
- Organization: Related logic (Request, Response, Validation, Handler) stays together in one file or folder.
- No More Bloat: You never need
privatehelper methods shared by 10 unrelated actions.
What We’ll Build
In this guide, we will replace a traditional Controller action with a FastEndpoint. We will cover:
- The Request DTO: Binding data from JSON, Route, or Query.
- The Validator: FluentValidation integration without boilerplate.
- The Endpoint: Implementing the handler logic.
- The Response: Sending typed responses with 200/201/400 status codes.
Architecture Overview
flowchart LR
subgraph Client["📱 Client"]
Req["POST /api/documents"]
end
subgraph Server["⚡ FastEndpoints Pipeline"]
DTO["📦 Request DTO\n(Binding)"] --> Val["🛡️ Validator\n(FluentValidation)"]
Val --> EP["💼 Endpoint Class\n(Handler)"]
EP --> Dom["🧱 Domain Logic\n(Service/Aggregate)"]
Dom --> EP
EP --> Resp["📤 Response DTO"]
end
Client --> DTO
Resp --> Client
classDef primary fill:#7c3aed,color:#fff
classDef secondary fill:#06b6d4,color:#fff
classDef db fill:#f43f5e,color:#fff
classDef warning fill:#fbbf24,color:#000
class Client warning
class Server primary
class EP primary
Implementation
The Request & Validation
FastEndpoints has FluentValidation [FluentValidation Documentation] — Jeremy Skinner , 2024 built-in. You don’t need to register validators manually; it scans and registers them automatically.
// 1. The Request DTO
public sealed record CreateDocumentRequest
{
public string Title { get; init; }
public IFormFile File { get; init; }
public string[] Tags { get; init; }
}
// 2. The Validator
public sealed class CreateDocumentValidator : Validator<CreateDocumentRequest>
{
public CreateDocumentValidator()
{
RuleFor(x => x.Title)
.NotEmpty()
.MaximumLength(100)
.WithMessage("Title prevents chaos.");
RuleFor(x => x.File)
.NotNull()
.Must(f => f.Length > 0)
.WithMessage("Cannot upload empty air.");
}
}
The Endpoint
The Endpoint class replaces the Controller Action. It inherits from Endpoint<TRequest, TResponse>.
public sealed class CreateDocumentEndpoint : Endpoint<CreateDocumentRequest, CreateDocumentResponse>
{
private readonly IDocumentService _service;
private readonly IUserContext _user;
public CreateDocumentEndpoint(IDocumentService service, IUserContext user)
{
_service = service;
_user = user;
}
public override void Configure()
{
Post("/api/documents");
AllowFileUploads(); // Helper for multipart/form-data
Policies("StartProcessing"); // Auth Policy
Summary(s =>
{
s.Summary = "Uploads a new document";
s.Description = "Uploads a file and starts the OCR process.";
s.Response<CreateDocumentResponse>(201, "Document created successfully");
});
}
public override async Task HandleAsync(CreateDocumentRequest req, CancellationToken ct)
{
// 1. Call Domain Logic
var result = await _service.UploadAsync(
ownerId: _user.CustomId,
title: req.Title,
stream: req.File.OpenReadStream(),
ct: ct
);
// 2. Map Response
var response = new CreateDocumentResponse
{
DocumentId = result.Id.Value,
Status = "Processing",
EstimatedTime = "5 seconds"
};
// 3. Send 201 Created
await SendCreatedAtAsync<GetDocumentEndpoint>(
new { id = result.Id.Value },
response,
cancellation: ct
);
}
}
Vertical Slice Organization
Files should be grouped by Feature, not by Type.
✅ Do this:
/Features
/Documents
/Create
CreateDocumentEndpoint.cs
CreateDocumentRequest.cs
CreateDocumentValidator.cs
CreateDocumentResponse.cs
/Get
GetDocumentEndpoint.cs
❌ NOT this:
/Controllers
DocumentsController.cs
/Models
CreateDocumentRequest.cs
/Validators
CreateDocumentValidator.cs
Conclusion
FastEndpoints removes the friction from API development. It gives you the raw speed of Minimal APIs with the structure and discipline required for large enterprise systems.
After migrating BlueRobin’s API from controllers to FastEndpoints, the codebase went from 3 controller files averaging 600 lines each to 45 endpoint files averaging 40 lines each. The total line count barely changed, but the navigability and testability improved dramatically. Every endpoint is independently testable, independently deployable (conceptually), and independently understandable. If you are starting a new .NET API project, I strongly recommend starting with FastEndpoints from day one rather than retrofitting later.
Next Steps:
- Combine this with Domain Events to trigger processing after the API response.
- Learn how Aggregates handle the actual logic inside
_service.UploadAsync. - See the Shared Kernel that provides the base types used across all endpoints.
Further Reading
- [FastEndpoints Documentation] — Dhananjay Kumar , 2024 — The official documentation covering all features including versioning, rate limiting, and Swagger integration.
- [Vertical Slice Architecture] — Jimmy Bogard , 2018 — Jimmy Bogard’s influential post on organizing code by feature rather than technical layer.
- [REPR Design Pattern] — Steve Smith , 2024 — The Request-Endpoint-Response pattern that FastEndpoints implements, explained by Steve Smith.