Question 12 · Section 6

How to properly name REST endpoints?

Endpoint naming is about creating a clear and predictable API.

Language versions: English Russian Ukrainian

Junior Level

Endpoint naming is about creating a clear and predictable API.

Basic rules:

  1. Use nouns, not verbs:
    • GET /users/1
    • GET /getUser/1
  2. Use plural forms:
    • /users, /orders, /products
    • /user, /order, /product
  3. Use kebab-case:
    • /audit-logs, /user-profiles
    • /audit_logs, /userProfiles
  4. 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
  • /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: /users represents 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”):

  1. Resource-oriented (best): PATCH /users/1 with body {"status": "ACTIVE"}
  2. Sub-resource: POST /users/1/activation — we create an “activation entity”
  3. 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. Configure StrictTrailingSlashPatternMatch. By default, Spring Boot 3+ does a 308 redirect from /users/ to /users. If this is undesirable, configure useTrailingSlashMatch in 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.

Diagnostics

  • 404 vs 405: If the resource exists but the method doesn’t fit — return 405 Method Not Allowed with an Allow header, 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 GET to POST /_search
  • Matrix Parameters: Spring MVC supports @MatrixVariable for 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/1 exists but you called POST instead of GET — the server should return 405 Method Not Allowed with an Allow: GET, PUT, DELETE header. Returning 404 is 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/1 with body or POST /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/profile instead of /users/{id}/profile if 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_logs or userProfiles in 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]]