# Scalar.AspNetCore — API Documentation UI

> **Scope:** dotnet-vsa
> **Layer:** 0 (always load)
> **Keywords:** scalar, openapi, swagger, api-docs, documentation
> **Load When:** dotnet VSA projects

**Verified against:** .NET 10 + Scalar.AspNetCore 2.x + Microsoft.AspNetCore.OpenApi 10. Last-verified: 2026-05-20.

---

## Overview

Scalar replaces Swagger UI as the API documentation frontend. It provides a modern UI with built-in request testing, client code generation, and dark themes. Works with ASP.NET Core's native OpenAPI support (`Microsoft.AspNetCore.OpenApi`).

---

## Package Setup

```xml
<!-- .csproj -->
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.12.40" />
```

No Swashbuckle needed. ASP.NET Core 9+ has built-in OpenAPI document generation (`Microsoft.AspNetCore.OpenApi`); this project targets .NET 10.

---

## Program.cs Configuration

```csharp
using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

// Enable OpenAPI document generation
builder.Services.AddOpenApi();

var app = builder.Build();

// Map the OpenAPI JSON endpoint (dev only)
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

// Map Scalar UI at /scalar/v1
app.MapScalarApiReference(options =>
{
    options.WithTheme(ScalarTheme.DeepSpace);

    // Enable Developer Tools (request + client code panel)
    options.WithDefaultHttpClient(
        ScalarTarget.CSharp,
        ScalarClient.HttpClient);
});
```

**Access URL:** `https://localhost:{port}/scalar/v1`

---

## Available Themes

| Theme | Description |
|-------|-------------|
| `ScalarTheme.Default` | Light theme |
| `ScalarTheme.DeepSpace` | Dark blue (recommended) |
| `ScalarTheme.Purple` | Purple accent |
| `ScalarTheme.Moon` | Dark grey |
| `ScalarTheme.Saturn` | Warm dark |
| `ScalarTheme.Mars` | Red accent |

---

## ApiTags Organization

Group endpoints by feature using a constants class and `.WithTags()`:

```csharp
// Constants/ApiTags.cs
public static class ApiTags
{
    public const string Books = "books";
    public const string Authors = "authors";
    public const string Orders = "orders";
}
```

Apply tags in endpoints:

```csharp
internal sealed class CreateBookEndpoint : IApiEndpoint
{
    public void MapEndpoint(IEndpointRouteBuilder app)
    {
        app.MapPost("books", async (
            IHandler<CreateBookRequest, Result<CreateBookResponse>> handler,
            CreateBookRequest command,
            CancellationToken cancellationToken) =>
        {
            var result = await handler.HandleAsync(command, cancellationToken);
            return result.Match(
                onSuccess: () => Results.Ok(result.Value),
                onFailure: error => Results.BadRequest(error));
        })
        .WithTags(ApiTags.Books)
        .Produces<CreateBookResponse>(StatusCodes.Status200OK)
        .Produces(StatusCodes.Status400BadRequest);
    }
}
```

---

## Endpoint Metadata

Use `.Produces<T>()` to document response types for OpenAPI:

```csharp
app.MapGet("books/{id:guid}", async (...) => { ... })
    .WithTags(ApiTags.Books)
    .WithName("GetBookById")
    .WithSummary("Get a book by its unique identifier")
    .Produces<GetBookResponse>(StatusCodes.Status200OK)
    .Produces(StatusCodes.Status404NotFound);

app.MapDelete("books/{id:guid}", async (...) => { ... })
    .WithTags(ApiTags.Books)
    .Produces(StatusCodes.Status204NoContent)
    .Produces(StatusCodes.Status404NotFound);
```

### Common Metadata Methods

| Method | Purpose |
|--------|---------|
| `.WithTags("tag")` | Group in Scalar sidebar |
| `.WithName("OperationId")` | Unique operation identifier |
| `.WithSummary("...")` | Short description in endpoint list |
| `.WithDescription("...")` | Long description in detail panel |
| `.Produces<T>(statusCode)` | Document success response type |
| `.Produces(statusCode)` | Document error response (no body) |
| `.ProducesProblem(statusCode)` | Document ProblemDetails response |
| `.RequireAuthorization()` | Marks as requiring auth in docs |

---

## Client Code Generation

Scalar auto-generates client code for API calls. The `WithDefaultHttpClient` option selects the default language:

| Target | Client | Description |
|--------|--------|-------------|
| `ScalarTarget.CSharp` | `ScalarClient.HttpClient` | .NET HttpClient |
| `ScalarTarget.JavaScript` | `ScalarClient.Fetch` | Browser fetch() |
| `ScalarTarget.Python` | `ScalarClient.Requests` | Python requests |
| `ScalarTarget.Shell` | `ScalarClient.Curl` | cURL command |

---

## Anti-Patterns

| Do NOT | Do Instead |
|--------|------------|
| Install Swashbuckle | Use `Microsoft.AspNetCore.OpenApi` + `Scalar.AspNetCore` |
| Expose OpenAPI in production | Guard with `IsDevelopment()` check |
| Skip `.WithTags()` on endpoints | Every endpoint needs a tag for organized docs |
| Skip `.Produces<T>()` | Always document response types for accurate API docs |
| Use magic strings for tags | Define tags in `Constants/ApiTags.cs` |
| Map Scalar before `MapOpenApi()` | `MapOpenApi()` first, then `MapScalarApiReference()` |

---

*MORPH-SPEC by Polymorphism Tech*
