Skip to content

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.

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 GET
app.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);

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.

Terminal window
dotnet add package Pragmatic.Endpoints
dotnet add package Pragmatic.Endpoints.AspNetCore # ASP.NET Core hosting integration
dotnet add package Pragmatic.SourceGenerator # the unified analyzer

(Building inside this monorepo? See Monorepo Structure.)

Pick the base class that matches the endpoint’s result:

Base classForReturns
VoidEndpointFire-and-forget commandsVoidResult → 204
Endpoint<TResponse>Always-succeeds readsTResponse → 200
Endpoint<TResponse, TError>Operations that can failResult<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.

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 (HandleAsyncV2 convention, [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.

The endpoint pipeline, binding, groups, versioning, rate-limiting, caching, and DomainAction/Query/ Mutation integration are functional within the 0.8 preview. See the roadmap.

GuideWhat you will learn
Architecture and ConceptsMental model, pipeline lifecycle, decision tree for endpoint types
Getting StartedFirst endpoint from zero to HTTP response in 5 minutes
GuideWhat it covers
Binding ReferenceRoute, query, header, body, form, claim binding — all scenarios
Endpoint GroupsShared route prefix, tags, auth, nesting
Endpoint ProcessorsPre/post processor pipeline, ordering, short-circuit
API VersioningHandleAsyncV2 convention, SinceVersion, versioned body DTOs
Rate LimitingInline, named policies, all 4 strategies, distributed
Response CachingOutput cache, VaryBy, distributed via Pragmatic.Caching
File Upload and DownloadIFormFile, validation, FileResponse, ETag, range requests
Error HandlingResult types, ProblemDetails RFC 7807, custom errors
DomainAction IntegrationDomainAction pipeline, invoker, convention versioning
Query and Mutation EndpointsQuery filters, Mutation CRUD, pagination, autocomplete
GuideWhen to use
Common MistakesWrong code → right code for the most common pitfalls
TroubleshootingChecklists for 404s, binding issues, auth failures, diagnostics

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.

  • .NET 10.0+
  • ASP.NET Core 10.0+ (for Pragmatic.Endpoints.AspNetCore)
  • Pragmatic.SourceGenerator analyzer

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