Who this is for
API Engineers, Backend Developers, and anyone designing HTTP/JSON APIs who wants predictable, intuitive resource models and names.
Prerequisites
- Basic HTTP knowledge: methods (GET, POST, PATCH, DELETE) and status codes.
- JSON familiarity for request/response bodies.
- High-level understanding of your product domain (users, orders, posts, etc.).
Why this matters
Good resource modeling and naming make APIs easy to discover, scale, and maintain. Real tasks you will face:
- Designing collection and item endpoints for a new feature (e.g.,
/orders,/orders/{order_id}). - Representing relationships (e.g., a user's posts, an order's line items).
- Choosing names for fields, filters, and actions that stay consistent across teams.
- Adding new features without breaking existing clients.
Concept explained simply
Think in nouns first. An API resource represents a thing in your domain (user, order, invoice). Collections are plural (/users), individual resources are singular via an ID (/users/{user_id}). Relationships are either embedded fields or subresources (/users/{user_id}/posts).
Mental model
Imagine a graph of domain objects:
- Nodes = resources (Users, Orders, Products)
- Edges = relationships (User has many Orders, Order has many Items)
- Paths = how clients traverse that graph using HTTP URLs
Operations act on resources via HTTP methods rather than verbs in paths. Name things so clients can guess the URL without docs.
Core principles and naming rules
- Collections are plural nouns:
/users,/orders. - Items are addressed by stable IDs:
/users/{user_id}. - Use kebab-case for URL segments:
/line-items. Use a single case for JSON fields (commonlycamelCaseorsnake_case) and stick to it. - Relationships use subresources when they are first-class collections:
/orders/{id}/line-items. - Do not put actions as verbs in paths. Prefer HTTP methods and subresources:
POST /orders/{id}/cancelonly when no natural resource fits; otherwise change state via PATCH. - Filtering, sorting, pagination live in query params:
/orders?status=paid&sort=-created_at&page=2. - Consistent IDs and formats across resources (e.g., UUIDs). Do not leak database internals.
- Avoid nesting deeper than two levels. Prefer linking with IDs if chains become long.
- Version at the top level if needed:
/v1/orders(keep versions small and long-lived).
When to prefer PATCH vs action endpoints?
Use PATCH when you are updating resource state that can be represented as fields (e.g., {"status":"canceled"}). Use an action endpoint (e.g., POST /orders/{id}/cancel) for domain actions that have side effects not well modeled as a partial update or that need their own input/response schema (e.g., multi-step workflows).
Worked examples
Example 1: Blog platform
Domain: Users write Posts; Posts have Comments; Users can like Posts.
- Collections:
/users,/posts,/comments - Items:
/users/{user_id},/posts/{post_id} - Relationships:
/users/{user_id}/posts,/posts/{post_id}/comments - Likes as a subresource collection:
/posts/{post_id}/likes(each like has an ID or is addressed by user) - Field naming (JSON): choose and keep consistent, e.g., camelCase:
{"title":"...","createdAt":"..."}
Bad vs good
- Bad:
/getUserPosts,/createComment(verbs in paths) - Good:
GET /users/{id}/posts,POST /posts/{id}/comments
Example 2: E-commerce orders
- Collections:
/products,/orders,/orders/{order_id}/line-items - Cancel an order: Prefer
PATCH /orders/{id}with{"status":"canceled"}. If business rules require extra input/reason, usePOST /orders/{id}/cancel. - Filtering:
/orders?status=paid&customer_id=... - Sorting:
/orders?sort=-created_at(minus for descending)
Inventory reservation?
If reservation is its own resource, model it: /reservations and /orders/{id}/reservations. Avoid hidden side effects on unrelated endpoints.
Example 3: Teams and invites
- Teams have Members and Invites
- Collections:
/teams,/teams/{team_id}/members,/teams/{team_id}/invites - Accept invite:
POST /invites/{invite_id}/accept(action endpoint OK if it does more than a simple field flip and requires a token) - Alternative:
PATCH /invites/{invite_id}with{"status":"accepted"}if that suffices.
How to model resources (step-by-step)
Exercises
These mirror the interactive exercises below. Try them here first, then compare with the solutions.
Exercise 1 — Model a movie API
Design resources and names for a movie database: Movies, People (actors/directors), and Credits (who acted in which movie). Include endpoints for listing, getting, creating, and relating entities. Add a way to rate a movie.
- Name collections, items, and subresources.
- Decide where to place ratings and how to update them.
- Add filtering for year and person name.
Exercise 2 — Clean up messy endpoints
Refactor these into a consistent style:
/getAllUsers/userProfile?id=123/Orders/AddItem/orders/{id}/removeLineItem/orders/{id}/doCancel
Provide improved paths, methods, and any necessary body shapes.
- I used plural nouns for collections and stable IDs for items.
- I kept URL case (kebab-case) and JSON case consistent.
- I avoided verbs in paths unless modeling a complex action.
- I kept nesting to two levels or less.
- I placed filters, sorting, and pagination in query params.
Common mistakes and self-check
- Verb-first URLs like
/createUser. Self-check: Can I map this to POST on a collection? - Inconsistent pluralization (
/uservs/users). Self-check: Collections plural, items via ID. - Deep nesting (
/orgs/{o}/teams/{t}/members/{m}/devices/{d}). Self-check: Limit to 1–2 levels; use linking by IDs. - Mixed casing (
snake_casein one response,camelCasein another). Self-check: Choose once; lint schemas. - Hidden actions via GET (state changes). Self-check: GET must be safe and idempotent.
Consistency tip
Create a short naming guide (1 page) that lists: URL case, JSON case, pluralization rules, ID format, sorting/filtering patterns. Apply it in each PR.
Practical projects
- Design a "Task Manager" API with tasks, projects, and comments. Include filters (
?status=,?assignee_id=) and sorting. - Refactor an existing toy API to follow plural collections and subresource patterns. Write a diff of before/after.
- Create a small API spec (YAML/JSON) for a bookstore: books, authors, reviews, and ratings, with at most two levels of nesting.
Learning path
- Start here: resource modeling and naming (this page).
- Then: pagination, filtering, and sorting patterns.
- Next: error models and status codes.
- Later: versioning strategies and backward compatibility.
Next steps
- Finalize your team’s naming guide.
- Apply the checklist to one real endpoint you own.
- Take the Quick Test below to validate your understanding.
Mini challenge
Design endpoints for a "playlist" feature: Users have Playlists; Playlists have Tracks. Users can follow Playlists. Add a way to reorder tracks in a playlist. Keep nesting shallow and names consistent.
See one possible sketch
/playlists
/playlists/{playlist_id}
/playlists/{playlist_id}/tracks
/users/{user_id}/playlists
/playlists/{playlist_id}/followers
PATCH /playlists/{playlist_id}/tracks (body with ordered track_ids)Quick Test info
The Quick Test is available to everyone. Only logged-in users get saved progress.