ワンクリックで
minimal-api
Use when building minimal API endpoints with route groups, filters, or TypedResults.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Use when building minimal API endpoints with route groups, filters, or TypedResults.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when writing async code, propagating CancellationTokens, or fixing async/await pitfalls.
Use when applying or enforcing C# coding style — namespaces, sealed classes, var usage, XML docs.
Use when applying modern C# idioms — records, pattern matching, primary constructors, collection expressions.
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Use when selecting or implementing design patterns in C# — factory, builder, strategy, decorator, or mediator.
SOC 職業分類に基づく
| name | minimal-api |
| description | Use when building minimal API endpoints with route groups, filters, or TypedResults. |
| metadata | {"category":"api","agent":"api-designer"} |
| when_to_use | When creating minimal API endpoints, route groups, or endpoint filters |
IEndpointGroup patternTypedResults for compile-time-checked return types[AsParameters] for complex query parameter bindingpublic interface IEndpointGroup
{
void MapEndpoints(IEndpointRouteBuilder app);
}
// Auto-registration extension
public static class EndpointGroupExtensions
{
public static void MapEndpointGroups(this WebApplication app)
{
var groups = typeof(Program).Assembly
.GetTypes()
.Where(t => t.IsAssignableTo(typeof(IEndpointGroup))
&& !t.IsInterface && !t.IsAbstract)
.Select(Activator.CreateInstance)
.Cast<IEndpointGroup>();
foreach (var group in groups)
group.MapEndpoints(app);
}
}
// Program.cs
app.MapEndpointGroups();
public sealed class OrderEndpoints : IEndpointGroup
{
public void MapEndpoints(IEndpointRouteBuilder app)
{
var group = app.MapGroup("/orders")
.WithTags("Orders")
.RequireAuthorization();
group.MapGet("/", GetOrders)
.WithSummary("List orders with filtering");
group.MapGet("/{id:guid}", GetOrder)
.WithSummary("Get order by ID");
group.MapPost("/", CreateOrder)
.WithSummary("Create a new order");
group.MapPut("/{id:guid}", UpdateOrder)
.WithSummary("Update an existing order");
group.MapDelete("/{id:guid}", DeleteOrder)
.WithSummary("Delete an order");
}
private static async Task<Ok<PagedList<OrderResponse>>> GetOrders(
[AsParameters] OrderFilter filter,
ISender sender, CancellationToken ct)
{
var result = await sender.Send(
new ListOrdersQuery(filter), ct);
return TypedResults.Ok(result);
}
private static async Task<Results<Ok<OrderResponse>, NotFound>>
GetOrder(Guid id, ISender sender, CancellationToken ct)
{
var result = await sender.Send(
new GetOrderQuery(id), ct);
return result is not null
? TypedResults.Ok(result)
: TypedResults.NotFound();
}
private static async Task<Results<Created<OrderResponse>,
BadRequest<ProblemDetails>>> CreateOrder(
CreateOrderRequest request,
ISender sender, CancellationToken ct)
{
var result = await sender.Send(
new CreateOrderCommand(request.CustomerName), ct);
return result.Match<Results<Created<OrderResponse>,
BadRequest<ProblemDetails>>>(
order => TypedResults.Created(
$"/orders/{order.Id}", order),
error => TypedResults.BadRequest(
error.ToProblemDetails()));
}
}
public sealed record OrderFilter(
[FromQuery] string? CustomerName,
[FromQuery] OrderStatus? Status,
[FromQuery] int Page = 1,
[FromQuery] int PageSize = 20);
// Validation filter
public sealed class ValidationFilter<TRequest>(
IValidator<TRequest> validator) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(
EndpointFilterInvocationContext context,
EndpointFilterDelegate next)
{
var request = context.Arguments
.OfType<TRequest>().FirstOrDefault();
if (request is null)
return TypedResults.BadRequest("Request body is required");
// C-Q2 fix: forward the request's cancellation token to FluentValidation
// so client-cancelled requests stop validating mid-flight.
var result = await validator.ValidateAsync(
request, context.HttpContext.RequestAborted);
if (!result.IsValid)
{
return TypedResults.ValidationProblem(
result.ToDictionary());
}
return await next(context);
}
}
// Apply filter to endpoint
group.MapPost("/", CreateOrder)
.AddEndpointFilter<ValidationFilter<CreateOrderRequest>>();
// Program.cs
app.UseExceptionHandler(error => error.Run(async context =>
{
context.Response.ContentType = "application/problem+json";
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
await context.Response.WriteAsJsonAsync(new ProblemDetails
{
Status = 500,
Title = "Internal Server Error",
Type = "https://tools.ietf.org/html/rfc9110#section-15.6.1"
});
}));
Program.cs (use endpoint groups)IResult without TypedResults (loses compile-time checking)CancellationToken from endpoint to handlerMapGroup, MapGet, MapPost in Program.cs or endpoint filesIEndpointGroup or IEndpointRouteBuilder extension methodsTypedResults usageWithTags, WithSummary metadata callsAddEndpointFilter callsIEndpointGroup interface and auto-discovery extensionTypedResults for explicit return type contractsWithSummary, WithTags, WithDescription[AsParameters] for complex query parameter objects| Scenario | Recommendation |
|---|---|
| Simple CRUD API | Minimal API with endpoint groups |
| Complex model binding | Controllers may be easier |
| Real-time + REST | Minimal API + SignalR hubs |
| Need OpenAPI docs | Add WithSummary/WithDescription to every endpoint |