Resource Identification (URI Design)

Introduction

One of the most important principles of REST is that everything meaningful is treated as a resource. A user, product, order, invoice, payment, book, customer, employee, cart, report, or account can all be resources when an API exposes them for clients. If resources are the foundation of REST, then identifying them clearly is one of the most important parts of API design. That identification is done through URIs.

A URI, or Uniform Resource Identifier, is the address used to identify a resource. When a client sends GET /users/101, the URI identifies the user resource with id 101. When a client sends GET /products?category=laptops, the URI identifies the product collection, and the query parameter filters the results. Good URI design makes APIs predictable. Poor URI design makes APIs confusing even when the backend logic works.

For API testers, URI design is not only a developer concern. Every API request begins with a URI, and a bad URI structure affects test readability, automation maintainability, defect reports, documentation, client integrations, and long-term API evolution. If one endpoint uses /users, another uses /UserList, and another uses /getAllCustomers, testers must handle unnecessary inconsistency. If path parameters and query parameters are used incorrectly, clients may misunderstand what identifies a resource and what filters a collection.

This tutorial explains resource identification, URI design, resources, URL vs URI vs URN, collection and individual resources, naming conventions, path parameters, query parameters, nested resources, good and bad examples, and API testing strategies. The goal is to understand URI design as a core REST skill, not as a cosmetic naming detail.

What Is Resource Identification?

Resource identification is the practice of assigning a unique URI to every resource or resource collection in a REST API. Each URI should help the client understand what is being addressed. A collection URI identifies a group of similar resources. An individual resource URI identifies one specific resource inside that collection.

A simple definition is this: resource identification is the practice of uniquely identifying every resource in a REST API using a URI. The URI tells the server which resource the client wants to retrieve, create under, update, partially update, delete, or interact with according to the HTTP method.

For example, /users identifies the collection of users. /users/101 identifies a specific user. /users/101/orders identifies orders belonging to user 101. These URIs are readable because they describe resources and relationships instead of low-level implementation details.

What Is a Resource?

A resource is any object, data, or business concept exposed by an API. In a school application, students, courses, teachers, exams, and results may be resources. In banking, accounts, transactions, beneficiaries, statements, cards, and payments may be resources. In e-commerce, products, carts, orders, customers, shipments, and reviews may be resources.

RESTful design asks teams to think in terms of resources before actions. Instead of starting with "get users" or "delete product," begin with "users" and "products." The action is then expressed through the HTTP method. This resource-first thinking produces cleaner and more consistent API designs.

Resources do not always map one-to-one to database tables. A resource is part of the API contract, not necessarily the internal storage design. A /dashboard-summary resource may combine data from multiple tables and services. An /orders/101/status resource may represent a subset of order information. The key is that the resource has a clear meaning to the client.

What Is a URI?

URI stands for Uniform Resource Identifier. It is used to identify a resource. In REST APIs, the URI is usually the path and optional query string used by the client to address a resource. For example:

/users/101

This URI identifies one user. A full API URL may include scheme, host, path, and query parameters:

https://api.example.com/users/101

In everyday API testing, people often say URL when they mean the full address and URI when they mean the resource identifier path. The distinction is useful, but the practical goal is clear: the address should identify the intended resource consistently.

URI vs URL vs URN

URI, URL, and URN are related terms. A URI identifies a resource. A URL identifies a resource and provides its location. A URN identifies a resource by name in a persistent naming scheme. In normal REST API work, URL and URI are the terms testers see most often.

For example:

https://api.example.com/users/101

This is both a URI and a URL because it identifies a resource and tells where it can be accessed. A URN is less common in day-to-day REST API testing and is usually used for name-based identification rather than HTTP location.

Interviewers may ask this distinction to check conceptual understanding. A practical answer can be brief: a URI identifies a resource, a URL is a type of URI that includes location, and a URN is a type of URI that identifies by name.

REST URI Structure

A typical REST URI uses a resource name and, when needed, an identifier:

/resource/{id}

Examples include:

/users
/users/101
/products/500
/orders/1001

The first form identifies a collection. The second form identifies one item in the collection. The same URI can be used with different HTTP methods. GET /users/101 retrieves the user. PUT /users/101 replaces the user. PATCH /users/101 partially updates the user. DELETE /users/101 deletes or deactivates the user, depending on the business rule.

This structure keeps APIs predictable. The URI identifies the resource, and the HTTP method identifies the operation.

Collection Resources

A collection resource represents a group of similar resources. /users represents all users available to the caller according to authorization and filtering rules. /products represents products. /orders represents orders. A GET request on a collection often returns a list.

GET /users

A response may be:

[
  {
    "id": 1,
    "name": "John"
  },
  {
    "id": 2,
    "name": "Alice"
  }
]

POST is commonly used on a collection to create a new item within that collection:

POST /users

In API testing, collection endpoints require checks for filtering, sorting, pagination, authorization, empty results, response schema, and performance. A collection URI should not imply that every record in the database will be returned without limits. Good APIs provide controlled collection access.

Individual Resources

An individual resource URI represents one specific resource. For example:

/users/101

This identifies user 101. A GET request should retrieve that user if it exists and the caller is authorized. A response may be:

{
  "id": 101,
  "name": "John"
}

Individual resource endpoints should be tested for valid ids, invalid ids, non-existing ids, unauthorized access, deleted resources, and malformed identifiers. If user 999999 does not exist, the API should return a meaningful 404 or documented error. If a caller tries to access another user's private resource, the API should deny access appropriately.

The path parameter is part of resource identity. That is why tests should treat it differently from optional filters. An invalid resource id is not the same as an empty search result.

Use Nouns, Not Verbs

REST URIs should represent resources, not actions. Use nouns in the URI and use HTTP methods to express actions. This is one of the most important URI design rules.

Good:

/users
/orders
/products

Avoid:

/getUsers
/createOrder
/deleteProduct

The action is already represented by the method. GET /users retrieves users. POST /orders creates an order. DELETE /products/25 deletes or removes product 25. Adding verbs to the URI makes the API less uniform and often leads to inconsistent naming.

Use Plural Nouns

Collections should generally use plural names. Use /users, /products, and /orders rather than /user, /product, and /order. Plural naming communicates that the URI represents a collection, and an id under that collection represents one item.

For example, /users/101 reads naturally as user 101 inside the users collection. The convention is simple and widely understood. The most important factor is consistency. If an API chooses plural resource names, it should use them consistently across the API surface.

Testers should look for mixed patterns because they create friction. A suite that calls /users, /product, /OrderList, and /customers is harder to read and maintain than one using consistent plural names.

Use Lowercase and Hyphens

REST URIs are commonly written in lowercase. Lowercase paths avoid confusion in systems where path casing may matter. Use /employees, not /Employees. Use /order-items, not /order_items or /orderItems, when multiple words are needed.

Hyphens improve readability in URLs. They are common in web-facing resource paths and are easier to read than long concatenated names. Consistent lowercase hyphenated naming also helps documentation, test scripts, logs, and defect reports.

This rule is not about beauty alone. Mixed casing and mixed word separators cause avoidable mistakes. A tester may call /ProductItems while the API expects /product-items. Clients may fail because one environment treats path case differently than another. Consistency prevents these failures.

Avoid File Extensions

REST APIs identify resources, not files. Avoid putting file extensions in resource URIs unless the API has a documented reason. Prefer:

/users

over:

/users.json

The representation format should be controlled through headers such as Accept and Content-Type, or through documented API behavior, not through file-looking paths. A user resource can be represented as JSON today and possibly XML or another format tomorrow without changing the resource identity.

There are practical exceptions for downloadable files or static assets, but for REST resource APIs, file extensions usually couple the URI to one representation. Testers should understand whether an extension is part of a file resource requirement or a sign of weak REST design.

Use Resource Hierarchies Carefully

Resource hierarchies show relationships between resources. For example:

/users/101/orders
/departments/10/employees
/orders/100/items

These URIs communicate ownership or containment. The first means orders belonging to user 101. The second means employees in department 10. The third means items belonging to order 100. This can make APIs easy to understand when the relationship is real and useful.

However, deeply nested URIs can become difficult to maintain:

/companies/10/departments/5/teams/2/employees/101/tasks/50

At this point, the URI becomes hard to read, test, and evolve. Deep nesting can also imply strict hierarchy where the business model may be more flexible. A practical rule is to nest only where the parent-child relationship is meaningful and where the parent context is needed to identify or authorize the child resource.

HTTP Methods with URIs

In REST, the URI identifies the resource and the HTTP method determines the action. The URI does not need to change for every operation. For example:

GET /users
POST /users
GET /users/101
PUT /users/101
PATCH /users/101
DELETE /users/101

GET /users retrieves users. POST /users creates a user. GET /users/101 retrieves user 101. PUT /users/101 replaces user 101. PATCH /users/101 partially updates user 101. DELETE /users/101 removes user 101 if allowed.

Testing should confirm that methods are used correctly. GET should not delete data. DELETE should not require an action word in the URI. Unsupported methods should return a documented error such as 405 Method Not Allowed when applicable. Method and URI work together as one contract.

Query Parameters

Query parameters are commonly used for filtering, searching, sorting, pagination, and optional modifiers. They appear after the question mark in a URI:

/products?category=laptops
/users?name=John
/products?sort=price
/users?page=2&size=20

The base resource remains the collection. The query parameters change which subset or ordering of the collection is returned. This makes query parameters suitable for optional criteria rather than resource identity.

API testers should validate normal query values, missing values, empty values, invalid values, boundary values, encoded values, multiple filters, sorting direction, pagination limits, and combinations. Query parameters are a common source of defects because they directly affect search and listing behavior.

Path Parameters vs Query Parameters

Path parameters identify a specific resource or required resource context. Query parameters filter, sort, search, paginate, or modify the response. This difference is important for both design and testing.

A path parameter example is:

/users/101

The id 101 identifies a specific user. A query parameter example is:

/users?country=USA

The country value filters the users collection. If user 101 does not exist, the API may return 404. If no users match country USA, the API may return an empty list with 200. These are different situations, and tests should reflect that difference.

Misusing query parameters for identity or path parameters for optional filters can make APIs confusing. A clean API uses path parameters for identity and query parameters for optional criteria.

Nested Resources

Nested resources represent relationships. /customers/101/orders retrieves orders for customer 101. /orders/100/items retrieves items belonging to order 100. Nesting can be useful when the child resource is naturally scoped under a parent or when authorization depends on the parent context.

Testing nested resources should include valid parent and child combinations, invalid parent id, invalid child id, child not belonging to parent, unauthorized parent access, empty child collection, and deleted parent behavior. For example, /customers/101/orders should not return orders belonging to customer 202.

Nested resources should remain readable. If a URI becomes too deep, consider whether a flatter resource design with query parameters or direct resource identifiers would be clearer. For example, /tasks/50 may be easier than a deeply nested company-department-team-employee-task path if task id is globally unique and authorization can be checked server-side.

Good URI Examples

Good URI examples are short, meaningful, lowercase, resource-based, and consistent:

/users
/users/101
/orders
/orders/1001
/orders/1001/items
/products
/products?name=laptop
/products?page=3&size=20

These examples are readable because they use nouns, plural collections, path parameters for identity, and query parameters for filtering or pagination. A new tester or developer can guess what most of these endpoints do even before reading detailed documentation.

Good URIs reduce the mental effort needed to use an API. They also make automation more readable. A test calling GET /orders/1001/items is easier to understand than one calling GET /getItemsByOrderId in one module and POST /fetchOrderItemList in another.

Bad URI Examples

Bad URI examples often include verbs, uppercase letters, file extensions, mixed naming styles, or action words:

/getProducts
/Products
/products.json
/ProductList
/deleteProduct
/create_new_order

These paths are not always technically broken, but they are less RESTful and less consistent. They make it harder for clients to predict the API shape. They also create confusion about which part of the request identifies the resource and which part describes the operation.

API testers should report these issues carefully. In existing legacy systems, changing URIs may be risky because clients already depend on them. But for new APIs or new versions, design feedback can prevent years of inconsistent usage.

Real-World URI Examples

In an e-commerce system, product resources may use GET /products to retrieve product listings and GET /products/101 to retrieve one product. Cart operations may use POST /cart/items to add an item, PATCH /cart/items/25 to update quantity, and DELETE /cart/items/25 to remove it.

In a banking system, GET /accounts/1001 retrieves an account, GET /accounts/1001/transactions retrieves transactions for that account, and POST /transfers creates a transfer. A tester should verify not only success cases but also access control: one user should not retrieve another user's account or transactions.

In a code-hosting platform, a URI such as /repos/openai/example can identify a repository by owner and name. This shows that path parameters are not always numeric ids. They can be slugs, names, composite identifiers, or other stable identifiers when documented clearly.

URI Design in API Testing

API testers should verify that correct URIs return expected resources, invalid resource ids return proper errors, unauthorized resources are protected, query parameters filter correctly, pagination works, nested resources return only related children, and naming conventions are consistent. URI testing is part functional testing, part contract testing, and part design review.

For invalid resource ids, tests may call:

GET /users/999999

The expected result is often 404 Not Found if the user does not exist, though some security-sensitive APIs may return a more generic response to avoid revealing resource existence. Testers should follow the API contract and security policy.

For query parameters, tests should verify that /users?page=2 returns the second page, /products?category=laptops returns only laptops, and invalid pagination values are rejected or normalized as documented. For nested resources, tests should verify that /customers/101/orders returns only customer 101's orders.

Security and Authorization in URI Design

Resource identifiers often appear directly in URIs, which creates authorization testing requirements. If a user can access /orders/1001, testers should try another user's order id and verify access is denied. This type of testing is often called IDOR testing, or insecure direct object reference testing.

A clean URI does not remove the need for authorization. The server must validate that the authenticated caller is allowed to access the resource identified by the path. This applies to users, accounts, payments, invoices, reports, files, and tenant-specific resources.

URI design should avoid exposing sensitive internal implementation details where possible. Sequential numeric ids are common, but they can make enumeration easier if authorization is weak. Some systems use opaque identifiers or UUIDs, but even those require server-side authorization. Security comes from access checks, not from hiding ids alone.

Testing URI Boundary and Error Scenarios

URI testing should include more than one valid happy path. Path parameters and query parameters are common sources of edge-case defects, so testers should validate boundaries carefully. For numeric ids, try existing ids, non-existing ids, zero, negative values, very large values, decimal values, alphabetic values, blank values, and special characters. For slug-based resources, test uppercase, lowercase, encoded spaces, reserved characters, and unknown slugs.

Query parameters need the same discipline. A pagination endpoint should be tested with first page, middle page, last page, page beyond the available range, zero page, negative page, missing size, very large size, and non-numeric values. A search endpoint should be tested with exact match, partial match, no match, case variations, special characters, encoded values, and multiple filters combined together. These tests reveal whether the URI contract is robust or only works for simple examples.

Error behavior should also be consistent. A missing required path resource may return 404. A malformed id may return 400. An unsupported method may return 405. An unauthorized resource may return 401 or 403 depending on the authentication state and API security policy. Testers should validate both status code and error body so clients receive predictable signals.

Automation should keep these URI edge cases reusable. Instead of writing every invalid id manually in every test, a framework can maintain common path-parameter and query-parameter data sets. This improves coverage while keeping tests readable.

URI Design and Long-Term Maintainability

URI design has long-term consequences because clients depend on URLs. Once an API is published, changing a URI can break mobile apps, partner integrations, automation suites, documentation, bookmarks, SDKs, and internal services. For this reason, URI design should be reviewed before release, not treated as a small implementation detail that can be fixed later.

Stable URI patterns help teams add new resources without confusing consumers. If all collections use plural lowercase nouns, a new endpoint such as /subscriptions is easy to understand. If filtering always uses query parameters, clients know where to look for search options. If nested resources are used only for real parent-child relationships, developers can predict when nesting is appropriate.

Maintainability also affects testing. Consistent URI design allows reusable automation helpers, shared route builders, common negative tests, and clearer defect reports. Inconsistent URI design forces every endpoint to be handled as a special case. That increases maintenance cost and makes test suites harder to scale as the API grows.

Good teams treat URI design as part of the API contract. They document URI patterns, review naming conventions, avoid unnecessary breaking changes, and version APIs when URI changes are unavoidable. Testers can contribute by identifying inconsistency early and explaining how it affects client usage and automation stability.

This early review prevents small naming choices from becoming permanent integration pain and rework after clients, tests, and documentation already depend on them.

Versioning and URI Design

Many APIs include versioning in their URI design, such as /v1/users or /api/v2/orders. URI-based versioning is easy to see and simple for clients to call. Other APIs use headers or media types for versioning. Each approach has tradeoffs.

If URI versioning is used, it should be consistent. Do not mix /v1/users, /users?v=2, and custom version headers randomly across the same API unless there is a clear transition strategy. Versioning affects documentation, routing, tests, client SDKs, and backward compatibility.

Testers should verify that old versions continue working during the support period, new versions expose expected behavior, deprecated versions return documented warnings or errors, and clients cannot accidentally mix incompatible URI versions in the same workflow.

Best Practices

Design URIs around resources, not actions. Use nouns instead of verbs. Prefer plural resource names for collections. Use lowercase letters. Use hyphens for readability when names contain multiple words. Keep URIs short, meaningful, and stable.

Use path parameters for resource identification and query parameters for filtering, searching, sorting, pagination, and optional modifiers. Use nested resources when the relationship is meaningful, but avoid excessive nesting. Avoid file extensions for normal REST resources. Keep naming conventions consistent across all API modules.

Document URI patterns clearly. Include examples for collection resources, individual resources, nested resources, query parameters, pagination, filtering, sorting, and error behavior. Good URI documentation reduces integration defects and helps testers write better automation.

Common Mistakes

A common mistake is using verbs in URIs, such as /getEmployees or /createOrder. REST expects the URI to identify the resource and the method to describe the action. Another mistake is using uppercase or inconsistent casing, such as /Users in one API and /users in another.

Deep nesting is another common issue. A URI such as /companies/10/departments/5/teams/2/employees/101/tasks/50 is difficult to read, test, and maintain. It may be better to expose a direct task resource and use query parameters or authorization rules to handle context.

Teams also mix naming conventions. One service may use plural nouns, another singular nouns, another action names, and another file extensions. This inconsistency makes APIs harder to learn and increases automation maintenance. A platform-level URI style guide helps prevent this.

Another mistake is using query parameters for required identity in ways that make resources unclear. /users?id=101 can work, but /users/101 usually communicates individual resource identity more clearly. Query parameters are better for optional filtering and searching.

Interview-Ready Explanation

Resource identification is one of the core REST principles where every resource is uniquely identified using a URI. A resource can be a user, product, order, payment, invoice, employee, or any other object exposed by an API. REST APIs use resource-oriented URIs such as /users for a collection and /users/101 for an individual resource.

Good URI design uses nouns instead of verbs, plural resource names, lowercase letters, hyphens for readability, and consistent hierarchical structures where relationships are meaningful. HTTP methods such as GET, POST, PUT, PATCH, and DELETE define the operation, so the URI should not include action names like /getUsers or /deleteProduct.

Path parameters identify specific resources, while query parameters are used for filtering, sorting, searching, and pagination. Proper URI design makes APIs predictable, maintainable, developer-friendly, and easier to test. In API testing, testers validate valid URIs, invalid ids, nested resources, query parameters, authorization, naming consistency, and documented error behavior.

Key Takeaway

Resource identification is the foundation of REST URI design. A REST API becomes easier to understand when each URI clearly identifies a resource or collection, and the HTTP method defines the action. Clean URI design improves usability, documentation, automation, and long-term maintainability.

For API testers, the practical rule is to inspect every URI as part of the contract. Validate resource access, path parameters, query parameters, nested relationships, naming consistency, error handling, and authorization boundaries. Strong URI validation helps ensure that an API is not only functional, but also predictable and RESTful.