REST API 02 ๐ Resources, URIs, and Collection Naming Conventions
A REST API’s URI structure is not cosmetic. It is the contract that clients navigate, the surface that documentation describes, and the layer where the difference between a well-designed API and an inconsistent one becomes visible. The uniform interface constraint from the previous chapter requires that every resource be identified by a URI, but it does not dictate how those URIs should look. That is where naming conventions come in: a consistent set of rules for naming resources, collections, and the relationships between them.
The conventions are not arbitrary either. They follow from the resource model. A resource is any information that can be namedโa user, an order, a collection of products. A collection is a resource that is a set of other resources. A sub-resource is a resource that exists only in the context of another resource. The URI structure reflects this model: collections are plural nouns, individual resources are identified by an identifier after the collection, and sub-resources are nested under their parent . The noun/verb distinction is the most fundamental rule: URIs identify things, HTTP methods perform actions.
Key point: REST URIs use plural nouns for collections (/users), append identifiers for individual resources (/users/42), and nest sub-resources under their parent (/users/42/orders); they do not contain verbs (use HTTP methods instead), and they use query parameters for filtering, sorting, and pagination rather than path segments.
Why URI conventions matter
The predictability problem. A client developer who has learned that GET /users returns a list and GET /users/42 returns one user can guess that GET /orders returns orders and GET /orders/7 returns one order. The structure is predictable. When an API mixes /getUser, /users/list, and /user?id=42, every endpoint must be looked up individually, and the mental model never forms. Consistency is not a luxuryโit is the property that makes an API usable without reading the entire documentation .
The resource model problem. The URI structure should mirror the resource model. If orders belong to users, the URI should reflect that: /users/42/orders rather than /orders?user_id=42. Both work, but the nested structure communicates the relationship and makes the access pattern visible. The nested form answers “what are this user’s orders?” The query form answers “which orders have this user ID?” These are different questions, and the URI should express which one is being asked .
The caching problem. Caches key on the URI. If GET /users/42 returns the same representation regardless of who asks, it is cacheable. If the URI includes session-specific query parameters (/users/42?session=abc), the cache hit rate drops to near zero. Clean, stable URIs are a precondition for the cacheable constraint from the previous chapter.
The versioning problem. URI structure is part of the API’s public contract. Changing a collection name from /users to /accounts is a breaking change for every client. Established conventionsโplural nouns, stable identifiers, nested resourcesโreduce the likelihood of a redesign because the structure follows the domain model rather than a transient preference.
a. Plural nouns for collections, identifiers for individual resources
A collection is a set of resources. Its URI uses a plural noun: /users, /orders, /products. An individual resource is identified by appending its identifier to the collection URI: /users/42, /orders/7, /products/abc-123 .
GET /users # List all users
POST /users # Create a new user
GET /users/42 # Retrieve user 42
PUT /users/42 # Replace user 42
PATCH /users/42 # Partially update user 42
DELETE /users/42 # Delete user 42
The plural form is the convention because the collection is the resource, and the individual member is a subset. Using singular (/user) works technically but is inconsistentโGET /user returns a list, which contradicts the singular noun. Using a verb (/getUsers) violates the uniform interface because the action is already expressed by the HTTP method.
The identifier should be stable and opaque to the client. A numeric ID, a UUID, or a slug are all acceptable. The client should treat the identifier as a string and should not parse it for meaning . If the server changes from numeric IDs to UUIDs, the client that treated the ID as opaque continues to work.
b. Nested resources for relationships
When a resource exists only in the context of another resource, the URI nests it under the parent. An order belongs to a user, so the URI is /users/42/orders. A comment belongs to a post, so the URI is /posts/7/comments. The nesting depth should be limitedโone or two levels is the practical maximum. Three or more levels (/users/42/orders/7/items/3/reviews) produce URIs that are difficult to read and difficult to cache .
GET /users/42/orders # List orders for user 42
POST /users/42/orders # Create an order for user 42
GET /users/42/orders/7 # Retrieve order 7 for user 42
The nested form is not always the right choice. If the child resource can exist independentlyโif orders can be queried across all usersโthen a top-level collection with a filter is better: /orders?user_id=42. The rule of thumb is: nest when the child cannot exist without the parent, and use a query parameter when the child has its own identity and can be accessed independently .
A common mistake is mixing the two forms inconsistently. If GET /users/42/orders returns the user’s orders, then GET /orders/7 should also work for direct access. The nested URI is a convenience for the relationship, not a replacement for the top-level resource.
c. Query parameters for filtering, sorting, and pagination
Query parameters are for modifying the representation of a collection, not for identifying resources. Filtering (?status=active), sorting (?sort=created_at&order=desc), pagination (?page=2&per_page=20), and field selection (?fields=id,name) all belong in the query string .
GET /users?status=active&sort=name&page=2&per_page=20
GET /orders?user_id=42&status=shipped
GET /products?category=electronics&min_price=100&max_price=500
The rule is: if the parameter changes which resources are returned, it is a filter and belongs in the query. If it changes which resource is identified, it belongs in the path. /users/42 identifies user 42. /users?status=active identifies a subset of the users collection. The distinction is subtle but consistent: path segments are for identity, query parameters are for selection and presentation .
Pagination deserves a specific note because it has multiple common conventions. Offset-based pagination (?page=2&per_page=20) is simple but has the “shifting window” problem when items are inserted or deleted during traversal. Cursor-based pagination (?cursor=abc123&limit=20) is stable but more complex to implement. Both are valid; the choice depends on the data’s mutability and the client’s needs.
Complete Example Session
The following session traces a set of URIs for a blog API and shows the resource model that each URI expresses.
// ============================================
// PART 1: THE COLLECTIONS
// ============================================
// Plural nouns identify collections
GET /posts # List all posts
POST /posts # Create a post
GET /users # List all users
POST /users # Create a user
GET /comments # List all comments (top-level, queryable)
// ============================================
// PART 2: INDIVIDUAL RESOURCES
// ============================================
// Identifier appended to the collection
GET /posts/7 # Retrieve post 7
PUT /posts/7 # Replace post 7
DELETE /posts/7 # Delete post 7
GET /users/42 # Retrieve user 42
// ============================================
// PART 3: NESTED RESOURCES
// ============================================
// Comments belong to a post, so they nest under it
GET /posts/7/comments # List comments for post 7
POST /posts/7/comments # Create a comment on post 7
GET /posts/7/comments/3 # Retrieve comment 3 on post 7
// ============================================
// PART 4: QUERY PARAMETERS FOR SELECTION
// ============================================
// Filter, sort, and paginate a collection
GET /posts?status=published&sort=created_at&order=desc&page=1&per_page=10
// ============================================
// PART 5: WHEN NOT TO NEST
// ============================================
// Comments can be queried across all posts,
// so a top-level collection with a filter is also valid
GET /comments?post_id=7
// ============================================
// PART 6: WHAT NOT TO DO
// ============================================
// Verbs in the URI (use HTTP methods instead)
GET /getPosts
POST /createPost
// Singular collection names (inconsistent with list response)
GET /post
// Deep nesting (hard to read, hard to cache)
GET /users/42/posts/7/comments/3/replies/1
// Session-specific parameters in the URI (breaks caching)
GET /posts?sessionid=abc123
The six parts show the resource model in practice: collections use plural nouns, individual resources append identifiers, nested resources express ownership, query parameters handle filtering and pagination, and the anti-patterns show what to avoid. What the session demonstrates is that the URI structure is a direct expression of the resource model, and consistency is what makes the API predictable.
Quick Reference
URI Patterns
| Pattern | Example | Meaning |
|---|---|---|
| Collection | /users | Set of users |
| Individual resource | /users/42 | One user |
| Nested collection | /users/42/orders | Orders belonging to user 42 |
| Nested individual | /users/42/orders/7 | Order 7 for user 42 |
| Filter | /users?status=active | Subset of users |
| Sort | /users?sort=name&order=asc | Ordered collection |
| Pagination | /users?page=2&per_page=20 | Page of results |
HTTP Methods on Resources
| Method | Collection | Individual |
|---|---|---|
| GET | List | Retrieve |
| POST | Create | Not typical |
| PUT | Not typical | Replace |
| PATCH | Not typical | Partial update |
| DELETE | Not typical | Remove |
Naming Rules
| Rule | Do | Don’t |
|---|---|---|
| Noun, not verb | /users | /getUsers |
| Plural collection | /users | /user |
| Identifier in path | /users/42 | /users?id=42 |
| Nest for ownership | /users/42/orders | /orders?user=42 (if owned) |
| Query for filtering | /users?status=active | /users/active |
Nesting Depth
| Depth | Example | Verdict |
|---|---|---|
| 1 | /users/42 | Good |
| 2 | /users/42/orders | Acceptable |
| 3+ | /users/42/orders/7/items | Avoid |
Best Practices
โ Do This:
GET /users # Plural collection
GET /users/42 # Identifier in path
GET /users/42/orders # Nested ownership
GET /orders?status=shipped # Query for filtering
DELETE /users/42 # Method expresses action
โ Don’t Do This:
GET /getUser # Verb in URI
GET /user # Singular collection
GET /users/42/orders/7/items/3 # Deep nesting
POST /users/42/delete # Action in path
GET /orders?sessionid=abc # Session in URI (breaks caching)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Verb in URI | RPC mindset | Use HTTP method |
| Singular collection | Inconsistent with list | Plural noun |
| ID in query for individual | Convenience | /users/42 not /users?id=42 |
| Deep nesting | Over-modeling relationships | Flatten after two levels |
| Filter as path segment | Confusing selection with identity | Use query parameter |
Real-World Examples
1. Collection
GET /users
2. Individual resource
GET /users/42
3. Nested collection
GET /users/42/orders
4. Filtered collection
GET /orders?status=shipped
5. Sorted collection
GET /users?sort=name&order=asc
6. Paginated collection
GET /posts?page=2&per_page=20
7. Create in collection
POST /users
8. Replace individual
PUT /users/42
9. Delete individual
DELETE /users/42
10. Top-level access to nested resource
GET /comments?post_id=7
Visual
Resource Model to URI
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ RESOURCE MODEL โ
โ User โ
โ โโโ has many Orders โ
โ โ โโโ has many Items โ
โ โโโ has one Profile โ
โ โ
โ URI STRUCTURE โ
โ /users (collection) โ
โ /users/42 (individual) โ
โ /users/42/orders (nested collection) โ
โ /users/42/orders/7 (nested individual) โ
โ /users/42/profile (sub-resource) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Path vs Query
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PATH (identity) โ
โ /users/42 โ
โ โ identifies user 42 โ
โ โ
โ QUERY (selection/presentation) โ
โ /users?status=active โ
โ โ selects from the users collection โ
โ โ
โ /users/42?fields=name,email โ
โ โ identifies user 42, selects fields โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Nesting Decision
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Can the child exist without the parent? โ
โ โโ NO โ nest โ
โ โ /users/42/orders โ
โ โโ YES โ top-level + filter โ
โ /orders?user_id=42 โ
โ โ
โ Depth > 2? โ
โ โโ YES โ flatten or use query โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Method Mapping
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ /users โ
โ GET โ list โ
โ POST โ create โ
โ โ
โ /users/42 โ
โ GET โ retrieve โ
โ PUT โ replace โ
โ PATCH โ partial update โ
โ DELETE โ remove โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Collection URI | Plural noun (/users) |
| Individual URI | Identifier in path (/users/42) |
| Nested URI | Parent + child (/users/42/orders) |
| Filtering | Query parameter (/users?status=active) |
| Sorting | Query parameter (?sort=name&order=asc) |
| Pagination | Query parameter (?page=2&per_page=20) |
| Verbs in URI | Not allowed |
| Nesting depth | 2 levels maximum |
| ID in query | Avoid for individual resources |
Key takeaways:
- URIs identify resources, not actions. Use plural nouns for collections and append identifiers for individual resources. The action is expressed by the HTTP method, not the path .
- Nest when the child is owned. A resource that cannot exist without its parent belongs under the parent’s URI. A resource that can be queried independently uses a top-level collection with a filter .
- Query parameters are for selection and presentation. Filtering, sorting, pagination, and field selection belong in the query string. Identity belongs in the path .
- Limit nesting to two levels. Deeper nesting produces URIs that are hard to read, hard to cache, and hard to document. Flatten or use query parameters instead .
- Identifiers are opaque. The client should not parse the ID for meaning. Numeric IDs, UUIDs, and slugs are all valid as long as they are stable and unique .
- Consistency is the property that matters. A predictable URI structure lets client developers guess endpoints correctly and reduces the documentation burden. Inconsistency forces every endpoint to be looked up individually .
Remember: The URI is the API’s most visible surface. It is what clients type, what documentation lists, and what caches key on. The conventionsโplural nouns for collections, identifiers in paths, nesting for ownership, query parameters for filteringโare not arbitrary rules. They follow from the resource model, and they produce an API that is predictable, cacheable, and consistent. When the URI structure mirrors the domain model, the API becomes self-explanatory. When it does not, every endpoint becomes a special case, and the client developer’s mental model never forms.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!