Replacing Swagger with Scalar in a Containerized Aspire.NET Project

Introduction

In modern .NET projects, API documentation is a must. While Swagger has become the default choice, Scalar offers a clean, minimal alternative with powerful customization options. In this tutorial, we’ll walk through how to integrate Scalar into a containerized .NET 8 API that runs as part of an Aspire.NET app.

This article is tailored for experienced developers looking to modernize their API tooling in a cloud-native setup. You’ll learn:

  • How to replace Swagger with Scalar in your ASP.NET Core app
  • How to expose Scalar in a containerized setup using Aspire.NET
  • How to fine-tune Scalar’s behavior using MapScalarApiReference

Problem & Context

Swagger is ubiquitous, but not always lightweight. Scalar steps in as a faster, more minimal alternative. In a recent microservices project based on Aspire.NET, we wanted to:

  • Provide clean OpenAPI documentation
  • Run the documentation UI within the API container
  • Keep the developer experience seamless via Aspire Dashboard

Scalar supports all of this out-of-the-box – but setting it up in an Aspire app (with containerization) needs a few deliberate steps.

Solution: Integrating Scalar in a Containerized API

Step 1: Install Scalar

First, add the Scalar NuGet package to your ASP.NET Core API project:

dotnet add package Scalar.AspNetCore

Ensure that you’re also adding OpenAPI generation:

builder.Services.AddOpenApi();

This enables the OpenAPI spec that Scalar will use under the hood.

Step 2: Configure Scalar Middleware

In your Program.cs, hook up Scalar using MapScalarApiReference inside the if (app.Environment.IsDevelopment()) block:

app.MapOpenApi();

app.MapScalarApiReference(options =>
{
    List<ScalarServer> servers = [];

    string? httpsPort = Environment.GetEnvironmentVariable("ASPNETCORE_HTTPS_PORT");
    if (httpsPort is not null)
    {
        servers.Add(new ScalarServer($"https://localhost:{httpsPort}"));
    }

    string? httpPort = Environment.GetEnvironmentVariable("ASPNETCORE_HTTP_PORT");
    if (httpPort is not null)
    {
        servers.Add(new ScalarServer($"http://localhost:{httpPort}"));
    }

    options.Servers = servers;
    options.Title = "Brickcity Story Management API";
    options.ShowSidebar = true;
});

This setup ensures that Scalar dynamically detects the correct port – even when running in a container.

Step 3: Configure Launch Settings

Make sure your launchSettings.json exposes the right ports and opens the Scalar UI:

"launchUrl": "scalar",
"applicationUrl": "https://localhost:7154;http://localhost:5185",

In your Docker profile:

"environmentVariables": {
  "ASPNETCORE_HTTPS_PORTS": "8081",
  "ASPNETCORE_HTTP_PORTS": "8080"
},
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/scalar",
"publishAllPorts": true

These environment variables are picked up by the middleware to build the correct OpenAPI server URLs.

Step 4: Connect via Aspire

In your AppHost project, reference the API project like this:

IResourceBuilder&lt;ProjectResource> apiStory = builder
    .AddProject&lt;Projects.BrickCity_Api_StoryManagement>("api-story-management");

Aspire takes care of service discovery, dashboard integration, and default port mapping. You should now be able to access the Scalar UI directly via the Aspire dashboard or by browsing to:

http://localhost:&lt;assigned-port>/scalar

Use Case: Brickcity Story Management API

In our case, we applied this setup to the Brickcity Story Management API. The API runs as a containerized service managed by Aspire, and developers get instant access to the documentation UI locally via /scalar. Thanks to the flexible port handling, the experience is seamless even in multi-service environments.

Performance-wise, Scalar loads faster than Swagger and keeps the focus on the essentials – ideal for internal APIs and rapid iterations.

Conclusion & Takeaways

Scalar is a great replacement for Swagger if you value speed and simplicity. Combined with Aspire.NET, it fits naturally into modern, containerized .NET environments.

Key Learnings:

  • Use MapScalarApiReference() to fine-tune behavior
  • Pass HTTP/HTTPS ports via env vars for container compatibility
  • Let Aspire handle service wiring and discovery
  • Keep your dev loop fast with launchUrl: "scalar"

What’s next?

Have you already tried Scalar in production? Curious how it behaves with versioned APIs? Let’s discuss!

For more insights, check out:

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Nach oben scrollen