Building REST APIs with Minimal APIs in .NET 8 (Beginner-Friendly Guide)

Building REST APIs with Minimal APIs in .NET 8

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<BookDbContext> options) : base(options) { }

    public DbSet<Book> Books => Set<Book>();
}

				
			

Register EF Core with SQLite (for simplicity) in Program.cs.

				
					builder.Services.AddDbContext<BookDbContext>(options =>
    options.UseSqlite("Data Source=books.db"));
				
			

Step 5: Repository Pattern

Repositories/IBookRepository.cs

				
					using BookStoreAPI.Models;

namespace BookStoreAPI.Repositories;

public interface IBookRepository
{
    Task<IEnumerable<Book>> GetAllAsync();
    Task<Book?> GetByIdAsync(int id);
    Task<Book> AddAsync(Book book);
    Task<Book?> UpdateAsync(Book book);
    Task<bool> 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<IEnumerable<Book>> GetAllAsync() =>
        await _context.Books.ToListAsync();

    public async Task<Book?> GetByIdAsync(int id) =>
        await _context.Books.FindAsync(id);

    public async Task<Book> AddAsync(Book book)
    {
        _context.Books.Add(book);
        await _context.SaveChangesAsync();
        return book;
    }

    public async Task<Book?> 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<bool> 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<BookDbContext>(options =>
    options.UseSqlite("Data Source=books.db"));

builder.Services.AddScoped<IBookRepository, BookRepository>();
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

  1. Use Layers (Repo/Services) – Don’t dump logic in endpoints.

  2. Add Validation – Use FluentValidation or data annotations.

  3. Enable Logging & Monitoring – Helps in production debugging.

  4. Secure APIs – Add JWT authentication for sensitive data.

  5. Use DTOs (Data Transfer Objects) – Avoid exposing database models directly.

  6. 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.

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Cart
0%