Svrooij.EndpointMapper 0.2.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package Svrooij.EndpointMapper --version 0.2.2
                    
NuGet\Install-Package Svrooij.EndpointMapper -Version 0.2.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Svrooij.EndpointMapper" Version="0.2.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Svrooij.EndpointMapper" Version="0.2.2" />
                    
Directory.Packages.props
<PackageReference Include="Svrooij.EndpointMapper" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Svrooij.EndpointMapper --version 0.2.2
                    
#r "nuget: Svrooij.EndpointMapper, 0.2.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Svrooij.EndpointMapper@0.2.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Svrooij.EndpointMapper&version=0.2.2
                    
Install as a Cake Addin
#tool nuget:?package=Svrooij.EndpointMapper&version=0.2.2
                    
Install as a Cake Tool

Svrooij.EndpointMapper

A .NET source generator that automatically discovers and registers API endpoints in ASP.NET Core applications using a clean, organized approach.

Overview

This source generator provides a simple way to organize your API endpoints into separate classes while automatically generating the registration code. It scans for classes implementing the IMapEndpoint interface and generates an extension method to register all endpoints at once.

Features

  • 🚀 Automatic Discovery: Finds all classes implementing IMapEndpoint in your project
  • 🔧 Source Generation: Generates registration code at compile time
  • 📁 Clean Organization: Keep endpoints in separate files/classes
  • Zero Runtime Overhead: All discovery happens at compile time
  • 🎯 Project-Specific: Generates unique extension methods per project
  • 📃 Dto mapping: In web api projects, it is very common to be able to select which fields you want to return.

Installation

Install the NuGet package in your ASP.NET Core project:

dotnet add package Svrooij.EndpointMapper

Or add it manually to your project file:

<ItemGroup>
  <PackageReference Include="Svrooij.EndpointMapper" Version="1.0.0" 
                    OutputItemType="Analyzer" 
                    ReferenceOutputAssembly="false" />
</ItemGroup>

Usage

1. Create Endpoint Classes

Create classes that implement the IMapEndpoint interface (needs a constructor without parameters):

using Svrooij.EndpointMapper;

namespace MyApi.Endpoints;

public class WeatherEndpoint : IMapEndpoint
{
    public void MapEndpoint(IEndpointRouteBuilder app)
    {
        app.MapGet("/weather", () => "Sunny")
           .WithName("GetWeather")
           .WithOpenApi();
           
        app.MapPost("/weather", (WeatherRequest request) => 
            Results.Ok($"Weather updated to {request.Description}"))
           .WithName("UpdateWeather")
           .WithOpenApi();
    }
}

public class UserEndpoint : IMapEndpoint
{
    public void MapEndpoint(IEndpointRouteBuilder app)
    {
        app.MapGet("/users", () => new[] { "Alice", "Bob" })
           .WithName("GetUsers")
           .WithOpenApi();
    }
}

2. Register All Endpoints

In your Program.cs, the source generator will create an extension method named MapEndpointFrom{YourProjectName}():

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();

// This method is generated automatically
app.MapEndpointFromMyApi(); // Replace "MyApi" with your actual project name

app.Run();

How It Works

  1. Interface Implementation: You implement IMapEndpoint in your endpoint classes
  2. Compile-Time Discovery: The source generator scans your project for implementing classes
  3. Code Generation: An extension method is generated with code to instantiate and register all endpoints
  4. Registration: Call the generated extension method in your Program.cs

Generated Code Example

For a project named "MyApi" with two endpoint classes, the generator creates:

using Microsoft.AspNetCore.Builder;

namespace Svrooij.EndpointMapper;

public static class EndpointMapperExtensions
{
    public static WebApplication MapEndpointFromMyApi(this WebApplication app)
    {
        new MyApi.Endpoints.WeatherEndpoint().MapEndpoint(app);
        new MyApi.Endpoints.UserEndpoint().MapEndpoint(app);
        return app;
    }
}

Benefits

Clean Separation of Concerns

Each endpoint class handles its own routing logic, making code more maintainable and testable.

Automatic Registration

No need to manually register each endpoint class - the source generator handles it automatically.

Compile-Time Safety

All endpoint discovery happens at compile time, so you'll know immediately if there are issues.

IntelliSense Support

The generated extension method appears in IntelliSense with full type safety.

Performance

Zero runtime overhead - all reflection and discovery happens at compile time.

DTO Mapping Support

If you add an attribute to your DTO with the source type, the generator will create an extension method to map from the source type to the DTO type. This method allows you to specify which field should be mapped, non-nullable properties are always mapped.

// The source type
public class User
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public required string Email { get; set; }
}

// The DTO with the attribute
[Svrooij.EndpointMapper.GenerateSelect(typeof(User))]
public class UserDto
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public string? Email { get; set; }
}

// This will generate the following extension method for use with Enttity Framework:
public static IQueryable<UserDto> SelectUserDto(this IQueryable<User> query, string properties)
// And this extension just to map individual objects
public static UserDto SelectUserDto(this User entity, string properties)

These extensions are not using linq expressions but in fact pre-generated code that boils down to the following:

/// <summary>
/// Maps a single User instance to UserDto using a pre-parsed bitmask.
/// This internal method is used by the IQueryable extension to avoid parsing the properties string multiple times.
/// </summary>
/// <param name="entity">The User instance</param>
/// <param name="selectedProps">Bitmask of selected properties</param>
/// <returns>A UserDto instance with selected properties populated</returns>
internal static UserDto SelectUserDto(this WebApiWithEndpointMapper.Endpoints.User entity, long selectedProps)
{
    return new WebApiWithEndpointMapper.Endpoints.UserDto
    {
        Id = entity.Id,
        Name = (selectedProps & NameFlag) != 0 ? entity.Name : null,
        Email = (selectedProps & EmailFlag) != 0 ? entity.Email : null,
    };
}

/// <summary>
/// Parses the properties string and returns a bitmask of selected properties.
/// </summary>
private static long ParseProperties(string properties)
{
    long flags = 0;
    if (string.IsNullOrEmpty(properties)) return flags;

    var selected = new System.Collections.Generic.HashSet<string>(
        properties
        .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
        .Select(p => p.ToLowerInvariant())
        .Where(p => ValidProperties.Contains(p)),
        System.StringComparer.OrdinalIgnoreCase
    );

    if (selected.Contains("name")) flags |= NameFlag;
    if (selected.Contains("email")) flags |= EmailFlag;

    return flags;
}

Project Structure Example

MyApi/
├── Program.cs
├── Endpoints/
│   ├── WeatherEndpoint.cs
│   ├── UserEndpoint.cs
│   └── ProductEndpoint.cs
└── Models/
    ├── WeatherRequest.cs
    └── UserModel.cs

Requirements

  • .NET 8.0 or later

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.

There are no supported framework assets in this package.

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
0.3.2 217 12/19/2025
0.3.1 200 11/28/2025
0.3.0 171 11/7/2025
0.2.2 207 11/5/2025
0.2.1 142 11/1/2025
0.1.0 207 10/27/2025