Minimal API Workshop
Module 1

Fundamentals & the Endpoint Pipeline

Route groups · binding · TypedResults · filters · OpenAPI

9:20 – 10:30 AM

By the end you can

Anatomy of Program.cs

Four parts — every line belongs to one

The four parts

var builder = WebApplication.CreateBuilder(args);   // 1. builder

builder.Services.AddOpenApi();                      // 2. services (DI)

var app = builder.Build();

app.UseHttpsRedirection();                          // 3. pipeline

app.MapGet("/health", () => "Healthy");            // 4. endpoints
app.Run();

“Minimal” = fewer abstractions between you and HTTP, not fewer capabilities.

Coming from controllers

Controller worldMinimal API world
[Route("api/[controller]")]app.MapGroup("/api/products")
[HttpGet("{id}")]group.MapGet("/{id}", handler)
ActionResult<T>Results<Ok<T>, NotFound>
Action filtersEndpoint filters
[Authorize].RequireAuthorization()

Route groups + feature folders

public static RouteGroupBuilder MapProductEndpoints(this IEndpointRouteBuilder app)
{
    var group = app.MapGroup("/api/products").WithTags("Products");

    group.MapGet("/", GetProducts).WithName("GetProducts");
    group.MapGet("/{id:int}", GetProduct).WithName("GetProduct");
    group.MapPost("/", CreateProduct).WithName("CreateProduct");
    return group;
}

Anything applied to the group — a tag, filter, policy, cache — applies to every endpoint in it.

Parameter binding

SourceHow it binds
Route /{id}Parameter with a matching name
Query stringSimple types not matched by a route value
BodyA complex type on POST / PUT / PATCH
Header[FromHeader]
ServicesAny registered service
SpecialHttpContext, CancellationToken, ClaimsPrincipal

Bind a group of query values into one record with [AsParameters].

TypedResults, not Results

static async Task<Results<Ok<ProductDto>, NotFound>> GetProduct(int id, IProductCatalog catalog)
{
    var product = await catalog.FindAsync(id);
    return product is null
        ? TypedResults.NotFound()
        : TypedResults.Ok(product.ToDto());
}

The Results<…> union is the contract: compile-time correctness and OpenAPI for free.

Endpoint filters

group.AddEndpointFilter(async (context, next) =>
{
    var sw = Stopwatch.StartNew();
    var result = await next(context);
    context.HttpContext.Response.Headers["X-Elapsed-Ms"] = sw.ElapsedMilliseconds.ToString();
    return result;
});

Middleware runs outside filters; filters run outside the handler — and see the bound arguments.

OpenAPI in .NET 10

builder.Services.AddOpenApi();
// ...
app.MapOpenApi();               // /openapi/v1.json
app.MapScalarApiReference();    // /scalar/v1  (browsable UI)

Built in — no Swashbuckle. Metadata flows from TypedResults, .WithName, .WithSummary.

Lab

Lab 1 · The Public Menu

Ends at checkpoint-1

Module 1 — Fundamentals
1 / 1
Speaker notes

Keyboard

SpaceNext (reveals bullets, then advances)
Back
Home / EndFirst / last
gOverview grid
sSpeaker notes
tLight / dark
fFullscreen
pPrint / save PDF
?This help
EscClose help/overview, or exit to the site

Swipe left/right on touch screens.