Chapter 03 — How APIs are designed6 min read

Resources and relationships

One table isn't enough

In the previous lessons, we looked at single tables: Users, Products. But real apps don't have one table. They have dozens, sometimes hundreds, all connected to each other.

Think about Uber. There's a Users table, a Drivers table, a Rides table, a Payments table, a Ratings table. Each one holds different data, but they all reference each other. A ride belongs to a user and a driver. A payment belongs to a ride. A rating belongs to a ride too.

These connections between tables are called relationships. And they're the reason your engineer sometimes says "that's more complicated than it sounds."


Relationships, the spreadsheet way

Let's look at two tables. First, a simple Users table:

idnameemail
1Alice Martinalice@acme.com
2Bob Chenbob@startup.io
3Clara Diazclara@bigcorp.com

Now an Orders table:

iduser_idproducttotalstatus
1011Wireless Mouse2500delivered
1021USB-C Hub4500shipped
1032Desk Lamp3200delivered
1043Keyboard8900processing
1053Monitor Stand5500delivered

See the user_id column? That's the link. Order #101 has user_id: 1, which means it belongs to Alice (id = 1 in the Users table). Order #103 has user_id: 2, so it belongs to Bob.

If you use spreadsheets, this is like a VLOOKUP. The user_id column says "go to the Users table and find the row with this ID." That's how databases connect tables together.

This one relationship answers tons of questions:

  • "What are Alice's orders?" → filter Orders where user_id = 1
  • "Who placed order #104?" → look up user_id = 3 → Clara
  • "How many orders does Bob have?" → count rows in Orders where user_id = 2 → one

Nested routes

REST conventions have a way to express these relationships in URLs. They're called nested routes:

GET /users/1/posts

Read it left to right: "Go to Users, find user #1, then get their posts." The URL mirrors the relationship in the database.

Let's try it. DummyJSON supports nested routes. Here are user #1's posts:

GEThttps://dummyjson.com/users/1/posts

Now user #1's todos:

GEThttps://dummyjson.com/users/1/todos

And the comments on post #1:

GEThttps://dummyjson.com/posts/1/comments

The pattern is always the same: /parent/id/children. The URL tells you exactly which relationship you're following. Once you know the resource names, you can often guess the nested route without reading the docs.


Why your engineer says "that's complicated"

Now for the practical part. You're a PM, you have a feature idea, and your engineer says "that's more complicated than you think." Here are three scenarios that explain why.

Scenario 1: "Show order history on the user profile"

Your Users table and Orders table are already connected by user_id. The relationship exists. The data is there. The engineer just needs to call GET /users/1/orders and display the results.

Verdict: straightforward. The data model already supports it.

Scenario 2: "Let users favorite products"

Right now, you have a Users table and a Products table. But there's no connection between them for favorites. Where do you store the fact that Alice favorited the Wireless Mouse?

You can't just add a favorites column to the Users table. What would it contain? A list of product IDs? That breaks how databases work (one value per cell, just like a spreadsheet).

The answer: you need a new table.

iduser_idproduct_idcreated_at
11422024-06-10
21172024-06-11
32422024-06-12
4382024-06-15

This is a Favorites table. Each row means "this user favorited this product on this date." Alice (user 1) favorited products 42 and 17. Bob (user 2) also favorited product 42.

This pattern is called many-to-many: many users can favorite many products, and many products can be favorited by many users. It always requires a new table in the middle.

That's why the engineer says it's complicated. It's not just showing data. It's creating a whole new table, new endpoints (GET /users/1/favorites, POST /users/1/favorites, DELETE /users/1/favorites/42), and new logic.

Scenario 3: "Add tags to products"

Sounds simple, right? "Just add a tags field." But think about it: a product can have many tags, and a tag can belong to many products. That's many-to-many again. You need:

  • A Tags table (id, name)
  • A ProductTags table (id, product_id, tag_id)

Two new tables for what sounded like "just a field." This is why PMs who understand data models write better specs and have more productive conversations with engineers.


Key takeaways

  • Real apps have many tables connected through ID references (like user_id)
  • A user_id column in Orders is like a VLOOKUP to the Users table
  • Nested routes (/users/1/posts) express these relationships in URLs
  • Many-to-many relationships (favorites, tags) always require a new table
  • When an engineer says "it's complicated," they often mean the data model needs to change