How to Choose the Right Resource Paths for Your REST API (With Examples)

How to Choose the Right Resource Paths for Your REST API (With Examples)

Choosing resource paths sounds simple until you actually sit down to design a real API. I remember staring at a blank endpoint list, unsure whether to write /getUsers, /users, or /user-list. Over the years I have built and reviewed a lot of APIs, and I want to walk you through exactly how I choose resource paths so they feel natural, predictable, and easy for any developer to guess correctly.

What Is a Resource Path?

A resource path is the part of the URL that identifies what you are working with in an API. For example, in:

GET https://api.example.com/orders/482

/orders/482 is the resource path. It tells me that we are dealing with an order, and specifically the order with ID 482. Good resource paths make an API feel obvious. Bad ones make developers reach for the documentation every single time.

Rule 1: Resources Are Nouns, Not Verbs

The most important rule I follow is that a resource path should describe a thing, not an action. Actions belong in the HTTP method, not in the URL.

Avoid this:

POST /createUser
GET /getUserById
POST /deleteOrder

Prefer this:

POST /users
GET /users/{id}
DELETE /orders/{id}

The HTTP method (POST, GET, DELETE) already tells the API what action is happening. Repeating that action in the path is redundant and makes the URL less predictable.

Rule 2: Use Plural Nouns for Collections

I always use plural nouns for a collection of resources, and I stay consistent across the entire API.

GET /users          -> list of users
GET /users/45       -> single user with ID 45
GET /products        -> list of products
GET /products/12     -> single product with ID 12

Mixing singular and plural (/user in one place, /products in another) is one of the fastest ways to make an API feel unpolished and inconsistent.

Rule 3: Represent Relationships with Nested Paths

When one resource clearly belongs to another, I nest the path to reflect that relationship.

GET /users/45/orders          -> all orders belonging to user 45
GET /users/45/orders/901      -> a specific order belonging to user 45

I try to keep nesting shallow — generally no more than two levels deep. Once I go past two or three levels, the URLs become long and fragile, and it usually means I should expose the deeper resource on its own.

Avoid deeply nested paths like this:

GET /users/45/orders/901/items/12/reviews/3

Prefer a flatter, more direct structure:

GET /reviews/3

I can still express the relationship through query parameters or by including relevant IDs in the response body, without dragging the whole hierarchy into every URL.

Rule 4: Use Path Parameters for Specific Resources, Query Parameters for Filtering

This is a distinction I see beginners struggle with a lot.

  • Path parameters identify a specific resource.
  • Query parameters filter, sort, paginate, or search within a collection.
GET /orders/901                      -> path parameter identifies exactly one order
GET /orders?status=shipped&limit=20  -> query parameters filter the collection

I never use a path parameter for something optional, and I never use a query parameter to identify a single unique resource.

Rule 5: Keep Casing and Naming Consistent

I always pick one naming convention and apply it everywhere:

  • Lowercase words
  • Hyphens (-) instead of underscores or camelCase in the URL path
GET /order-items
GET /shipping-addresses

I avoid:

GET /orderItems
GET /Order_Items
GET /OrderItems

Hyphenated, lowercase paths are easier to read, easier to type, and match how most modern REST APIs are designed.

Rule 6: Avoid File Extensions and Unnecessary Words

I never add things like .json or .xml to the end of a path, and I never add filler words like data, info, or api inside the path itself when they add no meaning.

Avoid:

GET /api/getUserData.json

Prefer:

GET /users/45

If I need to support multiple response formats, I use the Accept header instead of the URL.

Rule 7: Design Around Business Concepts, Not Database Tables

I choose resource names based on what makes sense to the people using the API, not what my internal database happens to call things. If my internal table is named tbl_ord_hdr, my public resource is still /orders, because that is the concept a client actually understands.

Rule 8: Handle Actions That Don’t Fit the CRUD Model

Sometimes an action does not map cleanly to create, read, update, or delete. For example, “publishing” an article or “cancelling” an order. In these cases, I treat the action itself as a sub-resource, expressed as a noun.

POST /orders/901/cancellation
POST /articles/12/publication

This keeps the path noun-based while still expressing an action clearly through the HTTP method. I avoid falling back to verbs like /orders/901/cancel, even though I see it done often, because it breaks the noun-based convention I try to keep consistent everywhere else.

Rule 9: Design for Predictability

A good test I use: if a new developer sees one endpoint in my API, can they correctly guess the next one? For example, if they see:

GET /products/12

They should be able to correctly guess:

GET /products
POST /products
PUT /products/12
DELETE /products/12

If a developer cannot predict this pattern from a single example, my naming is probably inconsistent somewhere.

A Full Example Layout

Here is what a clean, well-structured set of resource paths looks like for a simple e-commerce API:

GET    /products
GET    /products/{id}
POST   /products
PUT    /products/{id}
DELETE /products/{id}

GET    /customers/{id}/orders
GET    /orders/{id}
POST   /orders
POST   /orders/{id}/cancellation

GET    /orders/{id}/items

Every path here is a noun, plural for collections, nested only where a real relationship exists, and consistent in casing.

Final Thoughts

Choosing resource paths is really about designing a map that other developers can navigate without needing a guide. I always aim for paths that are predictable, consistent, and shaped around real-world concepts rather than internal code structure. Once you get this right, the rest of your API design tends to fall into place naturally.

Total
0
Shares

Leave a Reply

Previous Post
How to Modify an Existing API Without Breaking Your Users: A Practical Guide

How to Modify an Existing API Without Breaking Your Users: A Practical Guide

Next Post
How to Choose the Right HTTP Methods for API Actions: GET, POST, PUT, PATCH, and DELETE Explained

How to Choose the Right HTTP Methods for API Actions: GET, POST, PUT, PATCH, and DELETE Explained

Related Posts