WilderMinds.MinimalApiDiscovery 1.0.6

Suggested Alternatives

MinimalApis.Discovery

Additional Details

Renaming of Package to better align with other Minimal API packages.

dotnet add package WilderMinds.MinimalApiDiscovery --version 1.0.6
NuGet\Install-Package WilderMinds.MinimalApiDiscovery -Version 1.0.6
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="WilderMinds.MinimalApiDiscovery" Version="1.0.6" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add WilderMinds.MinimalApiDiscovery --version 1.0.6
#r "nuget: WilderMinds.MinimalApiDiscovery, 1.0.6"
#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.
// Install WilderMinds.MinimalApiDiscovery as a Cake Addin
#addin nuget:?package=WilderMinds.MinimalApiDiscovery&version=1.0.6

// Install WilderMinds.MinimalApiDiscovery as a Cake Tool
#tool nuget:?package=WilderMinds.MinimalApiDiscovery&version=1.0.6

MinimalApiDiscovery

This project is aimed to simplify the registration of Minimal APIs as projects grow. This is an idea I've been fumbling with for a few months and thought I'd put it into code. The project is open to PRs or discussions about how we could do this better or whether this even needs to exist.

Note that if you're building Microservices, using this amount of plumbing is probably not required, but for larger projects I think provides a cleaner way of handling mapping.

The basic idea of this small library is to allow you to annotate a class with an interface that allows simplified mapping of Minimal APIs.

Note: I have a complete write-up and video of this package at: https://wildermuth.com/2023/02/22/minimal-api-discovery/

To get started, you can just install the package from Nuget or using the .NET tool:

> dotnet add package WilderMinds.MinimalApiDiscovery

Or:

> Install-Package WilderMinds.MinimalApiDiscovery

To use the package, you can create API classes that implement the IApi interface:

/// <summary>
/// An interface for Identifying and registering APIs
/// </summary>
public interface IApi
{
  /// <summary>
  /// This is automatically called by the library to add your APIs
  /// </summary>
  /// <param name="builder">The IEndpointRouteBuilder object to register the API </param>
  void Register(IEndpointRouteBuilder builder);
}

This allows you to create classes that can bundle several different APIs together or even use .NET 7's Minimal API Grouping. For example, a simple API class might be:

using WilderMinds.MinimalApiDiscovery;

namespace UsingMinimalApiDiscovery.Apis;

public class StateApi : IApi
{
  public void Register(IEndpointRouteBuilder builder)
  {
    builder.MapGet("/api/states", (StateCollection states) =>
    {
      return states;
    });
  }
}

Within the Register call, you can simply create your mapped APIs. But you can also use non-lambdas if that is easier (though I suggest static methods to prevent usage of instance data that will become a singleton):

using WilderMinds.MinimalApiDiscovery;

namespace UsingMinimalApiDiscovery.Apis;

public class CustomerApi : IApi
{
  public void Register(IEndpointRouteBuilder builder)
  {
    var grp = builder.MapGroup("/api/customers");
    grp.MapGet("", GetCustomers);
    grp.MapGet("", GetCustomer);
    grp.MapPost("{id:int}", SaveCustomer);
    grp.MapPut("{id:int}", UpdateCustomer);
    grp.MapDelete("{id:int}", DeleteCustomer);
  }

  // Using static methods to ensure that the class doesn't hold state
  public static async Task<IResult> GetCustomers(CustomerRepository repo)
  {
    return Results.Ok(await repo.GetCustomers());
  }

  public static async Task<IResult> GetCustomer(CustomerRepository repo, int id)
  {
    return Results.Ok(await repo.GetCustomer(id));
  }

  public static async Task<IResult> SaveCustomer(CustomerRepository repo, Customer model)
  {
    return Results.Created($"/api/customer/{model.Id}", await repo.SaveCustomer(model));
  }

  public static async Task<IResult> UpdateCustomer(CustomerRepository repo, Customer model)
  {
    return Results.Ok(await repo.UpdateCustomer(model));
  }

  public static async Task<IResult> DeleteCustomer(CustomerRepository repo, int id)
  {
    var result = await repo.DeleteCustomer(id);
    if (result) return Results.Ok();
    return Results.BadRequest();
  }
}

Note, you do not have to have to implement the APIs as static methods, but the benefit here is, when made public, you can more easily test these APIs without having to generate the API boilerplate.

In this example, I'm using a Mapping Group as well as just using methods to implement the business logic. To wire it up, jsut call MapApis() to register all the APIs:

//Program.cs
using WilderMinds.MinimalApiDiscovery;

var builder = WebApplication.CreateBuilder(args);

// ...

var app = builder.Build();

// Get all IApi dependencies and call Register on them all.
app.MapApis();

app.Run();

The MapApis method is created using a source generator (therefore is safe for AOT trimming). The call to MapApis is generated by looking at all classes that implement IApi interface. Callng this method calls register on each of the classes. This does not require any interaction with the Service Container.

Note: Prior versions allowed you to point to an assembly to search, but in this version, we're not using reflection, so that has been removed.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.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
1.0.6 3,858 1/8/2024
1.0.5 95 1/8/2024
1.0.4 326 9/5/2023
1.0.4-beta 170 3/15/2023
1.0.3-beta 171 2/28/2023
1.0.2-beta 135 2/28/2023
1.0.1-beta 133 2/28/2023
1.0.0-alpha 142 2/24/2023
0.2.2 583 3/7/2023
0.2.1 233 2/24/2023
0.2.0 247 2/22/2023
0.1.1 259 2/14/2023
0.1.0 289 2/12/2023