# Resource Design

## 1. Nouns, Not Verbs

The single most important rule of REST resource design: **URIs name things, HTTP methods name actions.** The verb belongs in the method, not the path.

```
BAD:   POST /createOrder
BAD:   GET  /getOrderById?id=482
GOOD:  POST /orders            (create an order)
GOOD:  GET  /orders/482        (read order 482)
```

If you find yourself putting a verb in a URL, it's a sign the operation should be expressed as a different HTTP method on a resource, or (see section 4) modeled as its own sub-resource.

## 2. Collections vs. Single Items

Every resource type gets two URI shapes:

| Shape | Example | Represents |
|---|---|---|
| Collection | `/orders` | The set of all orders (GET = list, POST = create one) |
| Item | `/orders/482` | One specific order (GET = read, PUT/PATCH = update, DELETE = remove) |

Collections are (almost) always **plural nouns** — `/orders`, not `/order`. This stays consistent whether the collection has 0, 1, or 10,000 members.

## 3. Nesting and Relationships

When a resource only makes sense in the context of a parent, nest it:

```
GET  /orders/482/items          -> items belonging to order 482
GET  /orders/482/items/9        -> item 9 of order 482
POST /orders/482/items          -> add an item to order 482
```

**Depth guidance:** keep nesting to one or two levels. `/customers/7/orders/482/items/9/reviews/3` is technically consistent but painful to work with. A common pattern once you're deep enough is to "promote" the inner resource to a top-level collection reachable by its own id, and only use nesting for creation/listing in context:

```
GET /items/9              -> works regardless of which order it came from
GET /orders/482/items      -> list items scoped to order 482 (nesting used only for "in-context" listing)
```

### Embedding vs. linking

A collection response can either **embed** related data inline or **link** to it and require a follow-up request:

```json
// embedded — one request, larger payload, can go stale relative to the source of truth
{ "id": 482, "customer": { "id": 7, "name": "Ana" } }

// linked — smaller payload, always fresh, costs an extra request
{ "id": 482, "customer": { "href": "/customers/7" } }
```

Neither is "more correct" — it's a trade-off between request count and payload size/freshness, often solved with an opt-in `?expand=customer` query parameter that lets the client choose per-request.

## 4. Actions That Don't Fit CRUD

Not every operation maps cleanly to create/read/update/delete. "Cancel this order," "reset this user's password," "publish this article" are actions, not resource replacements. Two accepted patterns:

**Controller sub-resource (most common, recommended default):**
```
POST /orders/482/cancel
POST /users/7/password-reset
POST /articles/55/publish
```
This models the action as a POST to a URI that reads like "the cancellation of order 482" — technically still a noun (the *act* of cancelling), and it composes cleanly with HATEOAS links (section 5 of REST Fundamentals).

**State-changing PATCH (works when the action is really just a field transition):**
```
PATCH /orders/482
{ "status": "cancelled" }
```
This works well when the action *is* simply "change this one field," but breaks down once the action needs side effects beyond setting a field (e.g. cancelling also needs to trigger a refund) — at that point the controller sub-resource pattern communicates intent more clearly.

## 5. Casing and Pluralization Conventions

| Convention | Example | Notes |
|---|---|---|
| lowercase, hyphenated segments | `/order-items` | Most common for multi-word resource names; avoids case-sensitivity bugs across systems |
| plural collection names | `/orders`, `/customers` | Consistent even for a collection that only ever has one row (`/settings` is a common, deliberate exception) |
| no trailing slash | `/orders` not `/orders/` | Pick one and be consistent — most frameworks treat these as distinct routes |
| identifiers own their own segment | `/orders/482` not `/orders?id=482` | The path identifies *which resource*; query strings are reserved for filtering/pagination/sorting (its own topic) |

![Resource hierarchy tree for a food-delivery API showing collection and item nodes](diagrams/01-resource-hierarchy-tree.svg)

## 6. Under the Hood: How a Router Actually Matches a Path to a Resource

Resource design isn't just a naming convention — it's compiled into real matching logic by the framework serving the API. Understanding that mechanism explains *why* certain design choices (ambiguous nesting, overlapping patterns) cause real bugs.

### Path templates, not strings

A route like `/orders/:id/items/:itemId` (Express-style) or `/orders/{id}/items/{itemId}` (OpenAPI/RFC 6570 URI Template-style) is compiled — usually at server startup — into a matcher, most commonly a **regular expression** or a **trie** (prefix tree) keyed by path segment:

```
Route table (compiled once, matched per request):
  /orders                  -> regex ^\/orders\/?$
  /orders/:id               -> regex ^\/orders\/([^/]+)\/?$
  /orders/:id/items          -> regex ^\/orders\/([^/]+)\/items\/?$
  /orders/:id/items/:itemId   -> regex ^\/orders\/([^/]+)\/items\/([^/]+)\/?$
```

At request time, the incoming path `/orders/482/items/9` is tested against each compiled pattern (or walked through the trie one segment at a time) until one matches; the captured groups (`482`, `9`) are extracted into `req.params`.

### Specificity ordering resolves ambiguity

Two patterns can both match the same literal path — e.g. a static route `/orders/summary` and a dynamic route `/orders/:id` both match `/orders/summary`. Frameworks resolve this by **registration order** (Express: first-registered-wins) or by an explicit **specificity score** (some routers rank literal segments above dynamic ones automatically, regardless of registration order). This is precisely why resource design guides tell you to register static sub-paths (`/orders/summary`, `/orders/export`) *before* the generic `/orders/:id` pattern — if `:id` is checked first, a request for `/orders/summary` gets incorrectly captured as `id = "summary"`.

### Trie-based routers scale better than a sequential regex scan

High-throughput frameworks (e.g. Fastify's `find-my-way`) build a **radix trie** over route segments instead of testing every route in registration order. Matching becomes a walk of depth equal to the number of path segments (O(segments)) rather than a linear scan over every registered route (O(routes)) — this is why deeply nested but well-structured resource hierarchies (section 3's guidance to cap nesting at 1-2 levels) matter for router performance at scale, not just for readability.

## 7. Common Pitfalls

1. **Verbs in the URL** — `/getOrder`, `/orders/482/delete` — the method already carries the verb; repeating it in the path is redundant and inconsistent once mixed with proper resource paths.
2. **Inconsistent pluralization** — mixing `/order/482` and `/orders/999` across the same API confuses consumers and complicates client SDK generation.
3. **Over-nesting** — `/customers/7/orders/482/items/9/reviews/3/comments/1` — walk it back; promote deeply nested resources to top-level, scoped by query parameter or a single level of nesting for creation.
4. **Registering a dynamic route before a static one that overlaps it** — causes the static route to be silently shadowed (section 6).
5. **Using query parameters for identity** (`/orders?id=482`) instead of the path (`/orders/482`) — breaks caching (the path is the cache key for many HTTP caches) and REST's "resource identified by URI" constraint.
6. **Modeling every action as a `PATCH` field flip** when the action has side effects beyond that one field — use a controller sub-resource instead (section 4).

## 8. Quick Reference

```
Collection: plural noun            /orders
Item:       collection + id        /orders/482
Nested:     parent/id/child        /orders/482/items
Action:     controller sub-resource /orders/482/cancel   (POST)
Casing:     lowercase, hyphenated multi-word segments
Identity:   in the path, never in the query string
Nesting depth: cap at 1-2 levels; promote deep resources to top-level
```
