After spending a couple of months getting comfortable with C# fundamentals, I decided it was time to build something real. I chose to create a simple REST API for managing a book collection using ASP.NET Core. Here's what I learned along the way.
The Project Idea
I wanted something simple but practical: a Book API where you can create, read, update, and delete books. Nothing fancy, but enough to learn the core concepts of building a web API.
Scaffolding the Project
Creating the project was a single command:
dotnet new webapi -n BookApi
This generated a project with a sample WeatherForecast controller. I spent some time studying its structure before replacing it with my own.
Defining the Model
First, I created a simple Book model:
public class Book
{
public int Id { get; set; }
public required string Title { get; set; }
public required string Author { get; set; }
public int Year { get; set; }
public string? Genre { get; set; }
public bool IsRead { get; set; }
}
I used the required keyword for properties that must always have a value. This was a nice C# 11 feature I picked up from the documentation.
Building the Controller
The controller was where things got interesting. I learned about attribute routing, HTTP verbs, and how ASP.NET Core maps incoming requests to methods:
[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
private static readonly List<Book> _books = new();
private static int _nextId = 1;
[HttpGet]
public ActionResult<IEnumerable<Book>> GetAll()
{
return Ok(_books);
}
[HttpGet("{id}")]
public ActionResult<Book> GetById(int id)
{
var book = _books.FirstOrDefault(b => b.Id == id);
if (book is null) return NotFound();
return Ok(book);
}
[HttpPost]
public ActionResult<Book> Create(Book book)
{
book.Id = _nextId++;
_books.Add(book);
return CreatedAtAction(nameof(GetById), new { id = book.Id }, book);
}
}
Mistakes I Made
Forgetting About Content Types
My first POST request failed because I forgot to set the Content-Type: application/json header. The API returned a 415 Unsupported Media Type, and I spent twenty minutes debugging before I realized the issue. Lesson learned: always check your request headers.
Returning the Wrong Status Codes
Initially, I returned Ok() for everything, including creation. A quick read through REST conventions taught me that 201 Created is the proper response for resource creation, which is what CreatedAtAction provides.
Static Data Is Not Persistence
Using a static list was fine for learning, but I quickly realized that the data disappeared every time I restarted the application. This motivated me to look into Entity Framework Core, which I plan to explore next.
Testing with Swagger
One thing I absolutely loved was the built-in Swagger UI. Opening https://localhost:5001/swagger gave me a fully interactive page to test all my endpoints without needing Postman or curl. I could send requests, see responses, and even inspect the schema of my models.
What Clicked for Me
The request pipeline concept finally made sense. A request comes in, gets routed to a controller, the controller processes it and returns a result, and the framework serializes it to JSON. It sounds obvious in hindsight, but seeing it work end-to-end was an important moment for me.
I also appreciated how much ASP.NET Core does automatically: model binding from JSON, input validation, content negotiation, and consistent error responses. Coming from manually parsing request bodies in other frameworks, this felt like a significant upgrade.
Next Steps
I want to add a proper database with Entity Framework Core, learn about data validation with data annotations, and understand how middleware works. I've also been hearing a lot about dependency injection, which seems to be fundamental to how ASP.NET Core is structured. That will probably be my next deep dive.
Building this small API gave me a lot of confidence. It's one thing to read about RESTful services, but actually building one and seeing the pieces fit together is a completely different experience.
Comments (2)
Could you elaborate on this topic in a follow-up post?
Interesting thought, thanks for adding that.
Good question, I'd like to know too.
This is exactly what I was looking for, thank you!
Leave a Comment