Pragmatic.Endpoints
Source-generated HTTP endpoints for ASP.NET Core. Declare the shape; the generator writes the plumbing — binding, DI, authorization, error-to-HTTP mapping, and OpenAPI metadata — at compile time, zero reflection.
The Problem
Section titled “The Problem”Every ASP.NET Core endpoint repeats the same ceremony: parse route params, bind the body, validate, check authorization, call business logic, map errors to status codes, configure OpenAPI. Minimal APIs make you write it by hand; Controllers inherit it but cost flexibility. Either way the plumbing-to-logic ratio grows with every endpoint.
// Without Pragmatic: 40+ lines of plumbing for a simple GETapp.MapGet("/api/products/{id}", async (Guid id, IProductRepository repo, IAuthorizationService auth, HttpContext ctx, CancellationToken ct) =>{ if (!(await auth.AuthorizeAsync(ctx.User, "products.read")).Succeeded) return Results.Forbid(); var product = await repo.GetByIdAsync(id, ct); if (product is null) return Results.NotFound(new ProblemDetails { /* ... */ }); return Results.Ok(ProductDto.FromEntity(product));}).WithName("GetProduct").WithTags("Products").Produces<ProductDto>(200).ProducesProblem(404);The Solution
Section titled “The Solution”You declare what the endpoint does; the generator handles how.
[Endpoint(HttpVerb.Get, "/api/products/{id}")][RequirePermission("products.read")]public partial class GetProduct : Endpoint<ProductDto, NotFoundError>{ private IProductRepository _products = null!; // injected by the generator
[FromRoute] public Guid Id { get; set; }
public override async Task<Result<ProductDto, NotFoundError>> HandleAsync(CancellationToken ct) { var product = await _products.GetByIdAsync(Id, ct); return product is not null ? ProductDto.FromEntity(product) : new NotFoundError("Product", Id.ToString()); // → 404 ProblemDetails }}The generator produces the route registration, parameter binding, DI wiring, authorization enforcement, error-to-HTTP mapping, and OpenAPI metadata — all at compile time.
Installation
Section titled “Installation”dotnet add package Pragmatic.Endpointsdotnet add package Pragmatic.Endpoints.AspNetCore # ASP.NET Core hosting integrationdotnet add package Pragmatic.SourceGenerator # the unified analyzer(Building inside this monorepo? See Monorepo Structure.)
Quick Start
Section titled “Quick Start”Pick the base class that matches the endpoint’s result:
| Base class | For | Returns |
|---|---|---|
VoidEndpoint | Fire-and-forget commands | VoidResult → 204 |
Endpoint<TResponse> | Always-succeeds reads | TResponse → 200 |
Endpoint<TResponse, TError> | Operations that can fail | Result<TResponse, TError> → 200 / mapped error |
[Endpoint(HttpVerb.Post, "/api/cache/clear")]public partial class ClearCacheEndpoint : VoidEndpoint{ private ICacheStack _cache = null!; // injected
public override async Task<VoidResult> HandleAsync(CancellationToken ct) { await _cache.InvalidateByTagAsync("products", ct); return VoidResult.Success(); // → 204 No Content }}Properties bind from the request automatically (route param {id} → Id); typed errors map to the
right status + an RFC 7807 ProblemDetails. The generator emits MapEndpoint() (route registration),
SetDependencies() (DI), and the OpenAPI metadata. Full walkthrough:
Getting Started.
What you can declare
Section titled “What you can declare”All of these are attribute/convention-driven and documented in depth (see Documentation):
- Binding from route, query, header, body, form, and claims (
[FromRoute],[FromQuery], …). - Endpoint groups for a shared prefix, tags, and auth.
- Authorization via
[RequirePermission]/[RequirePolicy]. - API versioning (
HandleAsyncV2convention,[SinceVersion], versioned body DTOs). - Rate limiting (inline or named policies, 4 strategies, distributed) and response caching.
- File upload/download (
IFormFile,FileResponse, ETag, range requests). - Pre/post processors, and direct DomainAction / Mutation / Query endpoint integration.
Status
Section titled “Status”The endpoint pipeline, binding, groups, versioning, rate-limiting, caching, and DomainAction/Query/ Mutation integration are functional within the 0.8 preview. See the roadmap.
Documentation
Section titled “Documentation”| Guide | What you will learn |
|---|---|
| Architecture and Concepts | Mental model, pipeline lifecycle, decision tree for endpoint types |
| Getting Started | First endpoint from zero to HTTP response in 5 minutes |
Features
Section titled “Features”| Guide | What it covers |
|---|---|
| Binding Reference | Route, query, header, body, form, claim binding — all scenarios |
| Endpoint Groups | Shared route prefix, tags, auth, nesting |
| Endpoint Processors | Pre/post processor pipeline, ordering, short-circuit |
| API Versioning | HandleAsyncV2 convention, SinceVersion, versioned body DTOs |
| Rate Limiting | Inline, named policies, all 4 strategies, distributed |
| Response Caching | Output cache, VaryBy, distributed via Pragmatic.Caching |
| File Upload and Download | IFormFile, validation, FileResponse, ETag, range requests |
| Error Handling | Result types, ProblemDetails RFC 7807, custom errors |
| DomainAction Integration | DomainAction pipeline, invoker, convention versioning |
| Query and Mutation Endpoints | Query filters, Mutation CRUD, pagination, autocomplete |
| Guide | When to use |
|---|---|
| Common Mistakes | Wrong code → right code for the most common pitfalls |
| Troubleshooting | Checklists for 404s, binding issues, auth failures, diagnostics |
Samples
Section titled “Samples”Pragmatic.Endpoints.Samples (17 endpoint patterns) and the Showcase (Booking · Billing · Catalog) for real-world CRUD, groups, auth, file upload, versioning, and cross-boundary events.
Requirements
Section titled “Requirements”- .NET 10.0+
- ASP.NET Core 10.0+ (for
Pragmatic.Endpoints.AspNetCore) Pragmatic.SourceGeneratoranalyzer
License
Section titled “License”Part of the Pragmatic.Design ecosystem — see Licensing. Pragmatic.Endpoints is licensed under the PolyForm Small Business 1.0.0 license (free for small businesses; commercial license above the threshold).