ASP.NET Core  

Minimal APIs Cheatsheet (in ASP.NET Core)

Introduction

Minimal APIs were introduced in .NET 6 to simplify the creation of small, fast, and lightweight web APIs. They allow developers to write API logic with less code and configuration, without needing controllers or attributes like in traditional ASP.NET MVC.

They are best used when building simple services, microservices, or proof-of-concept applications.

1. Create a New Minimal API Project

To get started

dotnet new web -n MinimalApiDemo
  • This template sets up a basic minimal API.
  • No controllers or Views folder.

2. Basic Program.cs Structure

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

app.MapGet("/", () => "Hello World!");

app.Run();
  • MapGet defines a GET endpoint.
  • Everything is written in Program.cs.

3. MapGet() – Handle GET Requests

app.MapGet("/hello", () => "Hi there!");
  • Used to return data or simple messages.
  • Can return strings, objects, or JSON.

4. MapPost() – Handle POST Requests

app.MapPost("/add", (int x, int y) => x + y);
  • Accepts parameters directly from query or body.
  • Ideal for submitting data.

5. MapPut() – Handle PUT Requests

app.MapPut("/update", (int id, string name) => $"Updated {id} to {name}");
  • Used to update existing resources.

6. MapDelete() – Handle DELETE Requests

app.MapDelete("/delete/{id}", (int id) => $"Deleted item {id}");
  • Deletes a resource based on the ID.

7. Return JSON Response

app.MapGet("/user", () => new { Name = "John", Age = 25 });
  • Automatically serializes object to JSON.
  • No need for extra configuration.

8. Reading From Query Strings

app.MapGet("/greet", (string name) => $"Hello, {name}!");
  • Pass like /greet?name=Diksha.

9. Reading From Route Parameters

app.MapGet("/product/{id}", (int id) => $"Product ID: {id}");
  • Route parameters are defined inside {}.

10. Read JSON Body in POST

app.MapPost("/person", (Person person) =>
{
    return $"Name: {person.Name}, Age: {person.Age}";
});

record Person(string Name, int Age);
  • Automatically binds the JSON request body to an object.
  • Use record or class.

11. Dependency Injection (DI)

Register service

builder.Services.AddSingleton<MyService>();

Use it in the route

app.MapGet("/info", (MyService svc) => svc.GetInfo());
  • Supports constructor-less DI in lambda.

12. Validation Example

app.MapPost("/validate", (User user) =>
{
    if (string.IsNullOrWhiteSpace(user.Email))
        return Results.BadRequest("Email is required");

    return Results.Ok("Valid User");
});

record User(string Email);
  • Manual validation can be added easily.

13. Using Results Object

app.MapGet("/status", () => Results.Ok("Working"));
app.MapGet("/fail", () => Results.BadRequest("Something went wrong"));
  • Explicit control over HTTP response type and status.

14. Grouping Routes

var userGroup = app.MapGroup("/users");

userGroup.MapGet("/", () => "All users");
userGroup.MapPost("/", (User u) => $"User {u.Email} added");
  • Helps organize related endpoints under a common path.

15. Middleware Usage

app.Use(async (context, next) =>
{
    Console.WriteLine("Request coming in");
    await next();
    Console.WriteLine("Response going out");
});
  • Minimal APIs support middlewares like normal APIs.

16. Enable Swagger (OpenAPI)

Add services

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

Use Swagger UI

app.UseSwagger();
app.UseSwaggerUI();
  • Helps with testing and documentation.

17. Route Constraints

app.MapGet("/item/{id:int}", (int id) => $"Item ID: {id}");
  • :int ensures only integer is accepted.

18. Allow CORS

builder.Services.AddCors();

app.UseCors(policy => policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
  • Enables cross-origin API calls.

19. Environment-based Configuration

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
  • Add environment-specific behaviors.

20. Error Handling

app.UseExceptionHandler("/error");

app.Map("/error", () => Results.Problem("Unexpected error occurred"));
  • Define a global error route.

21. Run on Custom URL/Port

Update launchSettings.json or in code:

builder.WebHost.UseUrls("http://localhost:5001");

22. Route Prefix with [MapGroup] for Versioning

var v1 = app.MapGroup("/api/v1");

v1.MapGet("/products", () => "v1 products");
  • Helps version your API easily (/api/v1/products).

23. Use Authorization

builder.Services.AddAuthorization();
app.UseAuthorization();

app.MapGet("/secure", () => "Secure Data")
   .RequireAuthorization();
  • Supports role-based or policy-based authorization.

24. Async Endpoints

app.MapGet("/delay", async () =>
{
    await Task.Delay(1000);
    return "Done after delay";
});
  • All endpoints can be asynchronous using async/await.

25. Binding From Headers / Claims

app.MapGet("/agent", (HttpRequest req) =>
{
    var userAgent = req.Headers["User-Agent"];
    return $"Your agent: {userAgent}";
});
  • Use HttpRequest or HttpContext to access headers, claims, cookies, etc.

26. Custom Response Codes

app.MapGet("/notfound", () => Results.StatusCode(404));
  • Fine-grained control over HTTP response codes.

27. Conditional Endpoints (MapWhen)

app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments("/admin"), appBuilder =>
{
    appBuilder.Run(async ctx => await ctx.Response.WriteAsync("Admin section"));
});
  • Handle advanced routing logic manually.

28. Bind Form Data

app.MapPost("/upload", async (HttpRequest req) =>
{
    var form = await req.ReadFormAsync();
    var file = form.Files["file"];
    return $"Uploaded: {file?.FileName}";
});
  • Useful for uploading files or handling multipart/form-data.

29. Handle Query, Route, and Body Together

app.MapPost("/mixed/{id}", (int id, string name, MyData data) =>
{
    return $"ID: {id}, Name: {name}, Data: {data.Value}";
});

record MyData(string Value);
  • You can mix route, query, and body parameters.

Comparison: Minimal APIs vs Traditional RESTful APIs (Controllers)

Feature Minimal APIs RESTful APIs (Controllers)
Setup Very low setup, all in Program.cs Needs controllers, attributes, and routing setup
Performance Slightly better (fewer layers) Slightly heavier due to abstraction layers
Best For Small services, microservices, and quick APIs Large-scale apps with many endpoints
Testability Less structured, harder to unit test Easier to test with controllers/services
Routing Code-based (inline) Attribute routing or convention-based
Dependency Injection Direct in lambda Via constructor injection in controllers
Maintainability (Large apps) Harder to scale/organize Better separation of concerns
Swagger Support Fully supported Fully supported
Custom Filters / Middleware Limited (no filters, but use middleware) Full support (filters, middleware, etc.)
Validation (FluentValidation, etc.) Manual or via packages Built-in integration via ModelState
Extensibility Limited to what's in lambdas/middleware Highly extensible (Filters, Bindings, etc.)

When to Use What?

Use Case Recommendation
Quick prototype or POC Minimal API
Microservices / serverless Minimal API
Full-blown business app RESTful API (Controllers)
App needing filters/attributes RESTful API
Highly structured/maintainable code RESTful API

Conclusion

Minimal APIs are a powerful tool for quickly building web APIs with clean and minimal code. They reduce boilerplate and work great for microservices or simple services. But for complex systems, RESTful APIs using controllers still offer more structure and long-term scalability.

Use Minimal APIs when you want speed and simplicity, but switch to RESTful APIs when you need organization, structure, testability, or layered architecture.