Functions.Worker.AddOns.MiniApiRouting 1.1.5

dotnet add package Functions.Worker.AddOns.MiniApiRouting --version 1.1.5
                    
NuGet\Install-Package Functions.Worker.AddOns.MiniApiRouting -Version 1.1.5
                    
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="Functions.Worker.AddOns.MiniApiRouting" Version="1.1.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Functions.Worker.AddOns.MiniApiRouting" Version="1.1.5" />
                    
Directory.Packages.props
<PackageReference Include="Functions.Worker.AddOns.MiniApiRouting" />
                    
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 Functions.Worker.AddOns.MiniApiRouting --version 1.1.5
                    
#r "nuget: Functions.Worker.AddOns.MiniApiRouting, 1.1.5"
                    
#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 Functions.Worker.AddOns.MiniApiRouting@1.1.5
                    
#: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=Functions.Worker.AddOns.MiniApiRouting&version=1.1.5
                    
Install as a Cake Addin
#tool nuget:?package=Functions.Worker.AddOns.MiniApiRouting&version=1.1.5
                    
Install as a Cake Tool

Functions.Worker.AddOns.MiniApiRouting

Compile-time generated Mini APIs for Azure Functions isolated worker.

When you want many API routes for one Function, and single/consolidated Function Key management, you want an Azure Funtion MiniApi!

Overview

MiniApiRouting lets one Azure Function host a logical group of HTTP routes while preserving the isolated-worker programming model. RouteHandlers are ordinary methods discovered by a source generator and dispatched through focused generated code.

One Function
One Function Key
One Authorization Boundary
Many Routes

MiniApiRouting provides compile-time route validation and routing without assembly scanning, reflection, or runtime route discovery.

Give the project a Star 🌟

If you like this project or use it, please give it a Star. It is free and helps others find the project!

Buy me a Coffee ☕

I am happy to share with the community, but if you find this useful, especially for professional use, then I do love me some coffee!

<a href="https://www.buymeacoffee.com/cajuncoding" target="_blank" rel="noopener noreferrer"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>

Why not one Function per route?

Azure Functions encourages one Function per endpoint. That works well for many APIs, but it can become cumbersome when a logical API contains several related routes that should share the same authorization boundary and Function Key.

MiniApiRouting enables:

  • One Function
  • One Function Key
  • Many routes
  • Compile-time validation
  • Compile-time dispatch generation for routing runtime performance
  • Ultra lightweight footprint with plain vanilla Azure Functions isolated-worker hosting
  • No assembly scanning, reflection, or runtime discovery
  • No runtime route-discovery cold-start penalty

All while preserving Azure Functions isolated-worker hosting, middleware, dependency injection, authorization, and keys.

Installation

dotnet add package Functions.Worker.AddOns.MiniApiRouting

The package includes the runtime library and source-generator analyzer. Consumers install only this package.

Supported frameworks:

  • Runtime: net8.0, net10.0
  • Source generator: netstandard2.0

Quick start

1. Register MiniApiRouting

builder.Services.AddFunctionsMiniApiRouting();

AddFunctionsMiniApiRouting() registers the default IMiniApiRequestBodyDeserializer and applies source-generated registrations for RouteHandler classes and IMiniApiRouter.

Generated registrations use TryAddTransient and TryAddSingleton, allowing applications to provide explicit registration to override with custom behavior when needed.

2. Define RouteHandlers

[MiniApi]
internal sealed class WidgetRouteHandlers(IWidgetService widgets)
{
    [MiniApiGet]
    public Task<IReadOnlyList<WidgetDto>> GetWidgetsAsync(CancellationToken cancellationToken = default)
        => widgets.GetWidgetsAsync(cancellationToken);

    [MiniApiGet("/{widgetId:int}")]
    public Task<WidgetDto?> GetWidgetAsync(int widgetId, string? material = null, CancellationToken cancellationToken = default)
        => widgets.GetWidgetAsync(widgetId, material, cancellationToken);

    [MiniApiPost]
    public Task<WidgetDto> CreateWidgetAsync(CreateWidgetRequest createPayload, CancellationToken cancellationToken = default)
        => widgets.CreateWidgetAsync(createPayload, cancellationToken);

    [MiniApiPut("/{widgetId:int}")]
    public Task<WidgetDto> UpdateWidgetAsync(int widgetId, UpdateWidgetRequest updatePayload, CancellationToken cancellationToken = default)
        => widgets.UpdateWidgetAsync(widgetId, updatePayload, cancellationToken);

    [MiniApiDelete("/{widgetId:int}")]
    public Task DeleteWidgetAsync(int widgetId, CancellationToken cancellationToken = default)
        => widgets.DeleteWidgetAsync(widgetId, cancellationToken);
}

The convenience attributes cover the supported HTTP verbs:

[MiniApiGet]
[MiniApiPost]
[MiniApiPut]
[MiniApiPatch]
[MiniApiDelete]
[MiniApiHead]
[MiniApiOptions]

The lower-level form remains available when needed:

[MiniApiRouteHandler(MiniApiVerbs.Get, "/{widgetId:int}")]

3. Create the hosting Function

internal sealed class WidgetApiFunction(IMiniApiRouter router)
{
    [Function(nameof(WidgetApiFunction))]
    [MiniApiFunction]
    public ValueTask<object?> RunAsync(
        [HttpTrigger(
            AuthorizationLevel.Function,
            //Only define the verbs needed for the API…
            MiniApiVerbs.Get,
            MiniApiVerbs.Post,
            MiniApiVerbs.Put,
            MiniApiVerbs.Patch,
            MiniApiVerbs.Delete
            Route = "widgets/{*path}"
        )]
        HttpRequestData request,
        string? path,
        CancellationToken cancellationToken
    ) => router.DispatchAsync(request, path, cancellationToken);
}

That is the complete routing setup.

Async Delegation Pitfall

NOTE: It is a best practice to delegate directly to the IMiniApiRouter. However, when delegating directly to IMiniApiRouter, either return the router's ValueTask directly or explicitly await it. If you define the Function as async and return the router's ValueTask without awaiting it, the Function may behave unexpectedly and be very difficult to notice/debug that the issue is simpply a missed await . . . because some objects will return fine, but use of others like a direct HttpResponseData may fail, etc.

Route patterns

Root routes

[MiniApiGet]
public Task<IReadOnlyList<WidgetDto>> GetWidgetsAsync()

Matches:

GET /api/widgets

Route parameters

[MiniApiGet("/{widgetId:int}")]
public Task<WidgetDto?> GetWidgetAsync(int widgetId)

Matches:

GET /api/widgets/42

The route token name must match its RouteHandler parameter. Route values are properly URL-decoded before binding, including encoded spaces and Unicode characters.

Multiple route parameters

[MiniApiGet("/{category}/{widgetId:int}")]
public Task<WidgetDto?> GetWidgetAsync(string category, int widgetId)

Matches:

GET /api/widgets/tools/42

Optional trailing route parameters

Route parameters become optional when the matching C# parameter is nullable or has a default value.

As optional then when not bound from the request the default value will be bound id defined, or the binding will be null if no default is defined.

[MiniApiGet("/{category}/{name}")]
public Task<IReadOnlyList<WidgetDto>> SearchWidgetsAsync(string? category = null, string? name = null)

Matches:

GET /api/widgets
GET /api/widgets/tools
GET /api/widgets/tools/hammer

Optional route parameters must be contiguous and trailing. A required route segment cannot follow an optional route parameter.

Catch-all routes

[MiniApiGet("/assets/{*path}")]
public Task<DigitalAsset?> GetAssetAsync(string path, CancellationToken cancellationToken = default)

Matches:

GET /api/widgets/assets
GET /api/widgets/assets/logo.png
GET /api/widgets/assets/images/icons/logo.png

The bound path values are:

""
"logo.png"
"images/icons/logo.png"

Catch-all parameters must be terminal; the last match segment of the route. Encoded route values are decoded after segment matching, so an encoded slash remains within the captured segment during matching and is decoded before binding.

Route constraints

Constraints validate route shape before a RouteHandler is selected.

Constraint Example Matches
string {name:string} Any string value
int {id:int} 32-bit integer
long {id:long} 64-bit integer
guid {id:guid} GUID value
bool {enabled:bool} true or false
decimal {price:decimal} Invariant-culture decimal (rational) number

Examples:

[MiniApiGet("/{widgetId:int}")]
public Task<WidgetDto?> GetWidgetByIdAsync(int widgetId)
[MiniApiGet("/{assetIdentifier:guid}")]
public Task<DigitalAsset?> GetAssetByIdentifierAsync(Guid assetIdentifier)
[MiniApiGet("/{enabled:bool}")]
public Task<IReadOnlyList<WidgetDto>> GetWidgetsAsync(bool enabled)
[MiniApiGet("/{price:decimal}")]
public Task<IReadOnlyList<WidgetDto>> GetWidgetsAsync(decimal price)

For rational number input constraint decimal provides the general numeric route shape constraint but may be used when the matching parameter binds to any supported rational numeric type such as decimal, double, float, etc.

Parameter binding

MiniApiRouting binds RouteHandler parameters in this order:

  1. Supported framework parameters
  2. Explicit binding attributes
  3. Route values
  4. Query-string values
  5. One inferred request body
  6. Declared C# default values or nullable fallbacks

Route values take precedence over query-string values with the same name.

Framework parameters

The classes that include MiniApi route handlers/methods fully support Dependency Injection for resolving services, and other dependencies as the [MiniApi] attribute ensures it is automatically registered as a transient service.

In addition, RouteHandlers (methods) may receive the following as request specific injectable dependency arguments:

HttpRequestData
FunctionContext
CancellationToken
MiniApiRouteValues

Query-string values

Scalar parameters not matched to route tokens bind from HttpRequestData.Query.

[MiniApiGet("/{widgetId:int}")]
public Task<WidgetDto?> GetWidgetAsync(int widgetId, string? material = null)

Request:

GET /api/widgets/42?material=titanium

Repeated query values support arrays, List<T>, IList<T>, IReadOnlyList<T>, IEnumerable<T>, ICollection<T>, and IReadOnlyCollection<T> for supported scalar element types.

[MiniApiGet]
public Task<IReadOnlyList<WidgetDto>> SearchWidgetsAsync(string[] tags)

Request:

GET /api/widgets?tags=tools&tags=featured

Scalar parameters use the first value deterministically. Missing collections bind as empty collections.

Header values

Headers require explicit binding and are not bound by convention.

[MiniApiGet("/{widgetId:int}")]
public Task<WidgetDto?> GetWidgetAsync(
    int widgetId,
    [MiniApiFromHeader("x-correlation-id")] string? correlationId = null
)

Request body

One single complex parameter binds from the JSON request body automatically.

[MiniApiPost]
public Task<WidgetDto> CreateWidgetAsync(CreateWidgetRequest request)

Use [MiniApiFromBody] when explicit body binding is preferred.

The default MiniApiJsonRequestBodyDeserializer uses the Azure Functions worker-configured ObjectSerializer and accepts application/json and structured application/*+json content types.

Applications can support XML or another format by registering a custom IMiniApiRequestBodyDeserializer before calling AddFunctionsMiniApiRouting().

Default and named Mini APIs

Most applications can use the default group:

[MiniApi]
internal sealed class WidgetRouteHandlers
{
}

[MiniApiFunction]

When one Function app hosts several independent APIs, use named groups to separate the sets of APIs and dispatch them from separate root Functions.

Using Constants can help keep the association clean and explicit.

internal static class MiniApis
{
    internal const string Widgets = "widgets";
    internal const string Assets = "assets";
}

[MiniApi(MiniApis.Widgets)]
internal sealed class WidgetRouteHandlers
{
}

[MiniApiFunction(MiniApis.Widgets)]

Multiple classes may contribute RouteHandlers to the same default or named group. Group names are never inferred from class names. This allows implementations to decide how they want to structure and organize thier code.

Route priority and specificity

MiniApiRouteHandlerAttribute.Priority defaults to 100. Lower numbers are evaluated first.

When priorities are equal, routes are ordered naturally:

  1. Static segments
  2. Constrained parameters
  3. Unconstrained parameters
  4. Catch-all parameters

Use an explicit priority only when natural route specificity does not express the desired behavior.

[MiniApiGet("/health", Priority = 0)]
public static object GetHealth()
    => new { Status = "Healthy" };

Return values

RouteHandlers may return:

T
Task<T>
ValueTask<T>
void
Task
ValueTask

MiniApiRouting does not serialize or transform RouteHandler results. RouteHandlers may return DTOs, custom results, HttpResponseData, or any application-specific value. Existing Function code or middleware remains responsible for output handling.

Compile-time validation

MiniApiRouting reports actionable compiler errors for invalid RouteHandler declarations, malformed or ambiguous routes, unsupported constraints, optional-route ordering, catch-all placement, binding conflicts, multiple request bodies, missing route bindings, and invalid Function-to-Mini-API associations.

These issues are found during compilation rather than after deployment or during request processing.

Runtime errors

Request-specific routing and binding failures use:

  • MiniApiRouteNotFoundException
  • MiniApiParameterBindingException
  • MiniApiUnsupportedContentTypeException

The library does not create HTTP error responses. Applications may use isolated-worker middleware to map exceptions to JSON, XML, Problem Details, HttpResponseData, or another response format.

Non-goals (What MiniApi is not trying to do!)

MiniApiRouting is not trying to replace or be AspNetCore MVC, controllers, MediatR, pipeline behaviors, output serialization, automatic HTTP error responses, form-data binding, cookie binding, or arbitrary body-format binding.

Current release status

Initial implementation released under active validation in production use cases.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.1.5 68 9/9/2026
1.1.4 66 9/8/2026
1.1.3 71 9/8/2026
1.1.2 67 9/8/2026
1.1.1 75 9/7/2026
1.1.0 85 9/6/2026
1.0.1 80 9/6/2026
1.0.0 77 9/6/2026

v1.1.5
     - Improve Generated code performance
     - Eliminated duplicated grouping name, http verb, and route matching logic in Generated Code to improve performance and reduce generated code size.
     - Streamlined generated code if conditions to now have if/else conditions to improve runtime performance and eliminate unnecessary route matching checks.

     v1.1.4
     - Fix bug with empty path segements for MiniApi Route Attribute (e.g. with no segments/path [MiniApiGet]) not resolving to the MiniApi Root route (e.g. Function Base Route).

     v1.1.3 (Non Functional)
     - FIxed Nuget Package Icon
     - Fixed Nuget Package Project URL.

     v1.1.2:
     - Fixed bug in incorrect compile errors for Body deserialization to collection types T[], IList<T>, IEnumerable<T>, ICollection<T>, List<T>, etc.

     v1.1.1:
     - Refactored route matching into a shared runtime implementation instead of emitting matching logic into generated source.
     - Reduced generated router size and complexity.
     - Added support for the explicit `string` route constraint.
     - Added support for empty catch-all route matching (`/{*path}`).
     - Centralized route constraint definitions into a shared implementation used by both the generator and runtime components.
     - Added comprehensive route matcher test coverage!
     - Added complete constraint validation coverage
     - Simplified and optimized runtime constraint evaluation.

     v1.1.0:
     - Added convenience route attributes: [MiniApiGet], [MiniApiPost], [MiniApiPut], [MiniApiPatch], [MiniApiDelete], [MiniApiHead], and [MiniApiOptions]
     - Expanded generator validation and regression test coverage
     - Documentation and README improvements

     v1.0.1:
     - Improved DI lifetime handling using FunctionContext.InstanceServices
     - Fixed optional route validation edge cases
     - Removed unnecessary request body stream-position checks
     - Internalized generated infrastructure types
     - Added regression tests for optional route expansion and route conflicts
     - Improved generator validation and test coverage
     - README updates and documentation refinements

     Prior Releas Notes:
     - Initial v1.0 release of Functions.Worker.AddOns.MiniApiRouting.
     - Functions.Worker.AddOns.MiniApiRouting is a Compile-time generated Mini APIs for Azure Functions isolated worker -- One Function many routes!
     - Supports route, query-string, header, and JSON request-body binding.
     - Supports multiple Mini APIs, generated dispatch, DI registration, diagnostics, and flexible handler return values.