How to properly name REST endpoints?
Endpoint naming is about creating a clear and predictable API.
Junior Level
Endpoint naming is about creating a clear and predictable API.
Basic rules:
- Use nouns, not verbs:
- ✅
GET /users/1 - ❌
GET /getUser/1
- ✅
- Use plural forms:
- ✅
/users,/orders,/products - ❌
/user,/order,/product
- ✅
- Use kebab-case:
- ✅
/audit-logs,/user-profiles - ❌
/audit_logs,/userProfiles
- ✅
- Use lowercase:
- ✅
/users/active - ❌
/Users/Active
- ✅
Examples:
GET /users — get all users
GET /users/1 — get user with ID 1
POST /users — create a user
GET /users/1/orders — get user's orders
GET /users?role=admin — filter users by role
Filtering and search:
- ✅
/users?role=admin&status=active— query parameters - ❌
/users/role/admin— don’t make the URI hierarchical where there’s no hierarchy
Middle Level
Senior naming rules:
- Plural form:
/usersrepresents a collection,/users/1— an element of the collection - Kebab-case: Hyphen is the standard for URLs (RFC 3986), better perceived by search engines
- Lowercase: URLs are case-sensitive on some servers (Linux/Nginx)
How to handle actions?
If an action doesn’t fit into CRUD (e.g., “activate a user”):
- Resource-oriented (best):
PATCH /users/1with body{"status": "ACTIVE"} - Sub-resource:
POST /users/1/activation— we create an “activation entity” - Controller-style (acceptable):
POST /users/1/activate— for complex commands
Search and filtering
- ✅
/users?role=admin— Query Params for filtering - ❌
/users/role/admin— URI shouldn’t be hierarchical without a hierarchy
“Search-only” endpoints
Systems like Elasticsearch use a _ prefix: POST /users/_search. This helps avoid collisions with resource IDs.
Edge Cases
- Trailing Slash:
/users/and/users— two different resources. ConfigureStrictTrailingSlashPatternMatch. By default, Spring Boot 3+ does a 308 redirect from/users/to/users. If this is undesirable, configureuseTrailingSlashMatchin the configuration. - ID in path vs in header: As a rule, don’t pass the current user’s ID in the path if it’s already in the JWT. Use
/users/me/profile. Exception — administrative endpoints where an admin works with someone else’s ID. This prevents IDOR (Insecure Direct Object Reference) attacks.- IDOR (Insecure Direct Object Reference) — an attack where an attacker substitutes an ID in the URL (
/users/123→/users/124) to gain access to someone else’s data. Defense: compare the ID from the URL with the ID in the JWT.
- IDOR (Insecure Direct Object Reference) — an attack where an attacker substitutes an ID in the URL (
Diagnostics
- 404 vs 405: If the resource exists but the method doesn’t fit — return 405 Method Not Allowed with an
Allowheader, not 404 - URI Length: Browsers and proxies limit URL length (2048 or 8192 characters). With too many filters — switch to
POST /_search
Senior Level
Complex naming scenarios
Matrix Parameters
A rarely used feature URL;param=value (e.g., /users;status=active/orders). Spring MVC supports it via @MatrixVariable. Useful for passing parameters into the middle of a path.
IDOR Prevention
As a rule, don’t pass the current user’s ID in the path if it’s already in the JWT. Use /users/me/profile — this prevents Insecure Direct Object Reference attacks.
- IDOR (Insecure Direct Object Reference) — an attack where an attacker substitutes an ID in the URL (
/users/123→/users/124) to gain access to someone else’s data. Defense: compare the ID from the URL with the ID in the JWT.
Performance and Infrastructure
- URI Length Limit: Browsers and proxies limit URL length. With too many filters — switch from
GETtoPOST /_search - Matrix Parameters: Spring MVC supports
@MatrixVariablefor passing parameters into the middle of a path
API Discovery
A well-designed API allows calling OPTIONS /users and getting a list of available methods and parameters. This enables clients to dynamically discover API capabilities.
Edge Cases
- If the resource
/users/1exists but you calledPOSTinstead ofGET— the server should return 405 Method Not Allowed with anAllow: GET, PUT, DELETEheader. Returning404is a design error - When integrating with external systems, document all possible query parameters
Interview Cheat Sheet
Must know:
- URI — noun in plural form:
/users,/orders,/products - Kebab-case for URLs:
/audit-logs,/user-profiles(RFC 3986) - Filtering via query parameters:
/users?role=admin&status=active, not via URI hierarchy - Non-CRUD actions:
PATCH /users/1with body orPOST /users/1/cancellation(resource-as-action) - Trailing Slash:
/users/and/users— different resources; Spring Boot 3 does a 308 redirect - IDOR Prevention: use
/users/me/profileinstead of/users/{id}/profileif ID is already in JWT - 405 Method Not Allowed with Allow header — correct response when method doesn’t fit (not 404)
Common follow-up questions:
- How to handle actions like “activate”? — PATCH with
{"status": "ACTIVE"}or POST /users/1/activation - What is IDOR and how to protect against it? — ID substitution in URL; defense: compare ID from URL with ID in JWT
- When to use POST /_search instead of GET? — When there are too many filters (URL > 2048 characters)
- What are Matrix Parameters? — Rarely used
URL;param=value, supported via @MatrixVariable in Spring
Red flags (DO NOT say):
- “Verbs in URLs are fine” — URI = noun, action = HTTP method
- “404 is the correct response for a wrong method” — need 405 Method Not Allowed with Allow header
- “Singular is better than plural” — plural form is the industry standard
- “
audit_logsoruserProfilesin URLs” — kebab-case (audit-logs) is the URL standard
Related topics:
- [[What is RESTful API design]]
- [[Should you use verbs in URL]]
- [[What are the main HTTP methods used in REST]]
- [[What is the difference between 401 and 403]]
- [[How to organize REST API versioning]]