К основному содержанию
скилл CX Codex

minimal-api — скилл для Claude Code: эндпоинты .NET 10

Скилл для ИИ-агентов, который ведёт по минимальным API .NET 10: группы эндпоинтов, TypedResults, фильтры и OpenAPI.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add codewithmukesh/dotnet-claude-kit --skill minimal-api -a claude-code
Открыть на GitHub

Что делает minimal-api

Скилл описывает, как строить HTTP-эндпоинты в .NET 10 через минимальные API. Внутри — правила и готовые шаблоны кода: группировка через MapGroup, автообнаружение групп эндпоинтов через интерфейс IEndpointGroup и один вызов app.MapEndpoints() в Program.cs, который больше не меняется при добавлении новых групп.

Отдельно разобраны TypedResults для типизированных ответов и корректной генерации схем OpenAPI, привязка параметров из маршрута, query, заголовков, тела и DI, а также фильтры эндпоинтов для валидации и логирования. Канонический ValidationFilter<TRequest> на FluentValidation живёт в скилле error-handling — этот скилл советует брать его оттуда, а не писать свой в каждом проекте. Для документации используется встроенная поддержка OpenAPI в .NET 10 вместо Swashbuckle.

Скилл подключается к агенту и срабатывает, когда в разговоре появляются слова вроде «endpoint», MapGet, MapPost, MapGroup, TypedResults, «route», «swagger» или «rate limiting». Текст скилла обрезан на разделе про rate limiting, поэтому про настройку лимитов по нему ничего не скажешь — это повод проверить полную версию в репозитории.

Какие задачи решает

  • создать группу эндпоинтов в отдельном файле и зарегистрировать её автоматически
  • вернуть типизированный ответ с корректной схемой в OpenAPI
  • связать параметры запроса из маршрута, query или заголовка без ручного разбора
  • навесить валидацию на изменяющий эндпоинт через фильтр
  • настроить документацию OpenAPI без Swashbuckle

Примеры запросов агенту

  • добавь новый эндпоинт для заказов через MapGroup и TypedResults
  • почему у меня не генерируется схема ответа в OpenAPI
  • настрой привязку параметров из query через AsParameters

Как установить minimal-api

1Через Skills CLI (Vercel)

npx skills add codewithmukesh/dotnet-claude-kit --skill minimal-api -a claude-code

2Вручную: положить файлы

~/.claude/skills/minimal-api/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/minimal-api/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о minimal-api

Как установить minimal-api в Claude?

Выполните npx skills add codewithmukesh/dotnet-claude-kit --skill minimal-api -a claude-code. Скилл окажется в ~/.claude/skills/minimal-api/SKILL.md.

Что делает minimal-api?

Даёт агенту правила и шаблоны для минимальных API .NET 10: группы эндпоинтов, TypedResults, фильтры, привязка параметров и метаданные OpenAPI.

Скилл сам реализует фильтр валидации?

Нет. Он ссылается на канонический ValidationFilter<TRequest> из скилла error-handling и советует не переписывать его в каждом проекте.

Исходный текст

SKILL.md · MIT
---
name: minimal-api
description: >
  .NET 10 minimal APIs — the default for building HTTP endpoints. Covers MapGroup,
  endpoint filters, TypedResults, OpenAPI metadata, parameter binding, and route
  conventions.
  Load this skill when creating API endpoints, configuring routing, setting up
  OpenAPI documentation, or when the user mentions "endpoint", "MapGet", "MapPost",
  "MapGroup", "TypedResults", "route", "minimal API", "OpenAPI", "swagger",
  "rate limiting", or "output caching".
---

# Minimal APIs (.NET 10)

## Core Principles

1. **Minimal APIs are the default** — Use controllers only when migrating legacy code. Minimal APIs are lighter, faster, and compose well with any architecture style.
2. **Group endpoints with `MapGroup`** — Never scatter individual `MapGet`/`MapPost` calls in `Program.cs`. Group related endpoints together.
3. **Use `TypedResults` for OpenAPI** — `TypedResults.Ok(value)` gives you compile-time type safety AND correct OpenAPI documentation. `Results.Ok(value)` does not.
4. **Metadata over comments** — Use `.WithName()`, `.WithTags()`, `.WithSummary()` to document endpoints. The metadata feeds into OpenAPI specs.

## Patterns

### Endpoint Group Auto-Discovery (Required Pattern)

Every endpoint group lives in its own file and implements `IEndpointGroup`. A single `app.MapEndpoints()` call in `Program.cs` discovers and registers all groups automatically. **Program.cs never changes when you add new endpoint groups.**

```csharp
// Extensions/IEndpointGroup.cs
public interface IEndpointGroup
{
    void Map(IEndpointRouteBuilder app);
}
```

```csharp
// Extensions/EndpointExtensions.cs
public static class EndpointExtensions
{
    public static WebApplication MapEndpoints(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.Map(app);

        return app;
    }
}
```

```csharp
// Program.cs — this NEVER changes when adding endpoints
var app = builder.Build();
app.MapEndpoints();
app.Run();
```

```csharp
// Features/Orders/OrderEndpoints.cs — one file per endpoint group
public sealed class OrderEndpoints : IEndpointGroup
{
    public void Map(IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/api/orders").WithTags("Orders");

        group.MapPost("/", CreateOrder)
            .WithName("CreateOrder")
            .WithSummary("Create a new order")
            .Produces<OrderResponse>(StatusCodes.Status201Created)
            .ProducesValidationProblem()
            .RequireAuthorization();

        group.MapGet("/{id:guid}", GetOrder)
            .WithName("GetOrder")
            .Produces<OrderResponse>()
            .ProducesProblem(StatusCodes.Status404NotFound);

        group.MapGet("/", ListOrders)
            .WithName("ListOrders")
            .Produces<PagedList<OrderResponse>>();
    }

    private static async Task<Results<Created<OrderResponse>, ValidationProblem>> CreateOrder(
        CreateOrderRequest request,
        ISender sender,
        CancellationToken ct)
    {
        var result = await sender.Send(new CreateOrder.Command(request.CustomerId, request.Items), ct);
        return result.IsSuccess
            ? TypedResults.Created($"/api/orders/{result.Value.Id}", result.Value)
            : TypedResults.ValidationProblem(result.Errors);
    }

    private static async Task<Results<Ok<OrderResponse>, NotFound>> GetOrder(
        Guid id,
        ISender sender,
        CancellationToken ct)
    {
        var result = await sender.Send(new GetOrder.Query(id), ct);
        return result.IsSuccess
            ? TypedResults.Ok(result.Value)
            : TypedResults.NotFound();
    }

    private static async Task<Ok<PagedList<OrderResponse>>> ListOrders(
        [AsParameters] ListOrdersQuery query,
        ISender sender,
        CancellationToken ct)
    {
        var result = await sender.Send(query, ct);
        return TypedResults.Ok(result);
    }
}
```

### TypedResults for Type-Safe Responses

`TypedResults` provides compile-time guarantees and automatic OpenAPI schema generation.

```csharp
// GOOD — TypedResults with union return type
private static async Task<Results<Ok<Product>, NotFound, ValidationProblem>> GetProduct(
    Guid id,
    AppDbContext db,
    CancellationToken ct)
{
    var product = await db.Products.FindAsync([id], ct);
    return product is not null
        ? TypedResults.Ok(product)
        : TypedResults.NotFound();
}
```

### Parameter Binding

.NET 10 minimal APIs bind parameters from route, query, header, body, and DI automatically.

```csharp
// Route parameters
app.MapGet("/orders/{id:guid}", (Guid id) => ...);

// Query parameters (nullable = optional)
app.MapGet("/orders", (int page, int? pageSize, string? status) => ...);

// Complex query parameters with [AsParameters]
public record ListOrdersQuery(int Page = 1, int PageSize = 20, string? Status = null);
app.MapGet("/orders", ([AsParameters] ListOrdersQuery query) => ...);

// Header binding
app.MapGet("/orders", ([FromHeader(Name = "X-Correlation-Id")] string? correlationId) => ...);

// DI services are auto-resolved (no attribute needed)
app.MapPost("/orders", (CreateOrderRequest request, ISender sender) => ...);
```

### Endpoint Filters

Filters are the minimal API equivalent of action filters. Use them for cross-cutting concerns like validation, logging, and idempotency checks.

The canonical `ValidationFilter<TRequest>` implementation (FluentValidation, resolves the validator from DI and skips gracefully when none is registered) lives in the **error-handling** skill — use that one, don't re-implement it per project.

```csharp
// Apply the canonical filter (see error-handling skill) to a mutating endpoint
group.MapPost("/", CreateOrder)
    .AddEndpointFilter<ValidationFilter<CreateOrderRequest>>();

// Apply a filter to a group (affects all endpoints in the group)
group.AddEndpointFilter<LoggingFilter>();
```

### OpenAPI / Swagger Configuration

.NET 10 has built-in OpenAPI support. Use it instead of Swashbuckle.

```csharp
// Program.cs — service registration only, no endpoint wiring
builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}
app.MapEndpoints(); // auto-discovers all IEndpointGroup implementations

// Endpoint metadata enriches the OpenAPI spec
group.MapPost("/", CreateOrder)
    .WithName("CreateOrder")
    .WithSummary("Create a new order")
    .WithDescription("Creates a new order for the specified customer with the given line items.")
    .Produces<OrderResponse>(StatusCodes.Status201Created)
    .ProducesValidationProblem()
    .ProducesProblem(StatusCodes.Status500InternalServerError);
```

### Rate Limiting

```csharp
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("api", opt =>
    {
        opt.PermitLimit = 100;
        opt.Window = TimeSpan.FromMinutes(1);
    });
});

// Apply inside an IEndpointGroup.Map method
var group = app.MapGroup("/api/orders")
    .WithTags("Orders")
    .RequireRateLimiting("api");
```

### Output Caching

```csharp
builder.Services.AddOutputCache(options =>
{
    options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromMinutes(5)));
    options.AddPolicy("ByIdCache", builder => builder
        .Expire(TimeSpan.FromMinutes(10))
        .SetVaryByRouteValue("id"));
});

group.MapGet("/{id:guid}", GetOrder)
    .CacheOutput("ByIdCache");
```

## Anti-patterns

### Don't Put Endpoints in Program.cs

```csharp
// BAD — endpoints scattered in Program.cs
app.MapGet("/orders", async (AppDbContext db) => await db.Orders.ToListAsync());
app.MapGet("/orders/{id}", async (Guid id, AppDbContext db) => await db.Orders.FindAsync(id));
app.MapPost("/orders", async (Order order, AppDbContext db) => { /* ... */ });
app.MapGet("/products", async (AppDbContext db) => await db.Products.ToListAsync());

// ALSO BAD — manual MapGroup calls in Program.cs (grows with every feature)
app.MapGroup("/api/orders").WithTags("Orders").MapOrderEndpoints();
app.MapGroup("/api/products").WithTags("Products").MapProductEndpoints();
app.MapGroup("/api/customers").WithTags("Customers").MapCustomerEndpoints();
// Program.cs grows every time you add a feature...

// GOOD — auto-discovered, Program.cs never changes
app.MapEndpoints(); // discovers all IEndpointGroup implementations
```

### Don't Use Untyped Results

```csharp
// BAD — Results.Ok doesn't contribute to OpenAPI schema
private static async Task<IResult> GetOrder(Guid id, AppDbContext db)
{
    var order = await db.Orders.FindAsync(id);
    return order is not null ? Results.Ok(order) : Results.NotFound();
}

// GOOD — TypedResults with explicit union type
private static async Task<Results<Ok<Order>, NotFound>> GetOrder(Guid id, AppDbContext db)
{
    var order = await db.Orders.FindAsync(id);
    return order is not null ? TypedResults.Ok(order) : TypedResults.NotFound();
}
```

### Don't Return Domain Entities Directly

```csharp
// BAD — leaks internal structure, can't evolve independently
app.MapGet("/orders/{id}", async (Guid id, AppDbContext db) =>
    await db.Orders.Include(o => o.Items).FirstOrDefaultAsync(o => o.Id == id));

// GOOD — map to a response DTO
app.MapGet("/orders/{id}", async (Guid id, AppDbContext db) =>
{
    var order = await db.Orders
        .Where(o => o.Id == id)
        .Select(o => new OrderResponse(o.Id, o.Total, o.CreatedAt))
        .FirstOrDefaultAsync();
    return order is not null ? TypedResults.Ok(order) : TypedResults.NotFound();
});
```

## Decision Guide

| Scenario | Recommendation |
|----------|---------------|
| New HTTP API | `IEndpointGroup` per feature + `app.MapEndpoints()` auto-discovery |
| Existing MVC project | Keep controllers, migrate incrementally |
| OpenAPI documentation | Use `TypedResults` + `.WithName()` + `.WithSummary()` |
| Request validation | Endpoint filter with FluentValidation |
| Authentication/authorization | `.RequireAuthorization("PolicyName")` on group or endpoint |
| Rate limiting | `AddRateLimiter` + `.RequireRateLimiting()` |
| Response caching | `AddOutputCache` + `.CacheOutput()` |
| Complex model binding | `[AsParameters]` with a record type |

© codewithmukesh · MIT · codewithmukesh/dotnet-claude-kit

Похожие скиллы

Всё по теме «Бэкенд и API» →
aspnet-minimal-api-openapi официальный github Скилл помогает создавать типизированные ASP.NET Minimal API endpoints с OpenAPI-документацией. Пригодится для новых и расширяемых API. 9 176установок dotnet-core-expert https://github.com/Jeffallan Скилл для Claude Code, который ведёт агента по сборке бэкенда на .NET 8: минимальные API, чистая архитектура, EF Core, CQRS и JWT, от требований до dotnet test. CX Codex +1 4 700установок aspnet-core официальный openai Помогает проектировать, писать и обновлять приложения на ASP.NET Core по официальным гайдам Microsoft. Пригодится при работе с Blazor, Minimal API и gRPC. CX Codex +1 3 900установок dotnet-webapi dotnet Скилл для ИИ-кодинга ведёт агента при добавлении эндпоинтов ASP.NET Core Web API: HTTP-статусы, метаданные OpenAPI, обработка ошибок. CX Codex +1 4 300установок openapi-to-application-csharp-dotnet официальный Awesome Copilot Community Создаёт .NET-приложение по спецификации OpenAPI. Подойдёт, когда нужно получить основу ASP.NET Core проекта с контроллерами. ★ 40 418звёзд репо api-design Aaronontheweb Скилл для проектирования публичных API по принципу «только расширять»: помогает не ломать совместимость при выпуске новых версий. CX Codex +1 660установок

Из того же набора46

ci-cd Скилл помогает создавать YAML-пайплайны для проектов на .NET. Пригодится для быстрой настройки GitHub Actions и Azure DevOps с этапами сборки, тестов и публикации. CX Codex +1 2 800установок resilience Скилл для ИИ-агента, который подсказывает, как строить отказоустойчивые вызовы в .NET 10 на Polly v8: retry, circuit breaker, таймауты, fallback. CX Codex +1 2 800установок authentication Скилл для ИИ-агента, который подсказывает, как правильно настроить аутентификацию и авторизацию в ASP.NET Core: JWT, OIDC, Identity, политики. CX Codex +1 2 800установок dependency-injection Скилл внедряет паттерны dependency injection для .NET 10: выбор времён жизни сервисов, keyed services, декораторы через Scrutor и защиту от captive dependency. CX Codex +1 2 800установок testing Скилл задаёт стратегию тестирования приложений .NET 10: интеграционные тесты через WebApplicationFactory, реальные базы в Testcontainers, снапшоты Verify. CX Codex +1 2 800установок ef-core Набор паттернов Entity Framework Core для .NET 10. Помогает настроить DbContext, пакетные обновления, миграции и проекции запросов без лишней нагрузки на базу данных. CX Codex +1 2 800установок error-handling Задаёт стратегию обработки ошибок в .NET 10: Result, ProblemDetails, валидация и глобальный перехват исключений. Нужен при проектировании контрактов API. CX Codex +1 2 800установок architecture-advisor Задаёт вопросы про домен, команду и срок жизни системы, а потом советует архитектуру для .NET-проекта. Пригодится до того, как разложить решение по папкам. CX Codex +1 2 800установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой