Sub-Resource (Nested Resource)
Sub-resources represent entities that only exist in the context of a parent resource. /users/123/orders, /posts/abc/comments. The nesting reflects a strong ownership relationship. Keep nesting to one or two levels maximum – deeper nesting creates brittle URLs and coupling.
Details
Nested resources model the case where one resource logically belongs to another. The nested URL expresses that relationship.
When to nest: - The child resource cannot exist without the parent (/orders/123/line-items) - You always filter by the parent when accessing the child (/posts/abc/comments means only comments for post abc) - Access control is inherited from the parent
When NOT to nest: - The child resource can exist independently (/users/123/tags – tags exist independently) - You need to access the child without knowing the parent (/comments/xyz – you know the comment ID) - The nesting would exceed 2 levels
Maximum depth rule: /level1/{id}/level2/{id} is the deepest commonly recommended. /level1/{id}/level2/{id}/level3/{id} creates brittle client code and makes link sharing unwieldy.
Dual endpoints: for child resources that need both nested and standalone access, provide both: POST /posts/abc/comments – create a comment on post abc GET /comments/xyz – access any comment by its ID directly
URL Examples
| Pattern | Description |
|---|---|
| GET /users/123/orders | All orders for user 123 |
| POST /posts/abc/comments | Create comment on post |
| GET /orders/ord_1/line-items | Line items for an order |
| DELETE /users/123/addresses/addr_5 | Delete a specific address |
Do
- +Use nesting when the child genuinely cannot exist without the parent
- +Limit nesting to 2 levels maximum: /parent/{id}/child/{id}
- +Provide both nested creation (POST /posts/{id}/comments) and direct access (GET /comments/{id})
- +Validate parent ownership in handlers – ensure /users/123/orders only returns user 123's orders
Don't
- !Never nest beyond 2 levels: /a/{id}/b/{id}/c/{id}/d/{id} – break into flatter endpoints
- !Never use nesting when the child exists independently (tags, categories, labels)
- !Never allow accessing /users/123/orders to return orders from other users
Examples
# Create: POST to nested URL
POST /api/v1/posts/post_abc/comments
{ "text": "Great article!" }
→ 201 Created, Location: /api/v1/comments/cmt_xyz
# Read: GET directly (no need to know parent)
GET /api/v1/comments/cmt_xyz
→ 200 OK { "id": "cmt_xyz", "postId": "post_abc", "text": "..." }