APIs are contracts
Before you write a single route
You’re about to build a REST API from scratch. Over the next few lessons, we’ll put together a bookstore API, complete with books, authors, and reviews. But before we write any code, we need to talk about something that most developers skip: design.
Why? Because your API is not just code. It’s a promise.
An API is a promise
When you publish an API, every developer who uses it is trusting you. They’re writing code that depends on your endpoints. Their apps depend on it. Their users depend on it.
If you change a field name, their app breaks. If you return data in a different shape, their frontend crashes. If your error messages are inconsistent, they spend hours debugging instead of building.
That’s why we say an API is a contract. You’re telling every consumer: “Send a request like this, and I guarantee you’ll get a response like that.” Once that contract is out there, changing it is expensive.
The cost of a bad API
Let’s make this concrete. Here are two endpoints that do the exact same thing, returning a list of books:
BAD: POST /api/getBooks { "page": 1, "pageSize": 10, "filterBy": "genre", "filterValue": "fiction" }
GOOD: GET /books?genre=fiction&limit=10&cursor=abc123 They return the same data. So what’s the difference?
The bad version uses POST for something that’s just reading data. That breaks caching, because browsers and CDNs only cache GET requests. It puts query parameters in the request body, which means you can’t bookmark the URL or share it as a link. And it invents a custom filter system (filterBy + filterValue) that no developer has ever seen before.
The good version uses GET, which is cacheable and bookmarkable. It puts filters in query parameters, which is standard. And it uses cursor-based pagination, which is reliable even when new data gets added.
A developer who has used any well-designed API before can look at the good version and immediately guess how it works. The bad version requires them to read documentation just to make a simple request.
Design first, implement second
Most developers build APIs backwards. They write their database queries and function signatures first, then expose whatever shape falls out of that as the API. The result is an API that looks like the code, not like the domain.
The better approach is to flip it around. Before you write any code, ask yourself: if I were the developer consuming this API, what would I want to call? What would the request look like? What would the response look like?
That’s what this course teaches. We’ll design the API first, then implement it. Once the design is right, the implementation follows naturally.
What we’re building
Our domain is a bookstore. It’s simple enough to learn from but complex enough to demonstrate every REST pattern you’ll encounter in the real world:
- Books have a title, author, genre, publication date, and ISBN
- Authors have a name, biography, and the books they wrote
- Reviews have a rating, text, reviewer name, and the book they belong to
This gives us one-to-many relationships (an author writes many books, a book has many reviews), nested resources (reviews live under books), and enough data to demonstrate pagination, filtering, and sorting.
This is exactly the kind of domain you’d see in a production API. The patterns we learn here apply whether you’re building a bookstore, a social network, or a payment system.
Exercises
Exercise 1: Find an API you use regularly (GitHub, Stripe, Twilio). Look at three endpoints. What makes them easy or hard to use?
Exercise 2: Think about an app you’ve built or want to build. If you were designing its API from scratch, what resources would you expose? What URLs would you use?
Why does using POST for a read operation hurt API consumers?