DEV Community

Cover image for Folio.AspNetCore — an embeddable OpenAPI UI for ASP.NET Core
Anushervon Bekov
Anushervon Bekov

Posted on

Folio.AspNetCore — an embeddable OpenAPI UI for ASP.NET Core

Most OpenAPI UIs for ASP.NET Core work the same way: you generate a spec,
then host a separate set of static files to render it. That's fine until
you want it to just... work, out of the box, offline, without another
build step in your pipeline.

I spent the last few months building Folio.AspNetCore — an OpenAPI
documentation and testing UI that ships entirely as a NuGet package. No
static files to host yourself, no CDN dependencies, no separate build
step. Install the package, point it at your OpenAPI document, done.

dotnet add package Folio.AspNetCore
Enter fullscreen mode Exit fullscreen mode
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();

var app = builder.Build();
app.MapOpenApi();

app.UseFolio(options =>
{
    options.SpecUrl = "/openapi/v1.json";
});

app.Run();
Enter fullscreen mode Exit fullscreen mode

That's the whole setup. Open /folio and you get the full UI.

What you get

Browsing

  • Endpoint list, grouped and searchable, generated straight from the spec
  • A full, separately-browsable model/schema catalog — not just inline types buried in each endpoint
  • Markdown-rendered descriptions (from the spec's own description fields)

A real request builder, not a raw JSON textarea

Parameters and request body fields get the right control for their
schema type — text inputs, number steppers, boolean toggles, enum
dropdowns, date pickers, nested object/array editors. You can still drop
into raw JSON if you want to.

Live code generation, in six languages

Every request gets an equivalent code snippet — cURL, JavaScript
(fetch), Python (requests), C# (HttpClient), Go (net/http),
PowerShell (Invoke-RestMethod) — updated live as you fill in the form,
before you even hit Send. Syntax-highlighted too.

Per-endpoint role visibility

If your endpoints use [Authorize(Roles = "...")], Folio shows exactly
which roles are required, right in the sidebar and on the endpoint page —
read directly from your routing metadata via EndpointDataSource.
Nothing to configure, nothing duplicated: it's a live mirror of what your
code already declares.

app.MapPost("/api/items", CreateItem)
    .RequireAuthorization(new AuthorizeAttribute { Roles = "Admin" });
Enter fullscreen mode Exit fullscreen mode

That attribute alone is enough — Folio picks it up automatically.

Environments, headers, auth

  • Switch between base URLs (local/staging/prod) without touching code, or import servers[] straight from the spec
  • Global headers (e.g. an API key) applied to every request, separate from per-endpoint headers
  • A dedicated modal for the spec's own security schemes (apiKey, HTTP Bearer/Basic)

Gating the docs themselves

Two ways to put a login in front of the UI:

// Integrate with auth you already have
app.UseFolio(options =>
{
    options.AuthorizationPolicy = "FolioAccess";
    options.LogoutUrl = "/account/logout";
});
Enter fullscreen mode Exit fullscreen mode
// Or a self-contained cookie-based login, zero existing auth required
builder.Services.AddFolioAuth(options =>
{
    options.Users = builder.Configuration.GetSection("Users").Get<List<FolioUser>>() ?? [];
});

app.UseFolioAuth();
app.UseFolio(options => options.SpecUrl = "/openapi/v1.json");
Enter fullscreen mode Exit fullscreen mode

Fine-grained control over what's shown

Every major UI element — the execute button, code generator, role
badges, schema viewer, environment switcher, and more — can be toggled
independently:

app.UseFolio(options =>
{
    options.ShowTryItOut = false; // read-only docs deployment, say
});
Enter fullscreen mode Exit fullscreen mode

...or bound from appsettings.json explicitly (Folio never reads config
on its own — this only runs if you call it):

app.UseFolio((context, options) =>
{
    options.ReadFrom.Configuration(context.Configuration, "Folio");
});
Enter fullscreen mode Exit fullscreen mode

How it's built

It's a classic ASP.NET Core middleware (app.Use...), not endpoint
routing. The UI's HTML/CSS/JS live as embedded resources, served through
a real StaticFileMiddleware on top of ManifestEmbeddedFileProvider —
proper ETag/Last-Modified/conditional requests, not hand-rolled file
serving.

Multi-targets .NET 6 through 9.

Try it

dotnet add package Folio.AspNetCore
Enter fullscreen mode Exit fullscreen mode

Feedback and issues welcome — especially if something's missing for your
use case.

Top comments (0)