PluginBench
Skill
Pass
Audit score 90

openapi

codewithmukesh/dotnet-claude-kit

Built-in OpenAPI support for .NET 10 with document generation, transformers, and security schemes—no Swashbuckle needed.

What is openapi?

Native OpenAPI documentation for .NET 10 applications using Microsoft.AspNetCore.OpenApi. Use this skill when setting up API documentation, customizing OpenAPI output, adding security schemes, or working with TypedResults metadata and document transformers.

  • Generate OpenAPI 3.1 specs automatically from TypedResults endpoints and metadata
  • Add security schemes (Bearer, OAuth) via document transformers without manual configuration
  • Support multiple OpenAPI documents with per-endpoint grouping via WithGroupName()
  • Extract XML documentation comments into OpenAPI descriptions at build time
  • Transform schemas, operations, and documents globally or per-endpoint
  • Serve OpenAPI specs in JSON and YAML formats with MapOpenApi()

How to install openapi

npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill openapi
Claude Code
Cursor
Windsurf
Cline

How to use openapi

  1. 1.Call builder.Services.AddOpenApi() in Program.cs to register the OpenAPI service
  2. 2.Add app.MapOpenApi() in Development environment to serve the spec at /openapi/v1.json
  3. 3.Use TypedResults.Ok<T>(), TypedResults.Created<T>(), etc. instead of Results.Ok() for proper schema inference
  4. 4.Add endpoint metadata: .WithName(), .WithSummary(), .WithDescription(), .Produces<T>() on every route
  5. 5.For security schemes, create an IOpenApiDocumentTransformer and register it via options.AddDocumentTransformer<T>()
  6. 6.Enable XML documentation in the project file with <GenerateDocumentationFile>true</GenerateDocumentationFile>

Use cases

Good for
  • Setting up API documentation for a new .NET 10 REST API without Swashbuckle
  • Adding JWT Bearer authentication to OpenAPI security schemes via transformers
  • Generating multiple API documents (public v1, internal admin) from one codebase
  • Extracting endpoint summaries and response codes from XML comments into the spec
  • Customizing schema formatting (e.g., decimal precision) across all endpoints
Who it's for
  • Backend developers building REST APIs in .NET 10
  • Teams migrating from Swashbuckle to the built-in OpenAPI solution
  • API maintainers needing client code generation from OpenAPI specs
  • Developers using Kiota or other OpenAPI-based code generators

openapi FAQ

Why should I use TypedResults instead of Results?

TypedResults.Ok<T>() automatically generates correct OpenAPI response schemas with proper status codes and types. Results.Ok() does not contribute to the OpenAPI spec and produces poor client generators.

How do I add Bearer token security to my OpenAPI spec?

Create an IOpenApiDocumentTransformer that adds a SecurityScheme to document.Components.SecuritySchemes and applies it to all operations. Register it via builder.Services.AddOpenApi(options => options.AddDocumentTransformer<YourTransformer>()).

Can I generate multiple OpenAPI documents from one API?

Yes. Call builder.Services.AddOpenApi("v1") and builder.Services.AddOpenApi("internal") for each document, then use .WithGroupName("v1") on endpoints to assign them to a specific document. Endpoints without WithGroupName() appear in all documents.

Is Swashbuckle still recommended for .NET 10?

No. Swashbuckle was removed from .NET 9+ templates. Microsoft.AspNetCore.OpenApi is the official, framework-maintained solution and is the recommended choice for new projects.

How do I generate the OpenAPI spec at build time?

Add <PackageReference Include="Microsoft.Extensions.ApiDescription.Server" /> and set <OpenApiDocumentsDirectory>.</OpenApiDocumentsDirectory> in your project file. The spec will be generated in the output directory during build.

Full instructions (SKILL.md)

Source of truth, from codewithmukesh/dotnet-claude-kit.


name: openapi description: > Built-in OpenAPI support for .NET 10 applications. Covers document generation, transformers, TypedResults metadata, security schemes, XML comments, build-time generation, and multiple document support. No Swashbuckle needed. Load this skill when setting up API documentation, customizing OpenAPI output, adding security schemes to docs, or when the user mentions "OpenAPI", "AddOpenApi", "MapOpenApi", "document transformer", "operation transformer", "schema transformer", "OpenAPI 3.1", "API documentation", "Swashbuckle replacement", "Produces", "WithSummary", "WithDescription", "ProblemDetails", "Kiota", or "client generation".

OpenAPI

Core Principles

  1. Built-in, not Swashbuckle — .NET 10 ships Microsoft.AspNetCore.OpenApi as the official, framework-maintained OpenAPI solution. Swashbuckle was removed from templates in .NET 9 and is no longer recommended.
  2. TypedResults drive the schema — TypedResults.Ok<T>() automatically generates correct OpenAPI response schemas. Results.Ok() does not. Always use TypedResults.
  3. Transformers over workarounds — Document, operation, and schema transformers compose cleanly. Use them for security schemes, global responses, and schema customization.
  4. Metadata on every endpoint — Use .WithName(), .WithSummary(), .WithTags() on every endpoint. This metadata feeds directly into the OpenAPI spec and client generators.

Patterns

Basic Setup

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();  // Serves at /openapi/v1.json
}

Endpoint Metadata

group.MapPost("/", CreateOrder)
    .WithName("CreateOrder")
    .WithSummary("Create a new order")
    .WithDescription("Creates a new order for the specified customer.")
    .Produces<OrderResponse>(StatusCodes.Status201Created)
    .ProducesValidationProblem()
    .ProducesProblem(StatusCodes.Status500InternalServerError);

With TypedResults, response metadata is inferred automatically:

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

Bearer Token Security Scheme

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});

internal sealed class BearerSecuritySchemeTransformer(
    IAuthenticationSchemeProvider authSchemeProvider) : IOpenApiDocumentTransformer
{
    public async Task TransformAsync(OpenApiDocument document,
        OpenApiDocumentTransformerContext context, CancellationToken ct)
    {
        var schemes = await authSchemeProvider.GetAllSchemesAsync();
        if (!schemes.Any(s => s.Name == "Bearer"))
            return;

        document.Components ??= new OpenApiComponents();
        document.Components.SecuritySchemes = new Dictionary<string, IOpenApiSecurityScheme>
        {
            ["Bearer"] = new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.Http,
                Scheme = "bearer",
                BearerFormat = "JWT",
                In = ParameterLocation.Header
            }
        };

        foreach (var operation in document.Paths.Values.SelectMany(p => p.Operations))
        {
            operation.Value.Security ??= [];
            operation.Value.Security.Add(new OpenApiSecurityRequirement
            {
                [new OpenApiSecuritySchemeReference("Bearer", document)] = []
            });
        }
    }
}

Document Info Transformer

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer((document, context, ct) =>
    {
        document.Info = new()
        {
            Title = "Checkout API",
            Version = "v1",
            Description = "API for processing orders and payments."
        };
        return Task.CompletedTask;
    });
});

Multiple OpenAPI Documents

builder.Services.AddOpenApi("v1");
builder.Services.AddOpenApi("internal", options =>
{
    options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});

// Endpoints choose their document via WithGroupName
app.MapGet("/public", () => "Hello").WithGroupName("v1");
app.MapGet("/admin", () => "Secret").WithGroupName("internal");

Endpoints without .WithGroupName() appear in all documents.

XML Documentation Comments (.NET 10)

Enable in the project file — the source generator extracts <summary>, <param>, <response> tags automatically:

<PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
/// <summary>Retrieves a project board by ID.</summary>
/// <param name="id">The project board ID.</param>
/// <response code="200">Returns the project board.</response>
/// <response code="404">Board not found.</response>
static async Task<Results<Ok<Board>, NotFound>> GetBoard(int id, AppDbContext db)
{
    var board = await db.Boards.FindAsync(id);
    return board is not null ? TypedResults.Ok(board) : TypedResults.NotFound();
}

XML comments on lambdas are not captured by the compiler. Use named methods.

Schema Transformer

options.AddSchemaTransformer((schema, context, ct) =>
{
    if (context.JsonTypeInfo.Type == typeof(decimal))
    {
        schema.Format = "decimal";
    }
    return Task.CompletedTask;
});

Per-Endpoint Operation Transformer (.NET 10)

app.MapGet("/old", () => "deprecated")
    .AddOpenApiOperationTransformer((operation, context, ct) =>
    {
        operation.Deprecated = true;
        return Task.CompletedTask;
    });

Build-Time Document Generation

<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="*" />
<PropertyGroup>
    <OpenApiDocumentsDirectory>.</OpenApiDocumentsDirectory>
</PropertyGroup>

The spec file is generated in the output directory during build.

YAML Endpoint (.NET 10)

app.MapOpenApi("/openapi/{documentName}.yaml");

Anti-patterns

Don't Use Swashbuckle for New Projects

// BAD — removed from .NET 9+ templates, maintenance concerns
builder.Services.AddSwaggerGen();
app.UseSwagger();
app.UseSwaggerUI();

// GOOD — built-in OpenAPI
builder.Services.AddOpenApi();
app.MapOpenApi();

Don't Use WithOpenApi() in .NET 10

// BAD — deprecated, produces ASPDEPR002 warning
app.MapGet("/", () => "hello").WithOpenApi(op => { op.Deprecated = true; return op; });

// GOOD — use per-endpoint operation transformer
app.MapGet("/", () => "hello")
    .AddOpenApiOperationTransformer((op, ctx, ct) =>
    {
        op.Deprecated = true;
        return Task.CompletedTask;
    });

Don't Use Untyped Results

// BAD — Results.Ok doesn't contribute to OpenAPI schema
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 union return type
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 Skip WithName on Endpoints

// BAD — client generators produce poor method names without operationId
group.MapGet("/{id:guid}", GetOrder);

// GOOD — operationId feeds into generated client method names
group.MapGet("/{id:guid}", GetOrder).WithName("GetOrder");

Don't Use OpenApiAny in .NET 10

// BAD — OpenApiAny types removed in Microsoft.OpenApi v2.x
schema.Example = new OpenApiString("2025-01-01");

// GOOD — use JsonNode from System.Text.Json.Nodes
schema.Example = JsonValue.Create("2025-01-01");

Decision Guide

ScenarioRecommendation
New API projectAddOpenApi() + MapOpenApi() (built-in)
API documentation UIScalar (MapScalarApiReference())
Security schemes in docsDocument transformer with IOpenApiDocumentTransformer
Response documentationTypedResults with union return types
XML doc integration<GenerateDocumentationFile>true</GenerateDocumentationFile>
Multiple API versionsMultiple AddOpenApi("v1") calls + WithGroupName()
Client code generationKiota (Microsoft recommended) or NSwag
Build-time specMicrosoft.Extensions.ApiDescription.Server package
OpenAPI version3.1 (default in .NET 10), force 3.0 if consumers require it
Per-endpoint customization.AddOpenApiOperationTransformer() on the endpoint