GeneratedEndpoints 9.0.0-preview.1
See the version list below for details.
dotnet add package GeneratedEndpoints --version 9.0.0-preview.1
NuGet\Install-Package GeneratedEndpoints -Version 9.0.0-preview.1
<PackageReference Include="GeneratedEndpoints" Version="9.0.0-preview.1" />
<PackageVersion Include="GeneratedEndpoints" Version="9.0.0-preview.1" />
<PackageReference Include="GeneratedEndpoints" />
paket add GeneratedEndpoints --version 9.0.0-preview.1
#r "nuget: GeneratedEndpoints, 9.0.0-preview.1"
#:package GeneratedEndpoints@9.0.0-preview.1
#addin nuget:?package=GeneratedEndpoints&version=9.0.0-preview.1&prerelease
#tool nuget:?package=GeneratedEndpoints&version=9.0.0-preview.1&prerelease
GeneratedEndpoints - Attribute-driven, source-generated minimal API endpoints for feature-based development
GeneratedEndpoints is a .NET source generator that automatically wires up Minimal API endpoints from attribute-annotated methods. This simplifies integration of HTTP handlers within Clean Architecture (CA) or Vertical Slice Architecture (VSA) by keeping endpoint definitions inside their features while generating the boilerplate mapping code.
Getting Started
Installation
Add the package to the Minimal API project that will host your endpoints. You can install it with the .NET CLI:
dotnet add package GeneratedEndpoints
Once the package is referenced, the source generator will contribute its attributes and extension methods to the consuming project at build time.
1. Define a request handler
Create a feature class that encapsulates the logic for a single endpoint and decorate its handler method with one of the generated HTTP verb attributes. The attributes live in the Microsoft.AspNetCore.Generated.Attributes namespace and map directly to Minimal API routing methods.
Handler classes can be expressed in whichever style best fits the feature:
- Instance classes (non-static) allow constructor injection and can expose either instance or static handler methods. When an annotated method is not static the generator will call it on a resolved instance from dependency injection.
- Static classes make it easy to group stateless functionality. Every annotated method inside must also be static, mirroring standard C# rules.
Instance handler example
using Microsoft.AspNetCore.Generated.Attributes;
using Microsoft.AspNetCore.Http.HttpResults;
namespace Todos.Features;
public sealed class GetTodo
{
private readonly TodoDbContext _db;
public GetTodo(TodoDbContext db) => _db = db;
[MapGet("/todos/{id}", Summary = "Retrieve a todo", Description = "Returns the todo matching the provided identifier.")]
[Tags("Todos")]
[RequireAuthorization("Todos.Read")]
public async Task<Results<Ok<Todo>, NotFound>> HandleAsync(Guid id, CancellationToken cancellationToken)
{
var entity = await _db.Todos.FindAsync(new object?[] { id }, cancellationToken);
return entity is null ? TypedResults.NotFound() : TypedResults.Ok(entity);
}
}
Key points:
- Use
[MapGet],[MapPost],[MapPut],[MapDelete],[MapPatch],[MapHead],[MapOptions],[MapTrace], or[MapConnect]to describe the HTTP verb and route pattern. - Optional
Name,Summary, andDescriptionnamed parameters populate the generated.WithName,.WithSummary, and.WithDescriptionmetadata calls. When omitted, the generator derives the endpoint name from the method name (stripping a trailingAsync). - Apply standard ASP.NET Core parameter binding attributes (
[FromRoute],[FromQuery],[FromBody],[FromServices],[AsParameters], etc.). The generator mirrors them onto the produced delegate so binding behaves exactly as declared. - Annotate the class, an individual method, or both with
[Tags],[RequireAuthorization], or[DisableAntiforgery]. Class-level metadata is merged onto every generated endpoint, while method-level attributes can refine or augment the settings for a specific handler. - Non-static handler classes are automatically registered with dependency injection (as transient services). Their instance methods receive a scoped instance resolved from DI, while static methods continue to behave like any other static helper.
Static handler example
The same attribute-driven approach works for static handler types when no dependencies are needed:
using Microsoft.AspNetCore.Generated.Attributes;
using Microsoft.AspNetCore.Http.HttpResults;
namespace Todos.Features;
public static class ListTodos
{
[MapGet("/todos")]
[Tags("Todos")]
public static Ok<IReadOnlyList<Todo>> Handle()
=> TypedResults.Ok(TodoStore.All);
}
2. Wire up the application
The generator emits extension methods in the Microsoft.AspNetCore.Generated.Routing namespace. Call them during startup to register handler types and map the generated endpoints.
using Microsoft.AspNetCore.Generated.Routing;
var builder = WebApplication.CreateBuilder(args);
// Registers non-static handler classes with the DI container.
builder.Services.AddEndpointHandlers();
var app = builder.Build();
// Maps every method decorated with a Map* attribute.
app.MapEndpointHandlers();
app.Run();
AddEndpointHandlers ensures any non-static handler types can be resolved from dependency injection, while MapEndpointHandlers generates Minimal API route mappings for every annotated method in the application.
3. Compose additional endpoints
Add as many handler classes as needed—each annotated method becomes an endpoint. You can mix synchronous and asynchronous methods, return IResult or typed Results<>, and combine static and instance handlers in the same project. Metadata from attributes composes naturally: class-level attributes are applied to every endpoint, while method-level attributes add to (or override, when relevant) the defaults.
using Microsoft.AspNetCore.Generated.Attributes;
using Microsoft.AspNetCore.Http.HttpResults;
namespace Todos.Features;
[Tags("Todos")]
[RequireAuthorization("Todos.Read")]
public sealed class TodoEndpoints
{
private readonly TodoDbContext _db;
public TodoEndpoints(TodoDbContext db) => _db = db;
[MapGet("/todos/{id}", Summary = "Retrieve a todo")]
public async Task<Results<Ok<Todo>, NotFound>> GetAsync(Guid id, CancellationToken cancellationToken)
{
var entity = await _db.Todos.FindAsync(new object?[] { id }, cancellationToken);
return entity is null ? TypedResults.NotFound() : TypedResults.Ok(entity);
}
[MapDelete("/todos/{id}")]
[RequireAuthorization("Todos.Write")]
public static async Task<Results<NoContent, NotFound>> DeleteAsync(
Guid id,
[FromServices] TodoDbContext db,
CancellationToken cancellationToken)
{
var entity = await db.Todos.FindAsync(new object?[] { id }, cancellationToken);
if (entity is null)
return TypedResults.NotFound();
db.Todos.Remove(entity);
await db.SaveChangesAsync(cancellationToken);
return TypedResults.NoContent();
}
}
In this example:
- The class-level
[Tags]and[RequireAuthorization]attributes apply to both endpoints, while the method-level[RequireAuthorization]adds an additional policy for the delete handler. GetAsyncis an instance method that uses the injectedTodoDbContextfield, illustrating how non-static handlers can maintain state.DeleteAsyncis a static method in the same class and explicitly receives its dependencies via[FromServices], demonstrating that you can mix static and instance methods in a single handler type.
Every new handler will automatically appear in the generated routing table the next time the project builds—no manual MapGet, MapPost, or registration code is required.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.3 | 779 | 12/22/2025 |
| 10.0.2 | 1,150 | 11/17/2025 |
| 10.0.1 | 325 | 11/17/2025 |
| 10.0.0 | 242 | 11/16/2025 |
| 9.0.0-preview.1 | 307 | 9/19/2025 |
