Introduction
APIs power almost everything we do online today. From checking the weather on your phone to booking a ride on Uber, APIs are the invisible backbone of our digital world.
Traditionally, building APIs in .NET meant using Controllers with ASP.NET Core. While powerful, controllers can feel heavy for simple APIs. That’s where Minimal APIs come in.
With the release of .NET 8, Minimal APIs have matured into a production-ready, lightweight, and high-performance way to build REST APIs. They reduce boilerplate, improve readability, and help developers quickly spin up APIs without unnecessary complexity.
In this guide, you’ll learn:
What Minimal APIs are in .NET 8
Why they’re better for certain scenarios
Step-by-step setup of a REST API
Complete project architecture
Best practices for scaling APIs
By the end, you’ll have a working REST API in .NET 8 and the confidence to use Minimal APIs in your own projects. 🚀
What Are Minimal APIs in .NET 8?
Minimal APIs were introduced in .NET 6 as a way to simplify API development. Instead of writing a lot of boilerplate code with controllers, attributes, and startup classes, you can define your API endpoints in just a few lines.
In .NET 8, Minimal APIs have become even more powerful with support for:
Improved routing
Dependency Injection (DI)
Model validation
Authentication & Authorization
OpenAPI/Swagger integration
In short: Minimal APIs = Less code, same power.
When Should You Use Minimal APIs?
Minimal APIs are great when:
You’re building small to medium REST APIs
You want faster startup and less code
You need APIs for microservices or serverless apps
You’re prototyping and want to move quickly
For large, complex projects with many modules, Controllers + MVC might still be the better option for organization.
Setting Up a Minimal API in .NET 8 (Step by Step)
Let’s create a REST API for a Book Management System.
Step 1: Create a New Project
Open your terminal and run:
dotnet new webapi -n BookStoreAPI --use-minimal-apis
cd BookStoreAPI
This creates a new Minimal API project.
Step 2: Project Structure
Here’s a recommended folder structure for a clean architecture:
BookStoreAPI/
│-- Program.cs
│-- appsettings.json
│-- Models/
│ └── Book.cs
│-- Data/
│ └── BookDbContext.cs
│-- Repositories/
│ └── IBookRepository.cs
│ └── BookRepository.cs
│-- Services/
│ └── BookService.cs
│-- Endpoints/
│ └── BookEndpoints.cs
This keeps things organized:
Models → Data entities
Data → EF Core DbContext
Repositories → Data access layer
Services → Business logic
Endpoints → Minimal API route definitions
Step 3: Define the Book Model
namespace BookStoreAPI.Models;
public class Book
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public decimal Price { get; set; }
}
Step 4: Setup EF Core DbContext
Data/BookDbContext.cs
using BookStoreAPI.Models;
using Microsoft.EntityFrameworkCore;
namespace BookStoreAPI.Data;
public class BookDbContext : DbContext
{
public BookDbContext(DbContextOptions options) : base(options) { }
public DbSet Books => Set();
}
Register EF Core with SQLite (for simplicity) in Program.cs.
builder.Services.AddDbContext(options =>
options.UseSqlite("Data Source=books.db"));
Step 5: Repository Pattern
Repositories/IBookRepository.cs
using BookStoreAPI.Models;
namespace BookStoreAPI.Repositories;
public interface IBookRepository
{
Task> GetAllAsync();
Task GetByIdAsync(int id);
Task AddAsync(Book book);
Task UpdateAsync(Book book);
Task DeleteAsync(int id);
}
Repositories/BookRepository.cs
using BookStoreAPI.Data;
using BookStoreAPI.Models;
using Microsoft.EntityFrameworkCore;
namespace BookStoreAPI.Repositories;
public class BookRepository : IBookRepository
{
private readonly BookDbContext _context;
public BookRepository(BookDbContext context)
{
_context = context;
}
public async Task> GetAllAsync() =>
await _context.Books.ToListAsync();
public async Task GetByIdAsync(int id) =>
await _context.Books.FindAsync(id);
public async Task AddAsync(Book book)
{
_context.Books.Add(book);
await _context.SaveChangesAsync();
return book;
}
public async Task UpdateAsync(Book book)
{
var existing = await _context.Books.FindAsync(book.Id);
if (existing == null) return null;
existing.Title = book.Title;
existing.Author = book.Author;
existing.Price = book.Price;
await _context.SaveChangesAsync();
return existing;
}
public async Task DeleteAsync(int id)
{
var book = await _context.Books.FindAsync(id);
if (book == null) return false;
_context.Books.Remove(book);
await _context.SaveChangesAsync();
return true;
}
}
Step 6: Define Minimal API Endpoints
Endpoints/BookEndpoints.cs
using BookStoreAPI.Models;
using BookStoreAPI.Repositories;
namespace BookStoreAPI.Endpoints;
public static class BookEndpoints
{
public static void MapBookEndpoints(this IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("/api/books");
group.MapGet("/", async (IBookRepository repo) =>
{
var books = await repo.GetAllAsync();
return Results.Ok(books);
});
group.MapGet("/{id}", async (int id, IBookRepository repo) =>
{
var book = await repo.GetByIdAsync(id);
return book is not null ? Results.Ok(book) : Results.NotFound();
});
group.MapPost("/", async (Book book, IBookRepository repo) =>
{
var newBook = await repo.AddAsync(book);
return Results.Created($"/api/books/{newBook.Id}", newBook);
});
group.MapPut("/{id}", async (int id, Book book, IBookRepository repo) =>
{
if (id != book.Id) return Results.BadRequest();
var updated = await repo.UpdateAsync(book);
return updated is not null ? Results.Ok(updated) : Results.NotFound();
});
group.MapDelete("/{id}", async (int id, IBookRepository repo) =>
{
var deleted = await repo.DeleteAsync(id);
return deleted ? Results.NoContent() : Results.NotFound();
});
}
}
Step 7: Wire Everything in Program.cs
Program.cs
using BookStoreAPI.Data;
using BookStoreAPI.Repositories;
using BookStoreAPI.Endpoints;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext(options =>
options.UseSqlite("Data Source=books.db"));
builder.Services.AddScoped();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapBookEndpoints();
app.Run();
Step 8: Run the API
Run the project:
dotnet run
Visit Swagger at:
👉 https://localhost:5001/swagger
You now have a fully functional REST API!
Advantages of Minimal APIs in .NET 8
🚀 Faster Development – Less boilerplate code
🧩 Modular Design – Easily group endpoints
⚡ Performance – Lower memory usage and faster startup
🛡️ Security – Supports authentication & authorization middleware
📖 Swagger Support – Automatic documentation
Best Practices for Minimal APIs in .NET 8
Use Layers (Repo/Services) – Don’t dump logic in endpoints.
Add Validation – Use FluentValidation or data annotations.
Enable Logging & Monitoring – Helps in production debugging.
Secure APIs – Add JWT authentication for sensitive data.
Use DTOs (Data Transfer Objects) – Avoid exposing database models directly.
Version Your API – Example:
/api/v1/books
When to Use Controllers Instead
Minimal APIs are amazing, but controllers are still better when:
Large, enterprise-scale projects
Complex routing and filters
MVC + Razor Pages integration
Heavy dependency on attributes
Conclusion
Minimal APIs in .NET 8 are a game-changer for developers who want to build fast, clean, and scalable REST APIs.
With just a few lines of code, you can spin up endpoints, connect to a database, and expose APIs ready for production. For microservices, serverless apps, or small-to-medium APIs, Minimal APIs are the best choice in 2025.
Now that you’ve seen a real-world BookStore API example with architecture, it’s your turn. Start small, experiment, and soon you’ll be shipping high-performance APIs with confidence.
🚀 Next step: Try adding authentication with JWT or connect this API to a React/Angular frontend. That’s how you level up from learning to real-world application.
Let’s Connect & Learn Together!
I hope this curated list of GitHub repositories helps you grow in your developer journey in 2025 and beyond. I regularly share coding resources, learning roadmaps, project ideas, and career tips across multiple platforms. If you found this helpful, consider following me and joining the Logic Lense community!
Instagram – @logiclense
YouTube – Logic Lense
LinkedIn – Connect with me
Website – www.logiclense.com
Let’s code, grow, and innovate — together.
Subscribe to ASP.NET Core Newsletter.
Want to advance your career in .NET and Architecture? Join 1,000+ readers of my newsletter. Each week you will get 1 practical tip with best practices and real-world examples.
