Designing Resource URIs
1. Using Nouns for Resources
URIs identify resources (nouns), not actions (verbs). Use HTTP methods for verbs.
| Anti-Pattern | RESTful |
|---|---|
GET /getUsers | GET /users |
POST /createUser | POST /users |
POST /deleteUser/42 | DELETE /users/42 |
POST /updateUserEmail | PATCH /users/42 |
2. Using Plural Nouns
| Resource Type | Convention | Example |
|---|---|---|
| Collection | Plural noun | /orders, /users |
| Single Item | Plural + ID | /orders/100 |
| Singleton | Singular noun | /users/42/profile (only one) |
3. Structuring Hierarchical Resources
| Pattern | Example | Meaning |
|---|---|---|
| Parent/Child | /users/42/orders | All orders for user 42 |
| Grandchild | /users/42/orders/100/items | Items in order 100 |
| Max Depth | 2-3 levels | Deeper → flatten with query params |
Note: Avoid > 3 levels of nesting. Use
/items?orderId=100&userId=42 instead.4. Using Lowercase and Hyphens
| Bad | Good | Reason |
|---|---|---|
/UserProfile | /user-profiles | URIs case-sensitive; lowercase universal |
/user_profiles | /user-profiles | Hyphens better for SEO & readability |
/userProfiles | /user-profiles | camelCase reserved for body fields |
5. Avoiding File Extensions
| Bad | Good | Use Instead |
|---|---|---|
/users/42.json | /users/42 | Accept: application/json header |
/users/42.xml | /users/42 | Accept: application/xml header |
6. Designing Collection vs Singleton Resources
| Type | URI Pattern | Operations |
|---|---|---|
| Collection | /orders | GET (list), POST (create) |
| Item | /orders/{id} | GET, PUT, PATCH, DELETE |
| Singleton | /users/{id}/profile | GET, PUT, PATCH (no POST/DELETE) |
7. Handling Nested Resources
Example: Nested Resource Endpoints
GET /users/42/orders # List user's orders
POST /users/42/orders # Create order for user
GET /users/42/orders/100 # Specific order
DELETE /users/42/orders/100 # Cancel user's order
# Alternative (flat) - preferred when child has independent identity
GET /orders?userId=42
GET /orders/100
| When to Nest | When to Flatten |
|---|---|
| Child has no meaning without parent | Child has independent identity |
| Strong containment (1:N) | Cross-cutting queries needed |
8. Using Resource Identifiers
| ID Type | Example | Pros | Cons |
|---|---|---|---|
| Integer | /users/42 | Compact, sortable | Enumerable, leaks count |
| UUID | /users/550e8400-e29b-... | Unguessable, distributed-safe | Long, not human-friendly |
| Slug | /articles/intro-to-rest | SEO-friendly, readable | Mutable, collision risk |
| ULID/NanoID | /orders/01ARZ3NDEK... | Sortable + unguessable | Newer, less tooling |
9. Designing Action-Based Endpoints
Some operations don't fit CRUD. Use sub-resources or controller endpoints sparingly.
| Action | RESTful Approach |
|---|---|
| Send email | POST /users/42/emails (create email resource) |
| Cancel order | POST /orders/100/cancel or PATCH /orders/100 {"status":"cancelled"} |
| Search | GET /users/search?q=john or GET /users?q=john |
| Login | POST /sessions or POST /auth/login |
| Bulk import | POST /users/import |
10. Handling Special Characters in URIs
| Character | Encoded | Use |
|---|---|---|
(space) | %20 or + (query) | Always encode |
/ | %2F | If part of ID, not separator |
? | %3F | In path segments |
# | %23 | Always (fragment delimiter) |
& | %26 | In query values |
| Unicode | UTF-8 → percent-encode | e.g. caf%C3%A9 |
11. Using Query Parameters
| Purpose | Example |
|---|---|
| Filter | ?status=active&role=admin |
| Sort | ?sort=-createdAt,name |
| Paginate | ?page=2&limit=20 |
| Field Select | ?fields=id,name,email |
| Expand | ?expand=author,comments |
| Search | ?q=john |
Note: Path = identification, Query = modification. Never use query params to identify resources.