Iâm really proud of the OpenAPI experience in Minimal APIs, especially in comparison to REST API frameworks in other ecosystems. Because weâre able to build on C#âs strong type system and .NETâs rich set of APIs for introspecting runtime behavior, we can create a really magical experience that lets us derive fairly accurate OpenAPI docs from APIs without a lot of user intervention. In the following code:
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapPost("/todos/{id}", (int id, Todo todo)
=> TypedResults.Created($"/todos/{id}", todo));
app.Run();
record Todo(int Id, string Title, bool IsCompleted, DateTime CreatedAt);
The AddOpenApi call registers services that are required for OpenAPI document generation, including a set of providers that understand how to examine the endpoints in the app and derive structured descriptions for them (see this earlier blog post I wrote). The MapOpenApi method registers an endpoint that emits the OpenAPI document in JSON format. By default, the document is served at http://localhost:{port}/openapi/v1.json, where v1 is the default document name. The document that you get is rich with metadata about the parameters and responses that this API produces.

Today, I want to hone in on the MapOpenApi method and talk a little bit about some of the design choices wrapped up in it. Itâs a small and tight API, but itâs a total workhorse. Hereâs what its method signature in the framework looks like:
public static IEndpointConventionBuilder MapOpenApi(
this IEndpointRouteBuilder endpoints,
[StringSyntax("Route")] string pattern = "/openapi/{documentName}.json")
Letâs walk through the details of the these three lines of code.
First, why MapOpenApi instead of something like UseOpenApi? The Map verb typically refers to components that are modeled as endpoints in the ASP.NET Core ecosystem, whereas the Use verb typically refers to components that are modeled as middleware. The choice to model this as an endpoint instead of a middleware is actually pretty cool because it lets the OpenAPI document participate in all the endpoint-specific behavior that is available in ASP.NET Coreâs other APIs. For example, if you want to lock down your API docs behind auth? Easy. Want to cache the document so youâre not regenerating it on every request? Also easy. Your code ends up being a chain of fluent calls to modify the behavior of the endpoint.
app.MapOpenApi()
.RequireAuthorization()
.WithOutputCaching()
You mightâve noticed the [StringSyntax("Route")] attribute on the pattern parameter. Thatâs a cute little hint to your editor that says âhey, this is a route template, maybe colorize it accordingly.â So if youâre staring at your code in VS Code or Visual Studio, youâll get nice syntax highlighting on the route parameters. Itâs one of those small touches that makes the DX a bit nicer for this API. In addition to colorization, it also opts in the parameter to a bunch of static analysis that ASP.NET Core does automatically. For example, if you provide a route pattern template that is invalid for whatever reason, youâll get a warning during build about this and be able to rectify the situation. This is part of the âshift-leftâ philosophy of API design, where errors and warnings happen as code is written and built, not when it is running.
The default route pattern is sensible enough that most folks wonât need to change it, but if you want to customize it, there are plenty of options for you. The most important thing is making sure your route template includes a {documentName} parameter so the framework knows which document youâre asking for. The code below lets you serve the OpenAPI document from a different route in your service.
app.MapOpenApi("/docs/{documentName}/openapi.json");
Hereâs a fun one: we added support for emitting OpenAPI documents in YAML after the initial API shipped. Rather than polluting the API surface with a new overload or a MapOpenApiYaml method (gross!), I just leaned into the file extension in the route. If you change .json to .yaml in the route template, boom, you get YAML. Iâm particularly proud of this because it keeps the API surface tiny while still being expressive.
app.MapOpenApi("/docs/{documentName}/openapi.yaml");
And because youâre calling a method to register an endpoint and not some middleware, you can call MapOpenApi multiple times to register different routes. If you want to serve both YAML and JSON variants of your OpenAPI documents, you just register two different endpoints with two different extensions.
app.MapOpenApi("/docs/{documentName}/openapi.yaml");
app.MapOpenApi("/docs/{documentName}/openapi.json");
The beauty of this API is that itâs concise and expressive without being too clever. That said, it does lean pretty heavily on understanding how the route templating system works, which might trip up folks who are new to ASP.NET Core. But honestly, the terseness works out well in practice since most people are gonna be just fine with the defaults: serve a JSON document at the default route and plug it into the rest of their OpenAPI tooling.
And thatâs MapOpenApi in a nutshell. Itâs one of those APIs that looks deceptively simple on the surface but has a lot of thought packed into all the little details. The endpoint-based model gives you flexibility, the route templating keeps things consistent with the rest of the ecosystem, and the file extension trick for YAML support is just chefâs kiss (if I do say so myself!).
Learn more about Googleâs Year in Search, which explores search trends from 2025.

