Minimal APIs — An Endpoint in One Line
A new ASP.NET Core project does not make you create folders of classes before you can say hello. With Minimal APIs, ReadingRoom's first endpoint is one line in Program.cs. It is the default style of the webapi template, and it scales further than its name suggests.
The rule
Map a method and route to a function; parameters bind in, results go out.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<BookStore>(); // register services
var app = builder.Build();
app.MapGet("/books", (BookStore store) => store.All());
app.MapGet("/books/{id:int}", (int id, BookStore store) =>
store.Find(id) is Book book
? TypedResults.Ok(book)
: Results.NotFound());
app.MapPost("/books", (Book book, BookStore store) =>
{
store.Add(book);
return TypedResults.Created($"/books/{book.Id}", book);
});
app.Run();MapGet, MapPost, MapPut, MapPatch and MapDelete each pair a method with a route. {id:int} is a route parameter with a constraint: /books/abc never matches.
Binding fills each lambda parameter from the right place:
- a name that matches a route segment → from the route (
id) - a simple type not in the route → from the query string (
?page=2) - a complex type on POST/PUT → from the JSON body (
Book book) - a registered service → from dependency injection (
BookStore store)
Attributes such as [FromQuery] or [FromHeader] override the guess. Whatever you return is written back: an object becomes JSON with 200 OK; Results/TypedResults let you choose the status. app.MapGroup("/books") shares a prefix across related endpoints.
The shortcut
For each parameter, ask "is it in the route, a service, or a body?" — in that order. Anything left that is simple comes from the query string.
The endpoint
app.MapGet("/members/{memberId:int}/loans", (int memberId, bool overdueOnly, LibraryDb db) => ...)
The trap: returning null for a missing book. That sends 200 with an empty body; return Results.NotFound() instead.
Drills
Answer fast — trust the rule.
In app.MapGet("/books/{id:int}", (int id) => ...), what does /books/abc get?
In a MapPost, where does a Book book parameter come from by default?
What does TypedResults.Created("/books/7", book) produce?
What does app.MapGroup("/books") help with?
Remember
- Map a method and route to a function; parameters bind in, results go out.
- Route, then service, then body; leftover simple values come from the query.
- Choose the status with
Results/TypedResults, never return a bare null.
