Endpointer 0.1.0
dotnet add package Endpointer --version 0.1.0
NuGet\Install-Package Endpointer -Version 0.1.0
<PackageReference Include="Endpointer" Version="0.1.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="Endpointer" Version="0.1.0" />
<PackageReference Include="Endpointer"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Endpointer --version 0.1.0
#r "nuget: Endpointer, 0.1.0"
#:package Endpointer@0.1.0
#addin nuget:?package=Endpointer&version=0.1.0
#tool nuget:?package=Endpointer&version=0.1.0
Endpointer
A C# source generator for ASP.NET Core Minimal APIs implementing the REPR (Request-Endpoint-Response) pattern.
Endpointer is a thin layer above ASP.NET Core - not a framework. It generates the boilerplate at compile time with no reflection, while you keep full control over your endpoints.
Features
- Zero runtime overhead - All code is generated at compile time
- REPR pattern - Clean separation with Request, Endpoint, and Response in one file
- Automatic DI registration - Primary constructor parameters are auto-registered
- Automatic route mapping - All endpoints discovered and mapped via source generation
- Native ASP.NET Core - Uses
TypedResults,IEndpointRouteBuilder, and standard middleware - Incremental generator - Fast builds with Roslyn's latest
IIncrementalGeneratorAPI - No reflection - Everything resolved at compile time
Quick Start
1. Install the package
dotnet add package Endpointer
2. Create an endpoint
using Microsoft.AspNetCore.Http.HttpResults;
public class GetTimeEndpoint(TimeProvider timeProvider)
{
public record GetTimeResponse(DateTimeOffset CurrentTime);
public class Endpoint : IEndpoint
{
public void MapEndpoint(IEndpointRouteBuilder endpoints)
{
endpoints.MapGet("/time", (GetTimeEndpoint ep) => ep.Handle());
}
}
public Ok<GetTimeResponse> Handle()
{
return TypedResults.Ok(new GetTimeResponse(timeProvider.GetUtcNow()));
}
}
3. Register in Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointer(); // Generated - registers all endpoint classes
var app = builder.Build();
app.MapEndpointer(); // Generated - maps all endpoints
await app.RunAsync();
That's it! The source generator discovers all IEndpoint implementations and generates the registration code.
How It Works
Endpointer uses Roslyn's incremental source generator to:
- Discover endpoints - Finds all nested classes implementing
IEndpoint - Extract metadata - Captures the outer class name and its primary constructor parameters
- Generate registration - Creates extension methods for DI and route mapping
Generated Code
The generator produces two files:
IEndpoint.g.cs - The marker interface:
public interface IEndpoint
{
void MapEndpoint(IEndpointRouteBuilder endpoints);
}
EndpointerRegistration.g.cs - Extension methods:
public static class EndpointerExtensions
{
public static IServiceCollection AddEndpointer(this IServiceCollection services)
{
services.AddScoped<GetTimeEndpoint>();
services.AddScoped<GetUserEndpoint>();
services.AddScoped<CreateUserEndpoint>();
// ... all discovered endpoints
return services;
}
public static IEndpointRouteBuilder MapEndpointer(this IEndpointRouteBuilder endpoints)
{
new GetTimeEndpoint.Endpoint().MapEndpoint(endpoints);
new GetUserEndpoint.Endpoint().MapEndpoint(endpoints);
new CreateUserEndpoint.Endpoint().MapEndpoint(endpoints);
// ... all discovered endpoints
return endpoints;
}
}
The REPR Pattern
REPR (Request-Endpoint-Response) organizes API code by feature rather than by layer:
Endpoints/
├── Users/
│ ├── CreateUserEndpoint.cs # POST /users
│ ├── GetUserEndpoint.cs # GET /users/{id}
│ ├── UpdateUserEndpoint.cs # PUT /users/{id}
│ └── DeleteUserEndpoint.cs # DELETE /users/{id}
└── Health/
└── HealthEndpoint.cs # GET /health
Each file contains:
- Request - Input DTOs (records)
- Endpoint - Route mapping (nested
IEndpointclass) - Response - Output DTOs (records)
- Handler - Business logic (methods on outer class)
Requirements
- .NET 10.0 or later (for the application)
- The generator itself targets netstandard2.0 for broad compatibility
Building
# Build
dotnet build src/Endpointer.slnx
# Test
dotnet test --solution src/Endpointer.slnx
Full Example
A complete endpoint with request/response DTOs, dependency injection, and OpenAPI metadata:
using Microsoft.AspNetCore.Http.HttpResults;
namespace MyApi.Endpoints.Products;
public class CreateProductEndpoint(IProductRepository repository, ILogger<CreateProductEndpoint> logger)
{
// Request
public record CreateProductRequest(
string Name,
string Description,
decimal Price,
string Category);
// Response
public record ProductResponse(
int Id,
string Name,
string Description,
decimal Price,
string Category,
DateTimeOffset CreatedAt);
// Endpoint
public class Endpoint : IEndpoint
{
public void MapEndpoint(IEndpointRouteBuilder endpoints)
{
endpoints.MapPost("/products", (CreateProductEndpoint ep, CreateProductRequest request) => ep.HandleAsync(request))
.WithName("CreateProduct")
.WithTags("Products")
.WithSummary("Create a new product")
.WithDescription("Creates a new product in the catalog and returns the created product with its assigned ID.")
.Produces<ProductResponse>(StatusCodes.Status201Created)
.ProducesValidationProblem()
.WithOpenApi();
}
}
// Handler
public async Task<Results<Created<ProductResponse>, ValidationProblem>> HandleAsync(CreateProductRequest request)
{
if (request.Price < 0)
{
return TypedResults.ValidationProblem(new Dictionary<string, string[]>
{
["Price"] = ["Price must be greater than or equal to zero."]
});
}
logger.LogInformation("Creating product {Name} in category {Category}", request.Name, request.Category);
var product = await repository.CreateAsync(request);
var response = new ProductResponse(
product.Id,
product.Name,
product.Description,
product.Price,
product.Category,
product.CreatedAt);
return TypedResults.Created($"/products/{response.Id}", response);
}
}
License
This project is licensed under the MIT License - see the LICENSE file for details.
Learn more about Target Frameworks and .NET Standard.
This package has 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 |
|---|---|---|
| 0.1.0 | 36 | 1/24/2026 |