Skip to Content
Course content

72: Building a Minimal API

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Wait, where are the Controllers?

If you've looked at ASP.NET Core tutorials from a few years ago, you're probably wondering why we aren't creating a Controllers folder and inheriting from ControllerBase. To be honest, for a lot of small-to-medium services, that boilerplate was just overkill. Minimal APIs let us strip all that away.

In a Minimal API, we map routes directly to delegates (usually lambdas) right in Program.cs. I've found this makes the codebase much easier to navigate when you're building microservices. Instead of jumping between three different files to see how one endpoint works, it's all right there.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// This is the "Minimal" way. No controller class needed.
app.MapGet("/status", () => new { Status = "Online", Timestamp = DateTime.UtcNow });

app.Run();

How do I actually handle dynamic data in a URL?

You aren't just building static pages; you need to fetch specific resources. For our "Digital Library" example, you'll want to get a book by its ID. You do this by adding a placeholder in the route string and adding a matching parameter to your lambda function.

C# is smart enough to handle the type conversion for you. If you define the parameter as an int, the framework will try to parse the URL segment automatically. If the user passes "abc" instead of a number, it'll just return a 400 Bad Request without you having to write a single line of validation code.

// Example: Fetching a book by ID
app.MapGet("/books/{id}", (int id) => 
{
    // In a real app, you'd hit a database here
    return Results.Ok($"Fetching details for book ID: {id}");
});

What's the best way to accept a JSON body?

When you're building a POST or PUT endpoint, you usually aren't passing IDs in the URL; you're sending a whole object. The beauty of Minimal APIs is that they handle the JSON deserialization behind the scenes. You just define a record or class that matches the expected JSON shape, and put it in the method signature.

I highly recommend using records for this. They are immutable and concise, which fits the "minimal" philosophy perfectly.

public record Book(string Title, string Author, int Year);

app.MapPost("/books", (Book book) => 
{
    // The framework has already turned the JSON body into a 'book' object
    return Results.Created($"/books/{book.Title}", book);
});

How do I get my services or database into these lambdas?

You might be thinking, "If there's no constructor, how do I use Dependency Injection?" The answer is simple: you just add the service as a parameter to the delegate. The framework looks at the requested type and resolves it from the DI container automatically.

I'll use a hypothetical IBookRepository here. Notice how the repository is passed in right alongside the route parameters. The order doesn't strictly matter, but it's a common pattern to put services first.

app.MapGet("/books", (IBookRepository repo) => 
{
    var books = repo.GetAllBooks();
    return Results.Ok(books);
});



📋 Practical Task

Exercise: Build a Simple Inventory Tracker API

Your task is to create a Minimal API that manages a small inventory of electronics. Instead of a database, you can use a static List<Product> to keep things simple for this exercise.

Requirements:

  • Define a Product record with Id (int), Name (string), and Price (decimal).
  • Create a GET /products endpoint that returns the full list of products.
  • Create a GET /products/{id} endpoint that returns a specific product or a 404 Not Found if the ID doesn't exist.
  • Create a POST /products endpoint that accepts a Product object in the body and adds it to the list.

Tip: Use Results.NotFound() and Results.Ok() to ensure you are returning the correct HTTP status codes.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.