Skip to content

Nesting & Relationships

Resources relate to each other, and the URI structure can express some of those relationships. The trick is nesting enough to be intuitive without building brittle, deeply nested paths.

When one resource clearly belongs to another, nest it one level:

GET /articles/42/comments # comments of article 42
POST /articles/42/comments # add a comment to article 42
GET /users/7/articles # articles written by user 7
flowchart LR
  A[/articles/42/] --> C[/articles/42/comments/]
  C --> CI[/articles/42/comments/9/]
One level of nesting reads naturally; deeper gets unwieldy

Stop at one, occasionally two, levels. A path like /users/7/articles/42/comments/9/reactions/3 is hard to read and couples resources that should stand alone. Once a sub-resource has its own identity, give it a top-level URI too and link to it instead of nesting further:

GET /comments/9 # the comment as a first-class resource
GET /articles/42/comments # still fine as a scoped collection

Rather than embedding everything, return references the client can follow:

JavaScript

Nesting too deep Symptom:

  • /companies/\{id\}/departments/\{id\}/teams/\{id\}/members/\{id\}/tasks/\{id\}
  • URIs get very long and are hard to maintain and document
  • Cap nesting at two levels, or flatten to /tasks/\{id\} and filter with query parameters

A many-to-many relationship modeled as nested Symptom:

  • /users/\{id\}/tags/\{id\} — a user has many tags, a tag has many users
  • Nesting makes ownership unclear — does deleting remove the tag from the user or delete it entirely?
  • Use a junction resource instead: POST /user-tags with { userId, tagId }

💡 Real-world examples

GitHub API:

  • /repos/\{owner\}/\{repo\}/issues — a clear 2 levels
  • /repos/\{owner\}/\{repo\}/issues/\{number\}/comments — 3 levels max

Stripe API:

  • /customers/\{id\}/payment_methods — nested for an owned resource
  • /payment_methods/\{id\} — accessible directly once you know the ID

Sources: GitHub REST API — issues, Stripe API — payment methods

How deep should URI nesting usually go?
Once a sub-resource has its own stable identity, you should:
What does `POST /articles/42/comments` express?