ASP.NET Core  

Building RESTful APIs Using ASP.NET Core– A Practical Book Management API

Introduction

In my day-to-day development work, almost every application I build or maintain depends on APIs. Whether it is a web dashboard, a mobile app, or even a background service, RESTful APIs act as the backbone that connects everything together.

When I first started working with ASP.NET Core Web APIs, I struggled to understand where to start, how controllers talk to data, and how real projects are structured beyond tutorials. Most examples looked perfect on paper but didn’t explain why certain decisions were made.

In this article, I’ll walk you through building a simple but practical RESTful API using ASP.NET Core, based on a Book Management System. This is not just theory — it’s the same pattern I personally use as a foundation before integrating databases, authentication, or cloud deployment.

Step 1: Setting Up the ASP.NET Core Web API Project

I prefer starting with the CLI because it gives a clean, minimal setup and avoids unnecessary UI confusion.

dotnet new webapi -n BookStoreAPI
cd BookStoreAPI

This command creates:

  • A ready-to-run Web API project

  • Basic controller setup

  • Swagger enabled by default (very useful for testing)

At this stage, you can already run the project and see Swagger working, which helps beginners gain confidence early.

Step 2: Defining the Model (Book)

Before writing APIs, I always define what data I’m working with. For this example, we’ll manage books.

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public decimal Price { get; set; }
}

Why this matters:

  • Models represent real-world entities

  • They become the contract between client and server

  • Later, the same model can map directly to a database table

Step 3: Creating a Repository Layer (Why I Use It)

Many beginners directly write logic inside controllers. I used to do that too — until projects grew and became hard to maintain.

To keep things clean, I follow a repository pattern, even for small projects.

Interface

public interface IBookRepository
{
    IEnumerable<Book> GetAll();
    Book GetById(int id);
    void Add(Book book);
    void Update(Book book);
    void Delete(int id);
}

Implementation (In-Memory Data)

public class BookRepository : IBookRepository
{
    private readonly List<Book> _books = new();

    public IEnumerable<Book> GetAll() => _books;

    public Book GetById(int id) =>
        _books.FirstOrDefault(b => b.Id == id);

    public void Add(Book book) =>
        _books.Add(book);

    public void Update(Book book)
    {
        var existing = GetById(book.Id);
        if (existing != null)
        {
            existing.Title = book.Title;
            existing.Author = book.Author;
            existing.Price = book.Price;
        }
    }

    public void Delete(int id)
    {
        var book = GetById(id);
        if (book != null)
            _books.Remove(book);
    }
}

Why in-memory first?

  • Easy to understand

  • No database dependency

  • Perfect for learning API flow
    Later, you can replace this with EF Core or Dapper without changing controllers.

Step 4: Creating the Controller (Where REST Happens)

Controllers expose endpoints that clients consume.

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
    private readonly IBookRepository _repository;

    public BooksController(IBookRepository repository)
    {
        _repository = repository;
    }

    [HttpGet]
    public IActionResult GetAll()
    {
        return Ok(_repository.GetAll());
    }

    [HttpGet("{id}")]
    public IActionResult GetById(int id)
    {
        var book = _repository.GetById(id);
        if (book == null)
            return NotFound("Book not found");

        return Ok(book);
    }

    [HttpPost]
    public IActionResult Add(Book book)
    {
        _repository.Add(book);
        return CreatedAtAction(nameof(GetById),
            new { id = book.Id }, book);
    }

    [HttpPut("{id}")]
    public IActionResult Update(int id, Book book)
    {
        if (id != book.Id)
            return BadRequest("ID mismatch");

        _repository.Update(book);
        return NoContent();
    }

    [HttpDelete("{id}")]
    public IActionResult Delete(int id)
    {
        _repository.Delete(id);
        return NoContent();
    }
}

What I learned from experience:

  • Always return proper HTTP status codes

  • Avoid putting business logic inside controllers

  • Keep controllers thin and readable

Step 5: Registering Dependencies in Program.cs

Without dependency injection, the controller won’t know how to create IBookRepository.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IBookRepository, BookRepository>();

var app = builder.Build();

app.MapControllers();
app.Run();

This single line makes the entire architecture flexible and testable.

Step 6: Testing Using Swagger (My Favorite Tool)

One thing I love about ASP.NET Core is built-in Swagger.

You can test:

  • GET /api/books

  • GET /api/books/{id}

  • POST /api/books

  • PUT /api/books/{id}

  • DELETE /api/books/{id}

Swagger helps validate APIs without writing a frontend, which saves huge development time.

(Here you should add Swagger screenshots – this is VERY important for approval)

Use Case

Imagine a mobile bookstore app:

  • The app fetches available books → GET

  • Admin adds new books → POST

  • Prices change → PUT

  • Old books removed → DELETE

This API becomes the backend engine powering all those actions.

Conclusion

Building RESTful APIs with ASP.NET Core is simple once the fundamentals are clear. Through this Book Management System, I demonstrated how I personally structure APIs using models, repositories, controllers, and dependency injection.

From here, you can extend this project by:

  • Adding Entity Framework Core

  • Implementing authentication (JWT)

  • Adding logging and validation

  • Deploying to Azure or IIS

This foundation is something I consistently use in real projects, and it scales well as applications grow.