Loading content...
If REST is built on resources, then naming those resources is one of the most consequential design decisions you'll make. URIs are the addresses of your resources—they're what developers type, bookmark, share, and embed in their code. Poor naming creates confusion, inconsistency, and maintenance burdens that compound over time.
The best API URIs feel obvious. When a developer encounters them for the first time, they immediately understand what they represent and can predict related endpoints without consulting documentation. This clarity doesn't happen by accident—it emerges from consistent application of naming principles.
In this page, we'll explore the patterns and conventions that produce exceptional resource naming. You'll learn to model domains as resources, structure hierarchies correctly, handle edge cases, and avoid the common pitfalls that plague many APIs.
By the end of this page, you will be able to design resource URIs that are intuitive, consistent, and evolvable. You'll understand when to use collection resources vs. singleton resources, how to model hierarchical relationships, and how to handle complex resource relationships. These naming skills will make your APIs self-documenting and predictable.
Before diving into patterns, let's establish the anatomy of a URI and the constraints that govern resource naming.
A URI (Uniform Resource Identifier) consists of several components:
scheme://authority/path?query#fragment
https://api.example.com/v1/users/123?include=orders#section1
For REST API design, we primarily work with:
/v1/users/123)?include=orders)Key principles:
/) denote hierarchical relationshipsTim Berners-Lee's famous essay 'Cool URIs Don't Change' applies directly to API design. Every URI you publish becomes a commitment. Clients will hardcode them, bookmark them, and build systems around them. Changing URIs is a breaking change that cascades through all dependent systems. Design URIs to last.
Valid characters in URI paths:
a-z0-9- (preferred for word separation)_ (some APIs use these; pick one and be consistent)/ (hierarchy separator—should not appear in resource names)Characters to avoid:
%20).json, .xml)—use content negotiation instead12345678910111213141516171819
# GOOD: Lowercase, hyphens for word separationGET /api/v1/user-accounts/123GET /api/v1/order-items/456GET /api/v1/payment-methods # GOOD: Consistent singular/plural conventionsGET /api/v1/users # CollectionGET /api/v1/users/123 # Specific user # BAD: Inconsistent casingGET /api/v1/UserAccounts/123 # Mixed caseGET /api/v1/ORDER_ITEMS/456 # Uppercase # BAD: File extensions in pathsGET /api/v1/users/123.json # Use Accept headers instead # BAD: Verbs in pathsGET /api/v1/getUser/123 # 'get' is the HTTP method, not pathPOST /api/v1/createOrder # 'create' is implied by POSTThe most fundamental resource naming pattern distinguishes between collections and individual resources. This pattern forms the backbone of nearly every REST API.
A collection resource represents a group of related resources. It uses a plural noun:
/users — the collection of all users/products — the collection of all products/orders — the collection of all ordersAn individual resource represents a specific item within a collection. It uses the collection name plus a unique identifier:
/users/123 — user with ID 123/products/abc — product with ID 'abc'/orders/ord-456 — order with ID 'ord-456'The prevailing convention is to use plural nouns for collection names, even when referring to a single resource. This creates consistency:
/users → Collection of users
/users/123 → One user from the collection
Some APIs use singular nouns for individual resources (/user/123), but this creates inconsistency when the same concept appears in collections (/users) vs. individual access.
/users — all users/users/123 — specific user/users/123/orders — user's orders/users/123/orders/456 — specific order/users — all users/user/123 — specific user (singular)/user/123/orders — user's orders/user/123/order/456 — specific orderSometimes a resource doesn't belong to a collection—there's only one instance. These singleton resources are typically accessed relative to another resource or the API root:
/users/123/profile # The user's profile (one per user)
/users/123/settings # The user's settings (one per user)
/configuration # System configuration (global singleton)
/me # Current authenticated user (context-dependent singleton)
Singleton resources use singular nouns because they represent single instances, not collections.
The /me endpoint is a widely-used convention for accessing the currently authenticated user's data without knowing their ID. It maps to /users/{current-user-id} internally but provides a convenient shortcut. Examples: /me/profile, /me/orders, /me/notifications.
Real-world domains have relationships between entities. REST expresses these relationships through hierarchical URIs that nest child resources under parent resources.
When one resource conceptually "belongs to" another, the child resource appears nested under the parent:
/users/123/orders # Orders belonging to user 123
/orders/456/items # Items within order 456
/posts/789/comments # Comments on post 789
/organizations/acme/teams # Teams within organization 'acme'
Use hierarchical URIs when:
Avoid deep hierarchies when:
| URI Pattern | Represents | When to Use |
|---|---|---|
/users | Collection of users | Top-level resource |
/users/123 | Specific user | Individual access |
/users/123/orders | User's order collection | Orders scoped to user |
/users/123/orders/456 | Specific order of user | Order in user context |
/users/123/orders/456/items | Items in that order | Items scoped to order |
/orders/456 | Order by global ID | Direct order access |
/orders/456/items | Order items | Items without user context |
Deep hierarchies create long URIs and require clients to know the full path:
# Too deep — requires knowing org, team, project, AND repository
/organizations/acme/teams/backend/projects/api/repositories/main/branches/develop/commits/abc123
Better approach: Provide multiple access paths:
# Direct access (when the ID is globally unique)
/commits/abc123
# Or flatten the hierarchy
/repositories/acme-api-main/commits/abc123
Guidelines for hierarchy depth:
The URI hierarchy doesn't need to match your database schema exactly. URIs represent a logical view of resources that makes sense to API consumers, which may differ from internal implementation. An order item might be stored in a flat table but still be accessed via /orders/456/items/789.
A common question in resource naming is: when should something be a path segment vs. a query parameter? The distinction has semantic implications for caching, linking, and API clarity.
Path segments identify which resource you're accessing. They're part of the resource's identity:
/users/123 # Resource: user 123
/products/electronics/phones # Resource: phones in electronics category
/orders/2024-01-15 # Resource: orders on a specific date (if date is identifier)
Query parameters modify the request without changing which resource is accessed:
/users?status=active # Same /users resource, filtered
/users?sort=created_at&order=desc # Same /users, different ordering
/products?minPrice=100&maxPrice=500 # Filtered product collection
/users/123?include=orders,profile # User 123 with expanded relations
Ask: Does this value identify the resource, or does it modify how I view the resource?
| Value | Effect | Use |
|---|---|---|
| User ID | Identifies which user | Path: /users/123 |
| Status filter | Filters which users to list | Query: /users?status=active |
| Category | Identifies category resource | Path: /categories/electronics |
| Price range | Filters products | Query: /products?minPrice=100 |
| Pagination | Controls view of collection | Query: /users?page=2&limit=20 |
| Fields to include | Shapes response | Query: /users/123?fields=name,email |
12345678910111213141516171819202122232425
# RESOURCE IDENTIFICATION (Path Segments)GET /users/123 # Specific user by IDGET /teams/engineering/members # Members of specific teamGET /files/documents/report.pdf # Specific file by path # FILTERING AND OPTIONS (Query Parameters)GET /users?role=admin # Filter by roleGET /users?created_after=2024-01-01 # Filter by dateGET /orders?status=pending,shipped # Filter by multiple statuses # PAGINATIONGET /users?page=2&per_page=25 # Page-basedGET /users?offset=25&limit=25 # Offset-basedGET /users?cursor=abc123&limit=25 # Cursor-based # SORTINGGET /products?sort=price&order=ascGET /products?sort=-price,name # Price descending, then name ascending # FIELD SELECTIONGET /users/123?fields=id,name,emailGET /users?include=profile,orders # Including related resources # SEARCHGET /products?q=laptop&category=electronicsDifferent query parameters create different cached responses. /users?status=active and /users?status=inactive are cached separately. Design query parameters knowing that each unique combination is a distinct cacheable resource representation.
Consistent naming conventions make APIs predictable and reduce cognitive load for consumers. Establish conventions early and apply them uniformly.
For URI paths, there are several common conventions:
/user-profiles, /order-items ✓ Most common, URL-friendly/user_profiles, /order_items — Used by some APIs (Twitter)/userProfiles, /orderItems — Less common in URLs, more common in JSON/userprofiles, /orderitems — Hard to readRecommendation: Use kebab-case for URI paths and camelCase for JSON property names. This aligns with URL conventions and JavaScript conventions respectively.
| Convention | Example Path | Pros | Cons |
|---|---|---|---|
| kebab-case | /order-items | URL-friendly, readable, widely used | Different from code conventions |
| snake_case | /order_items | Matches Python/Ruby conventions | Underscore can be confused with underline styling |
| camelCase | /orderItems | Matches JavaScript conventions | Less URL-traditional, case-sensitivity issues |
| lowercase | /orderitems | Simple, no special characters | Hard to read multi-word names |
/invoices > /documents > /things/recurring-payments/users, /customers, and /clients for the same concept/users > /user-records > /user-table-rows12345678910111213141516171819202122232425262728293031
# E-commerce API/products/products/{id}/products/{id}/reviews/products/{id}/variants/categories/categories/{id}/products/carts/carts/{id}/items/orders/orders/{id}/items/orders/{id}/shipments # Social Media API/users/users/{id}/posts/users/{id}/followers/users/{id}/following/posts/posts/{id}/comments/posts/{id}/likes # Project Management API/projects/projects/{id}/tasks/projects/{id}/members/tasks/tasks/{id}/comments/tasks/{id}/attachments/teams/teams/{id}/projectsNot all relationships fit neatly into simple hierarchies. Real domains often have many-to-many relationships, cross-cutting concerns, and resources that span multiple parents.
Consider users and groups: a user can belong to multiple groups, and a group contains multiple users. How do you model this?
Option 1: Nested from both sides
/users/123/groups # Groups that user 123 belongs to
/groups/456/users # Users in group 456
Option 2: Separate membership resource
/memberships # All membership relationships
/memberships?user=123 # Memberships for user 123
/memberships?group=456 # Memberships in group 456
Option 3: Hyphenated compound resource
/user-groups/123-456 # Relationship between user 123 and group 456
Option 1 is usually sufficient for read operations. Option 2 is useful when memberships have their own properties (e.g., role, join date). Option 3 is rarely needed.
Some resources don't belong to a single parent. Consider a comment that can appear on posts, products, or tasks. Options:
Option 1: Polymorphic nesting
/posts/123/comments
/products/456/comments
/tasks/789/comments
/comments/abc # Direct access by ID
Option 2: Unified with query filtering
/comments?post=123
/comments?product=456
/comments?task=789
Recommendation: Provide nested access for intuitive discovery, plus direct access for when clients already have comment IDs.
Some operations don't map cleanly to resource CRUD. Consider "sending" an email draft, "canceling" an order, or "merging" two entities.
Option 1: State as a resource property
PATCH /orders/456
{"status": "canceled"}
Option 2: Sub-resource representing the action
POST /orders/456/cancellation
{"reason": "Customer request"}
POST /emails/123/sending
{}
Option 3: Controller-style action endpoint
POST /orders/456/cancel # Less RESTful, but sometimes pragmatic
Recommendation: Prefer modeling state changes as resource updates (Option 1) or as creation of sub-resources (Option 2). Option 3 is acceptable when the action is complex and doesn't fit resource semantics.
When you have an action that seems to need a verb, try 'nounifying' it. Instead of /users/123/activate (verb), create an /activations resource: POST /users/123/activation. Instead of /payments/456/refund, create: POST /payments/456/refund as a resource. This maintains resource-oriented design while expressing complex operations.
API versioning is covered in depth in Module 4, but it's relevant to resource naming because version indicators are often embedded in URIs.
1. Path prefix versioning:
/v1/users/123
/v2/users/123
Pros: Highly visible, easy to route, clearly separates versions Cons: Forces version awareness on clients, duplicates paths
2. Subdomain versioning:
https://v1.api.example.com/users/123
https://v2.api.example.com/users/123
Pros: Clean paths, infrastructure-level routing Cons: DNS/SSL complexity, CORS considerations
3. Query parameter versioning:
/users/123?version=1
/users/123?api-version=2024-01-01
Pros: Paths remain clean, easy to default Cons: Less visible, can complicate caching
/v1, /v2 — don't expose minor versions in URIs/users/123 maps to current stable version/v1 be deprecated?Most common industry practice: Path prefix versioning (/v1/...) due to its visibility and simplicity. Many major APIs (Stripe, Twilio, GitHub) use this approach.
12345678910111213141516
# Stripe - path prefixGET https://api.stripe.com/v1/customers # GitHub - Accept header (with path optional)GET https://api.github.com/users/octocatAccept: application/vnd.github.v3+json # Microsoft Azure - query parameter with dateGET https://management.azure.com/subscriptions/{id}/resourceGroups?api-version=2021-04-01 # Twilio - path prefix with date-based versionsGET https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages # Kubernetes - path prefix with resource versioningGET /apis/apps/v1/deploymentsGET /apis/networking.k8s.io/v1/ingressesLearning from common mistakes helps you avoid them. Here are the most frequent resource naming anti-patterns:
/getUsers, /createOrder, /deleteItem — Use HTTP methods instead/user/123 vs /products — Pick one convention/users/ vs /users — Be consistent (prefer no trailing slash)/users/123.json — Use Accept headers for content negotiation/users/create, /users/update/123 — Use HTTP methods/usr/123, /prod/456 — Spell out resource names/tbl_users/123, /user_records — Use domain language/orgs/1/depts/2/teams/3/users/4/tasks/5 — Flatten where possible/UserAccounts vs /order-items — Use one case convention/users/12345678 with sequential IDs — Use UUIDs or slugs for security| Anti-Pattern | Problem | Correction |
|---|---|---|
GET /getAllUsers | Verb in path | GET /users |
POST /users/create | CRUD verb redundant | POST /users |
GET /user/123/getOrders | Verb in path | GET /users/123/orders |
DELETE /removeUser?id=123 | RPC style | DELETE /users/123 |
GET /data/tbl_usr/123 | Internal naming | GET /users/123 |
GET /Users/123 | Uppercase | GET /users/123 |
GET /user/123/order/456 | Mixed singular/plural | GET /users/123/orders/456 |
Resource naming is a foundational skill that shapes the usability and longevity of your API. Let's consolidate the key principles:
/users, /orders, /products for consistency/users/123/orders for clear relationships/users/123 vs /users?status=active/order-items, not /orderItems or /order_itemsWhat's next:
With resources properly named and identified, we need to understand how to manipulate them. The next page explores HTTP Methods and Semantics—how GET, POST, PUT, PATCH, and DELETE work, what guarantees they provide, and how to use them correctly.
You now have a comprehensive understanding of resource naming patterns—from basic collections to complex relationships. These naming skills will make your APIs intuitive and predictable. Apply these patterns consistently, and your API will be self-documenting from its URIs alone.