Moclawr.MinimalAPI
2.0.0
See the version list below for details.
dotnet add package Moclawr.MinimalAPI --version 2.0.0
NuGet\Install-Package Moclawr.MinimalAPI -Version 2.0.0
<PackageReference Include="Moclawr.MinimalAPI" Version="2.0.0" />
<PackageVersion Include="Moclawr.MinimalAPI" Version="2.0.0" />
<PackageReference Include="Moclawr.MinimalAPI" />
paket add Moclawr.MinimalAPI --version 2.0.0
#r "nuget: Moclawr.MinimalAPI, 2.0.0"
#addin nuget:?package=Moclawr.MinimalAPI&version=2.0.0
#tool nuget:?package=Moclawr.MinimalAPI&version=2.0.0
Moclawr.MinimalAPI
Overview
Moclawr.MinimalAPI is a powerful library for building clean, structured, and maintainable ASP.NET Core Minimal APIs for .NET 9. It provides a class-based approach to endpoint definition with built-in MediatR integration for implementing the CQRS pattern, making it easy to create scalable API applications with clear separation of concerns.
- Strong typing for requests and responses
- Automatic model binding from various sources (route, query, body, form, header)
- Standardized response handling
- Support for versioning and authorization
Getting Started
- Add a reference to the MinimalAPI project in your application.
- Register the MinimalAPI services in your
Program.cs
file. - Create endpoint classes that inherit from
EndpointBase
. - Map all endpoints in your
Program.cs
file.
Usage Example
Step 1: Register Services
In your Program.cs
file, register the Minimal API services:
// Register Minimal API services
builder.Services.AddMinimalApi(
typeof(Program).Assembly, // Endpoints assembly
typeof(Application).Assembly, // Handlers assembly
typeof(Infrastructure).Assembly // Infrastructure assembly
);
// ... other service registrations
var app = builder.Build();
// Map all endpoints from the assembly
app.MapMinimalEndpoints(typeof(Program).Assembly);
Step 2: Create Endpoint Classes
You can create endpoints in three different ways depending on your response type:
- For endpoints that return a single item:
public class GetTodoEndpoint(IMediator mediator) : SingleEndpointBase<GetRequest, TodoItemDto>(mediator)
{
[HttpGet("api/todos/{id}")]
public override async Task<Response<TodoItemDto>> HandleAsync(GetRequest req, CancellationToken ct)
{
return await _mediator.Send(req, ct);
}
}
- For endpoints that return a collection:
public class GetAllTodosEndpoint(IMediator mediator) : CollectionEndpointBase<GetAllRequest, TodoItemDto>(mediator)
{
[HttpGet("api/todos")]
public override async Task<ResponesCollection<TodoItemDto>> HandleAsync(GetAllRequest req, CancellationToken ct)
{
return await _mediator.Send(req, ct);
}
}
- Or use the general base class for custom response formats:
public class DeleteTodoEndpoint(IMediator mediator) : EndpointBase<DeleteRequest, object, Response>(mediator)
{
[HttpDelete("api/todos/{id}")]
public override async Task<Response> HandleAsync(DeleteRequest req, CancellationToken ct)
{
return await _mediator.Send(req, ct);
}
}
Step 3: Create Request Classes
For commands that modify data and return a result:
public class CreateRequest : ICommand<CreateResponse>
{
public string Title { get; set; } = string.Empty;
public string? Description { get; set; }
}
public class CreateResponse
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
}
For commands that don't return data:
public class DeleteRequest : ICommand
{
[FromRoute]
public int Id { get; set; }
}
For queries that return data:
public class GetAllRequest : IQueryRequest<GetAllResponse>
{
public string? Search { get; set; }
public int PageIndex { get; set; } = 0;
public int PageSize { get; set; } = 10;
}
public class GetAllResponse
{
public List<TodoItemDto> Items { get; set; } = new();
public int TotalCount { get; set; }
}
Step 4: Implement Command/Query Handlers
Command handler with response:
public class CreateHandler(ITodoRepository repository) : ICommandHandler<CreateRequest, CreateResponse>
{
private readonly ITodoRepository _repository = repository;
public async Task<Response<CreateResponse>> Handle(CreateRequest request, CancellationToken cancellationToken)
{
var todo = new TodoItem
{
Title = request.Title,
Description = request.Description
};
var id = await _repository.AddAsync(todo, cancellationToken);
await _repository.SaveChangeAsync(cancellationToken);
return new Response<CreateResponse>
{
Data = new CreateResponse { Id = id, Title = todo.Title }
};
}
}
Command handler without response:
public class DeleteHandler(ITodoRepository repository) : ICommandHandler<DeleteRequest>
{
private readonly ITodoRepository _repository = repository;
public async Task<Response> Handle(DeleteRequest request, CancellationToken cancellationToken)
{
var todo = await _repository.GetByIdAsync(request.Id, cancellationToken);
if (todo == null)
return Response.NotFound($"Todo with id {request.Id} not found");
_repository.Remove(todo);
await _repository.SaveChangeAsync(cancellationToken);
return Response.Success();
}
}
Query handler:
public class GetAllHandler(ITodoRepository repository) : IQueryHandler<GetAllRequest, GetAllResponse>
{
private readonly ITodoRepository _repository = repository;
public async Task<Response<GetAllResponse>> Handle(GetAllRequest request, CancellationToken cancellationToken)
{
var (items, totalCount) = await _repository.GetPaginatedAsync(
request.Search,
request.PageIndex,
request.PageSize,
cancellationToken
);
return new Response<GetAllResponse>
{
Data = new GetAllResponse
{
Items = items.Select(i => new TodoItemDto
{
Id = i.Id,
Title = i.Title,
Description = i.Description,
IsCompleted = i.IsCompleted
}).ToList(),
TotalCount = totalCount
}
};
}
}
Advanced Features
Model Binding
The framework automatically binds request data from various sources:
[FromRoute]
- Bind from route parameters[FromQuery]
- Bind from query string[FromBody]
- Bind from request body (JSON)[FromForm]
- Bind from form data[FromHeader]
- Bind from HTTP headers[FromServices]
- Inject services from DI container
Example:
public class UpdateRequest : ICommand<UpdateResponse>
{
[FromRoute]
public int Id { get; set; }
[FromBody]
public string Title { get; set; } = string.Empty;
[FromBody]
public string? Description { get; set; }
[FromQuery]
public bool IsCompleted { get; set; }
[FromServices]
public ICurrentUserService CurrentUser { get; set; } = null!;
}
Response Handling
The library provides standardized response handling with different response types:
Response
- Base response with success status, HTTP status code, and an optional messageResponse<T>
- Response with data of type TResponesCollection<T>
- Response with a collection of items of type T
// Success response with data
return new Response<TodoItemDto>(
IsSuccess: true,
StatusCode: 200,
Message: "Todo item retrieved successfully",
Data: todoDto
);
// Success response without data
return new Response(true, 200, "Operation completed successfully");
// Collection response
return new ResponesCollection<TodoItemDto>(
IsSuccess: true,
StatusCode: 200,
Message: "Todo items retrieved successfully",
Data: todos
);
// Error responses
return new Response(false, 404, "Todo item not found");
return new Response(false, 400, "Invalid input data");
return new Response(false, 403, "You don't have permission to access this resource");
return new Response(false, 500, "An error occurred");
Versioning
The library includes enhanced versioning support:
// Define versioning options in your Program.cs
services.Configure<VersioningOptions>(options =>
{
options.Prefix = "v"; // Default prefix for version number
options.DefaultVersion = 1; // Default version if not specified
options.PrependToRoute = false; // Whether to prepend version to the route
});
// Use versioning in your endpoint
[HttpGet("api/v{version}/todos")]
public override async Task<Response<GetAllResponse>> HandleAsync(GetAllRequest req, CancellationToken ct)
{
// Use version-specific logic if needed
return await _mediator.Send(req, ct);
}
Authorization
The library offers enhanced authorization features:
// Method 1: Using attributes
[HttpPost("api/todos")]
[Authorize(Roles = "Admin,Editor")]
public override async Task<Response<CreateResponse>> HandleAsync(CreateRequest req, CancellationToken ct)
{
return await _mediator.Send(req, ct);
}
// Method 2: Using endpoint definition
public class CreateTodoEndpoint(IMediator mediator) : SingleEndpointBase<CreateRequest, CreateResponse>(mediator)
{
[HttpPost("api/todos")]
public override async Task<Response<CreateResponse>> HandleAsync(CreateRequest req, CancellationToken ct)
{
// Configure authorization in constructor or initialization code
Definition.EnabledAuthorization = true;
Definition.Roles("Admin", "Editor");
Definition.Policies("RequireMinimumAge");
Definition.AuthSchemes("Bearer");
return await _mediator.Send(req, ct);
}
}
Conclusion
This MinimalAPI library provides a structured approach to building APIs with minimal boilerplate while maintaining the benefits of strong typing, dependency injection, and separation of concerns through the CQRS pattern with .NET 9 features. *
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. 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 was computed. 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. |
-
net9.0
- MediatR (>= 12.2.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.5)
- Moclawr.Core (>= 2.0.0)
- Moclawr.Shared (>= 2.0.0)
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 |
---|---|---|
2.1.9 | 218 | 6/4/2025 |
2.1.8 | 124 | 6/4/2025 |
2.1.7 | 132 | 6/4/2025 |
2.1.6 | 132 | 6/4/2025 |
2.1.4 | 108 | 5/30/2025 |
2.1.3 | 119 | 5/30/2025 |
2.1.2 | 118 | 5/30/2025 |
2.1.1 | 131 | 5/28/2025 |
2.1.0 | 130 | 5/28/2025 |
2.0.1 | 58 | 5/24/2025 |
2.0.0 | 64 | 5/24/2025 |
1.0.3 | 59 | 5/24/2025 |
1.0.2.1 | 68 | 5/24/2025 |
1.0.2 | 137 | 5/22/2025 |
1.0.1 | 137 | 5/21/2025 |
1.0.0 | 135 | 5/19/2025 |
Added improved XML documentation and bug fixes.