REST API Nested Resources
Real data connects. A customer places orders, an order holds items, and a blog post gathers comments. REST expresses these links through URLs that mirror the structure of the data. Well-designed nesting makes an API feel natural, and careless nesting produces long, brittle paths.
The Folder Picture
Picture the folders on your computer. A folder named Customers holds a folder for each customer, and each customer folder holds an Orders folder. The path to a file tells you exactly where it lives. Nested URLs work the same way. The path /customers/42/orders/7 means order 7 inside the orders of customer 42.
The Resource Tree
/customers
└── /customers/42
├── /customers/42/profile
└── /customers/42/orders
└── /customers/42/orders/7
└── /customers/42/orders/7/items
When Nesting Fits
Use nesting when a child resource cannot exist without its parent. An order line item has no meaning outside its order. A comment belongs to one post. A chapter belongs to one book. These strong ownership links suit nested paths.
Common Operations
| Request | Meaning |
|---|---|
| GET /customers/42/orders | List all orders of customer 42 |
| POST /customers/42/orders | Create an order for customer 42 |
| GET /customers/42/orders/7 | Read order 7 of customer 42 |
| DELETE /customers/42/orders/7 | Remove order 7 |
The Two-Level Rule
Limit nesting to two levels. A path such as /customers/42/orders/7/items/3/discounts/9 forces clients to know five identifiers and makes every URL fragile. Once a resource has its own unique id, expose it at the top level.
Deep path (hard to use): /customers/42/orders/7/items/3 Flat path (easy to use): /items/3 /orders/7/items
Flattening with Query Parameters
Many-to-many links do not fit a single parent. A student joins many courses, and a course holds many students. Expose each side as its own collection and filter by relationship.
GET /courses?studentId=15 GET /students?courseId=3
Embedding vs Linking
A response can include related data directly or point to it with a link.
Embedded Data
{
"id": 7,
"customer": { "id": 42, "name": "Asha" },
"total": 120
}
Embedding saves a request and suits small, frequently needed data.
Linked Data
{
"id": 7,
"customerUrl": "/customers/42",
"itemsUrl": "/orders/7/items",
"total": 120
}
Links keep responses small and let clients fetch extra data only when they need it. An optional query parameter such as ?include=customer lets the client choose.
A Library Example
A library API manages members, loans, and books. A loan belongs to one member, so the path /members/18/loans lists every loan for member 18. A book exists on its own shelf, so /books/905 stands at the top level. A loan links to its book through a field such as bookUrl. This split keeps ownership clear and avoids copying book data into every loan.
/members/18/loans ──→ loan 3 ──→ bookUrl: /books/905
──→ loan 8 ──→ bookUrl: /books/117
Clients read the loan list first and follow each book link only when the screen needs book details.
Naming Tips
- Use plural nouns for collections: /orders, /items.
- Keep verbs out of paths. Write POST /orders/7/cancellation instead of /orders/7/cancelOrder.
- Use lowercase letters and hyphens for multi-word names, such as /order-items.
- Check that the parent exists before returning child data, and return 404 when it does not.
Common Mistakes
Mismatched Parent and Child
A request for /customers/42/orders/7 must confirm that order 7 belongs to customer 42. Skipping that check lets one customer read another customer's data.
Deep Chains Everywhere
Developers sometimes nest every relationship out of habit. Ask whether the child can stand alone, and flatten the path when it can.
Mixed Styles
One endpoint using /orders/7/items and another using /order-items?orderId=7 for the same data confuses users. Pick one pattern per relationship and use it consistently.
