RESTful API Development

Chapter 4
Resource and URI Design

Bachelors Program in Full Stack Development
RESTful API Development   •   Bachelor

Resource and URI Design

Introduction

RESTful API development begins with a modeling decision: what does the system expose as a resource? A resource is a meaningful business or technical concept that can be identified, retrieved, created, modified, or removed through a Web interface. Good resource design creates an API that is understandable, predictable, evolvable, and aligned with the domain. Poor design produces inconsistent paths, excessive coupling to database tables, unclear relationships, and difficult-to-use client code.

This chapter develops a systematic approach to modeling domains as resources and collections. It explains URI structure, naming conventions, relationships, nested resources, query parameters, and CRUD-oriented interactions. The discussion assumes HTTP-based APIs, but the principles also apply to event-driven systems, mobile back ends, and service-to-service integration.

Learning Objectives

By the end of this chapter, you should be able to:

  • distinguish resources, collections, individual resources, and representations;
  • model a business domain as coherent REST resources;
  • design consistent collection and item URIs;
  • select between nested, linked, and flattened relationship designs;
  • use query parameters for filtering, sorting, pagination, and field selection;
  • map CRUD operations to HTTP methods without confusing domain actions with CRUD;
  • identify security, governance, versioning, and maintainability consequences of URI choices; and
  • evaluate a resource design using a practical case study.

1. Resources, Collections, and Representations

A resource is an abstraction of something the API can identify. It may correspond to a physical entity, such as a book, or to a conceptual entity, such as a payment, search result, or shipment. A resource is not necessarily a database row. One resource may combine data from several tables, while a database table may be exposed through several representations.

A collection is a resource that represents a set of related items. For example, /books identifies the collection of books, whereas /books/9780132350884 identifies one book. The response representation may be JSON, but the resource itself is the conceptual object identified by the URI.

Important distinctions include:

ConceptMeaningExample
ResourceA domain object or concept exposed by the APIA book
CollectionA set of resourcesAll books
Member/itemOne identifiable member of a collectionBook ISBN 9780132350884
RepresentationData transferred about a resourceA JSON book document
URIIdentifier used to address a resource/books/9780132350884
RelationshipA meaningful association between resourcesAn order contains items

A representation can change without changing the identity of the resource. For example, the same book may be represented in a compact list view or a detailed view. Content negotiation can allow a client to request different media types, although most modern APIs commonly use application/json.

Resource modeling is not table exposure

Exposing every database table as a public endpoint often leaks implementation details. A database may contain book, book_author, inventory_snapshot, and pricing_rule tables, but clients may need only a stable books resource. Public resource models should reflect business meaning, security boundaries, and client tasks rather than database normalization alone.

A useful modeling question is: “What thing does a consumer need to identify or operate on?” If the answer is “a submitted order,” the API should expose an order resource even if the internal system represents it through several records.

2. From Business Domain to Resource Model

Resource modeling can be approached systematically.

Step 1: Identify domain nouns

Examine requirements, use cases, and business vocabulary. In an online bookstore, likely nouns include customers, books, authors, carts, orders, order items, payments, and shipments.

Step 2: Separate entities from attributes

An entity has an independent identity or lifecycle. A book has an identity and can be updated independently. A book’s title is normally an attribute, not a separate resource. However, an attribute may become a resource if it has its own identity, permissions, lifecycle, or relationships.

Step 3: Identify collections and cardinality

Ask whether a relationship is one-to-one, one-to-many, or many-to-many. An author may write many books, and a book may have several authors. An order has order items, while each order item refers to one book at a particular quantity and price.

Step 4: Identify lifecycle boundaries

If an object is created, authorized, paid for, cancelled, or audited independently, it may deserve a resource. A payment is not merely an attribute of an order when it has its own provider reference, status transitions, and security controls.

Step 5: Define stable identifiers

Identifiers should be stable, unique within the relevant scope, and safe to expose. They may be numeric IDs, UUIDs, ISBNs, or opaque strings. Do not expose mutable fields such as email addresses as primary identifiers unless the domain explicitly guarantees their immutability.

Step 6: Validate against use cases

A model is successful when common operations are natural. Test questions such as:

  • How does a client list books by genre?
  • How does it retrieve one order?
  • How does it add an item to a cart?
  • How does it inspect the books in an order?
  • Which objects may a customer modify?

A domain model that cannot answer these questions cleanly needs refinement.

3. URI Design Principles

A URI should identify a resource, not describe an implementation command. The path communicates resource identity and hierarchy; the HTTP method communicates the requested operation.

Collections and members

A common convention is plural nouns for collections and identifiers for members:

GET    /books
GET    /books/{bookId}
POST   /books
PATCH  /books/{bookId}
DELETE /books/{bookId}

Plural naming makes the collection nature explicit. Consistency is more important than whether an organization chooses singular or plural forms, but mixing styles is confusing.

Prefer:

/customers/42/orders

over inconsistent alternatives such as:

/getCustomerOrders?customer=42
/customerOrders/42

The latter forms treat an operation or an implementation-specific aggregate as the primary identity.

URI syntax and naming conventions

Recommended conventions include:

  • use lowercase path segments;
  • use hyphens for multiword segments, such as /order-items;
  • avoid spaces, underscores, and unexplained abbreviations;
  • use nouns rather than verbs in ordinary resource paths;
  • keep identifiers in a consistent format;
  • avoid file extensions such as .json in the path; and
  • document whether trailing slashes are accepted or redirected.

URI conventions should be applied consistently across the whole API. /order-items is clearer than a mixture of /order_items, /OrderItems, and /items.

URI identity and query strings

The path generally identifies the target resource. Query parameters refine a collection request or control representation. For example:

/books?genre=history&sort=-publishedAt&page=2&pageSize=20

This identifies a filtered and ordered view of the books collection. Query parameters should not be used to conceal fundamentally different resources or unsafe commands.

4. Relationships and Nested Resources

Business resources rarely exist in isolation. Relationships communicate ownership, membership, association, and navigation.

Consider the bookstore model below. An order contains order items. Each order item records a quantity and the price charged at the time of purchase, and references a book. The order item is not merely a duplicated book: it is a transactional snapshot linking an order to a book.

Bookstore resource relationships Order /orders/{orderId} Order item quantity, price Book /books/{bookId} contains references A transaction item links an order to a book and preserves purchase-specific data.
Bookstore resource relationships showing an order containing order items that reference books
Figure: Bookstore resource relationships showing an order containing order items that reference books

Figure: An order contains order items; each order item references a book.

When nesting is appropriate

Nested paths express a relationship clearly:

GET    /orders/{orderId}/items
GET    /orders/{orderId}/items/{itemId}
POST   /orders/{orderId}/items

Nesting is appropriate when the child has no useful meaning outside its parent, when the relationship is strong, and when the parent context is required for authorization. An order item may therefore be addressed as /orders/501/items/3.

Limits of nesting

Deep nesting creates long, tightly coupled paths:

/customers/7/orders/501/items/3/book

The same object may then have multiple canonical URIs, increasing documentation and caching complexity. A practical guideline is to keep nesting shallow, commonly one level, and provide a direct URI when the child has an independent identity:

/orders/501/items/3
/books/9780132350884

The API may return links or identifiers connecting related resources. Hypermedia links are useful, but a design should remain understandable even when clients use documented URI templates instead of following links.

Many-to-many relationships

For authors and books, possible designs include:

GET /authors/{authorId}/books
GET /books/{bookId}/authors

If the association has attributes such as contribution role or display order, model it explicitly as an authorship resource or relationship object rather than hiding those attributes in a simple array.

5. Query Parameters for Collection Operations

Query parameters are especially useful for controlling collection results.

Filtering

GET /books?genre=history&language=en

Filters should have documented semantics. Decide whether repeated parameters mean OR or AND, and define case sensitivity, null handling, and exact versus partial matching.

Sorting

A common convention uses a field name for ascending order and a leading minus sign for descending order:

GET /books?sort=-publishedAt,title

The API must whitelist sortable fields. Accepting arbitrary database expressions can create security and performance problems.

Pagination

Offset pagination is easy to understand:

GET /books?page=3&pageSize=25

If pages are one-based, page 3 with a page size of 25 represents records 51–75, assuming a stable ordering and a complete result set. In practice, records can be inserted or removed between requests, so offset pagination may produce duplicates or omissions.

Cursor pagination is often more stable for changing or large datasets:

GET /books?limit=25&after=eyJpZCI6MTIzfQ

The cursor is an opaque continuation token. The server defines its contents and should not require clients to decode it. A stable ordering and an expiration or invalidation policy should be documented.

Search and field selection

Search may be represented by a query parameter such as q:

GET /books?q=distributed+systems

Field selection can reduce response size:

GET /books?fields=id,title,author

These facilities must be constrained by authorization and should not expose protected fields merely because a client names them.

Query parameters are not a substitute for actions

POST /orders/501/cancel can be a legitimate domain action because cancellation may involve rules and state transitions. By contrast, GET /orders?delete=true is unsafe and violates the expectation that GET is safe. A query parameter should refine retrieval, not smuggle mutation into a retrieval request.

6. CRUD-Oriented Interactions and HTTP Semantics

CRUD is a useful starting model, but business operations often require more than create, read, update, and delete.

IntentTypical methodExampleCommon successful status
List collectionGET/books200 OK
Retrieve memberGET/books/12200 OK
Create memberPOST/books201 Created
Replace memberPUT/books/12200 OK or 204 No Content
Partially modifyPATCH/books/12200 OK or 204 No Content
Delete memberDELETE/books/12204 No Content

POST to a collection commonly asks the server to create a new member. The response should normally include a Location header containing the new URI. PUT generally expresses replacement at a known URI and is idempotent: repeating the same request should have the same intended effect. PATCH applies a partial modification, but its exact semantics depend on the patch format and API contract.

Idempotence does not mean that every response is identical. It means that repeating an identical request has the same intended server-side effect, subject to ordinary failures and concurrent changes. DELETE is conventionally idempotent even if the first request returns 204 and a later request returns 404.

Domain actions

Some operations do not map naturally to CRUD. Examples include approving an invoice, capturing a payment, or cancelling an order. Such actions should be modeled explicitly, for example:

POST /orders/501/cancellation
POST /payments/88/capture

The API should document state-transition rules, authorization, replay protection, and possible conflicts. A domain action must not be disguised as an ordinary update if that would obscure important business semantics.

Concurrency and conditional requests

Resource design must account for competing updates. A server can expose an entity tag, or ETag, and require clients to send If-Match when updating. If the representation has changed since the client retrieved it, the server can return 412 Precondition Failed rather than silently overwriting another user’s change. This is an HTTP-level form of optimistic concurrency control.

7. Response Design and Error Semantics

A resource representation should contain stable identifiers, useful attributes, relationship information where appropriate, and links or references that clients can use. Avoid returning internal database keys, secrets, or fields whose meaning is undocumented.

A collection response often includes items and pagination metadata:

{
  "items": [
    {"id": "b17", "title": "Designing APIs"}
  ],
  "page": 1,
  "pageSize": 20,
  "hasNext": false
}

The exact envelope is a design choice. Consistency matters more than choosing an allegedly universal format.

Errors should be machine-readable and human-understandable. A useful error document can include a stable error code, a message safe for the client, and field-level details. Do not reveal stack traces, SQL statements, access tokens, or personal data.

Common statuses include 400 for malformed input, 401 when authentication is required or invalid, 403 when the authenticated principal lacks permission, 404 when the target is not available, 409 for a state or uniqueness conflict, 412 for a failed precondition, and 422 when syntactically valid input fails domain validation. Teams should define consistent policies, especially for authorization-sensitive 404 responses that intentionally avoid revealing resource existence.

8. Detailed Applied Case: Bookstore Order API

The following is an illustrative case, not a description of a specific real organization.

Background and problem

A bookstore initially exposes database-oriented endpoints such as /getBooks, /insertOrder, and /customerOrderLines. Mobile and partner clients find the interface inconsistent. A customer can add an item to an order, but the API does not clearly distinguish the current book price from the price charged in a completed order. Clients also receive unbounded book lists and can infer internal numeric identifiers.

Analysis

The business concepts are:

  • books: publishable catalog resources;
  • customers: parties who own orders;
  • orders: purchases with a lifecycle;
  • order items: order-specific quantity and charged price;
  • payments: independently auditable payment attempts; and
  • shipments: fulfillment records with their own status.

An order item must retain the charged unit price because the catalog price may change later. It references a book but is not interchangeable with the current book resource.

Proposed resource design

GET    /books?genre=fantasy&limit=20&after={cursor}
GET    /books/{bookId}
GET    /customers/{customerId}/orders
POST   /orders
GET    /orders/{orderId}
GET    /orders/{orderId}/items
POST   /orders/{orderId}/items
PATCH  /orders/{orderId}/items/{itemId}
DELETE /orders/{orderId}/items/{itemId}
POST   /orders/{orderId}/cancellation
GET    /payments/{paymentId}
POST   /payments/{paymentId}/capture
GET    /shipments/{shipmentId}

The public API uses opaque identifiers. A client creates an order with a customer reference, then adds an item using a book reference and quantity. The server checks inventory, validates the order state, and determines the applicable price. It returns the order-item URI and does not allow the client to set the historical charged price directly.

Outcome

The redesigned API separates catalog data from transaction data, supports bounded and stable listing, makes relationships explicit, and gives security policies clear resource boundaries. A customer can access only authorized orders; a partner may read books but not payments; and fulfillment staff may update shipment status without modifying catalog prices.

Learning points

  1. Resource boundaries should follow business meaning and authorization boundaries.
  2. Relationships can be represented through shallow nested paths and direct identifiers.
  3. Transactional snapshots must not be confused with current master data.
  4. Domain actions such as cancellation deserve explicit contracts.
  5. Pagination, validation, and concurrency are part of resource design, not afterthoughts.

9. Security, Governance, and Maintainability

URI design does not provide authorization. Every request must be authorized against the authenticated principal and the target resource. Guessable identifiers increase enumeration risk, but replacing them with UUIDs does not remove the need for access control. Authorization should be enforced server-side at collection and item levels.

Avoid placing secrets, passwords, access tokens, or sensitive personal data in URIs. URIs can appear in browser history, proxy logs, analytics systems, and referrer headers. Query parameters containing sensitive information are especially risky.

Logging and observability should record resource type, operation, status, latency, and correlation identifiers while minimizing personal data. Retention and access to logs require governance controls.

API contracts should define naming, identifier formats, pagination, filtering, sorting, error structures, deprecation, and compatibility policy. OpenAPI can document paths and schemas, but documentation does not replace semantic design or authorization testing.

Versioning should be used deliberately. A URI version such as /v1/books is easy to discover, while media-type or header versioning can keep paths stable. Whichever approach is selected, breaking changes should be rare, documented, and accompanied by a migration period. Adding an optional field is usually less disruptive than renaming or changing the meaning of an existing field.

10. Contemporary Developments and Critical Considerations

Modern APIs increasingly expose asynchronous operations. Creating a large export may return 202 Accepted and a job resource such as /exports/91, which clients poll or subscribe to. This is preferable to pretending that a long-running operation completed synchronously.

GraphQL and query-oriented APIs offer clients different control over representation shape, while RESTful APIs emphasize resource identification and standardized HTTP semantics. The choice depends on domain complexity, caching needs, client diversity, and operational constraints; neither approach automatically eliminates modeling problems.

AI-assisted API generation can propose schemas, examples, or URI patterns, but generated designs require human review. Models may expose sensitive fields, invent relationships, overlook authorization, or encode database structure as public contracts. Teams should use schema validation, threat modeling, contract tests, and domain-expert review before deployment.

11. Design Checklist

Before publishing a resource design, ask:

  • Are the resources meaningful to the domain rather than merely database tables?
  • Are collection and member URIs consistent?
  • Are identifiers stable and safe to expose?
  • Are relationships and ownership clear?
  • Is nesting shallow and justified?
  • Are filters, sorting, pagination, and field selection documented and bounded?
  • Do methods and status codes reflect HTTP semantics?
  • Are domain actions explicit?
  • Are concurrency and duplicate-request risks addressed?
  • Are authorization, privacy, logging, and sensitive fields considered?
  • Can the design evolve without breaking existing clients?

Chapter Summary

Resource and URI design translates business concepts into stable Web-addressable interfaces. Resources should represent meaningful entities or concepts, while collections represent sets of members and representations carry transferable data. Consistent plural nouns, stable identifiers, lowercase paths, and shallow nesting make APIs easier to understand and maintain.

Relationships should reflect business cardinality and lifecycle. An order contains order items, and each item references a book; an item also preserves transaction-specific facts such as quantity and charged price. Query parameters are suitable for filtering, sorting, pagination, search, and controlled field selection. HTTP methods provide standardized CRUD semantics, but domain actions such as cancellation and payment capture require explicit contracts.

Professional resource design also includes authorization, privacy, concurrency control, status codes, error structures, versioning, observability, and evolution strategy. The central test is not whether a URI looks attractive, but whether it expresses domain meaning, supports real client tasks, and remains safe and stable over time.

Key Terms

Resource: A domain or technical concept identified and exposed by an API.

Collection: A resource representing a set of related members.

Representation: A transferable view of a resource, such as a JSON document.

URI: A uniform identifier used to address a resource.

Member resource: One identifiable item within a collection.

Identifier: A stable value used to distinguish a resource.

Relationship: A meaningful association between resources.

Nested resource: A resource addressed in the context of a parent resource.

Query parameter: A name-value component used to refine a request, especially collection retrieval.

Filtering: Restricting a collection to members matching specified criteria.

Pagination: Dividing a large collection into manageable result sets.

Cursor: An opaque continuation token used for stable pagination.

Idempotence: The property that repeating an identical request has the same intended server-side effect.

Safe method: An HTTP method intended for retrieval without state change, such as GET.

Representation replacement: Updating a resource as a complete document, commonly with PUT.

Partial modification: Updating selected resource fields, commonly with PATCH.

Domain action: An explicit business operation not naturally expressed as ordinary CRUD.

ETag: An HTTP validator used to identify a representation version for conditional requests.

Optimistic concurrency control: Preventing lost updates by checking whether a resource changed before modification.

Opaque identifier: An identifier whose internal structure is not meaningful or exposed to clients.

Review Questions

Short Answer Questions

  1. Distinguish a resource from its representation.
  2. Why should public resources not necessarily mirror database tables?
  3. State two conventions for readable and consistent URI paths.
  4. When is a nested resource design appropriate?
  5. What is the purpose of a cursor in pagination?

Long Answer Questions

  1. Explain a systematic process for modeling a bookstore domain as resources.
  2. Compare PUT and PATCH, including their implications for idempotence and validation.
  3. Discuss the benefits and limitations of nested resources.
  4. Explain how query parameters support filtering, sorting, and pagination while preserving a clear resource model.
  5. Describe how authorization, privacy, concurrency, and versioning influence URI design.

Analytical / Application Questions

  1. Design URIs and HTTP methods for a university system containing students, courses, enrollments, and grades. Justify which relationships should be nested.
  2. An API exposes /getOrders?customerId=7&delete=true. Identify the design and semantic problems, then propose a safer resource-oriented alternative.
  3. A client retrieves an order, another client updates it, and the first client then submits an old representation. Design a conditional-update strategy using ETags and explain the relevant failure response.

Multiple Choice Questions with Answers

Question 1

Which URI most clearly identifies a collection of books?

A. /getBooks

B. /books

C. /bookListAction

D. /fetch/books.json

Correct Answer: B. /books

Question 2

In the bookstore model, what does an order item primarily represent?

A. The current catalog definition of a book

B. A database connection used by an order

C. A transaction-specific link between an order and a book

D. A search filter for the books collection

Correct Answer: C. A transaction-specific link between an order and a book

Question 3

Which request is most appropriate for listing the items in order 501?

A. GET /orders/501/items

B. POST /orders/501/items

C. GET /items/orders/501

D. DELETE /orders/501/items

Correct Answer: A. GET /orders/501/items

Question 4

What is a principal advantage of cursor pagination over offset pagination for changing large datasets?

A. It allows clients to modify records during retrieval

B. It can provide more stable continuation as records change

C. It removes the need for ordering results

D. It guarantees that every record is public

Correct Answer: B. It can provide more stable continuation as records change

Question 5

Which HTTP method commonly represents partial modification of a resource?

A. GET

B. POST

C. PATCH

D. OPTIONS

Correct Answer: C. PATCH

Question 6

Why should a charged price usually be stored on an order item?

A. Because book titles cannot be stored on orders

B. Because it preserves the transaction-specific price if the catalog price later changes

C. Because it makes authorization unnecessary

D. Because it converts the item into a collection

Correct Answer: B. Because it preserves the transaction-specific price if the catalog price later changes

Question 7

Which URI best represents an explicit cancellation action for order 501?

A. GET /orders/501?cancel=true

B. POST /orders/501/cancellation

C. DELETE /orders/501/cancelled

D. GET /cancel/order/501

Correct Answer: B. POST /orders/501/cancellation

Question 8

What is the principal purpose of an ETag in a conditional update?

A. To encode a user password in the URI

B. To identify a representation version and help prevent lost updates

C. To paginate a collection automatically

D. To replace authorization checks

Correct Answer: B. To identify a representation version and help prevent lost updates

Question 9

Which practice creates the greatest risk of leaking sensitive information?

A. Using lowercase path segments

B. Returning a Location header after creation

C. Putting access tokens in query parameters

D. Using a stable opaque resource identifier

Correct Answer: C. Putting access tokens in query parameters

Question 10

A client sends POST /books to create a book. Which response is most conventionally appropriate after successful creation?

A. 201 Created, usually with a Location header

B. 304 Not Modified, without a resource identifier

C. 401 Unauthorized, even when authentication succeeded

D. 204 No Content, which must always replace 201

Correct Answer: A. 201 Created, usually with a Location header

RESTful API Development
Chapter 4 of 15