REST API Design and URL Naming
By the Syncfusion DevTutorials team · Last updated
Turn action-style endpoints into clean REST resource URLs, nest related resources, and add filtering, sorting and capped pagination with the query string.
A REST API organizes data as resources. A resource is any thing that the API manages, such as an order, customer, menu item, or product. In REST, the URL identifies which resource you want to work with, while the HTTP method tells the server what action to perform on that resource. This keeps URLs simple, predictable, and easy to understand.
For example:
GET /ordersreads orders.POST /orderscreates a new order.GET /orders/101reads a specific order.DELETE /orders/101removes a specific order.
A good REST API uses clear resource names instead of action names. This makes it easier for developers to learn the API, predict URLs, and build applications without constantly checking documentation.
Watch the Endpoints Multiply
The Brew & Bytes API started with only a few endpoints. Then the application needed to show:
- All orders for a customer.
- Open orders for a customer.
- Open orders sorted by total.
Each new requirement created another endpoint named after an action.
Program.cs — one endpoint per question
It works, but the URL names become longer and harder to manage:
/getOrdersByCustomerId/getOpenOrdersByCustomerId/getOpenOrdersByCustomerIdSortedByTotal
As more features are added, more action-based URLs appear. Soon there are dozens of endpoints, and it becomes difficult to remember what exists or predict what the next URL should be.
In simple words: action-based URLs become messy as an API grows. REST solves this problem by using a small number of resource-based URLs that can answer many different requests.
TL;DR
- A resource is a thing managed by the API, such as an order, customer, or menu item.
- Use plural nouns for collections:
/orders,/customers. - Use an id to access a single resource:
/orders/101. - Put related resources under their parent resource:
/customers/7/orders. - Let the HTTP method describe the action.
- Do not put words such as
get,create, ordeletein URLs. - Use query strings for filtering, sorting, and pagination.
- Always limit the maximum page size.
A Real-World Example
Think about a library. You do not ask for a book by describing an action such as: Get the book from shelf 4.
Instead, you identify where the book is located.
For example: Fiction → Shelf 4 → Book 12
The location identifies the item. What you do with it, such as borrowing or returning it, is a separate action. REST follows the same idea. A URL identifies a resource: /customers/7/orders The HTTP method decides what happens:
GETreads the orders.POSTcreates a new order.
A REST URL contains a collection, an id, optional child resources, and filters in the query string.
What You Will Learn
In this lesson, you will learn:
- What a resource is.
- How to create REST-style URLs.
- How to use filtering and sorting.
- How pagination works.
- How URL rules can be checked automatically.
Nouns, Not Verbs
The same API can be designed using resource names instead of action names.
Program.cs — resources, ids and nesting
| Old URL | REST URL |
|---|---|
GET /getAllOrders | GET /orders |
GET /getOrderById?id=101 | GET /orders/101 |
GET /getOrdersByCustomerId?id=7 | GET /customers/7/orders |
POST /createOrder | POST /orders |
POST /deleteOrderById?id=101 | DELETE /orders/101 |
Resources form a tree structure. Collections contain items, and related resources are grouped underneath.
If order 999 does not exist: GET /orders/999
the server should return: 404 Not Found because the requested resource does not exist. In C#, {id:int} means that the route expects a numeric id. In Hono, :id works the same way and can be accessed with: c.req.param("id")
REST URLs should be simple, consistent, and easy to read. Use plural nouns (such as /orders), keep URLs in lowercase, use hyphens between words (/menu-items), avoid verbs, and place resource IDs in the path. Also, avoid deep nesting whenever possible. For example, /customers/7/orders is clear and readable, while /customers/7/orders/101/items/3/notes is unnecessarily complex.
Filter and Sort
Instead of creating a new endpoint for every search requirement, use query strings.
For example:
/orders?status=open&sort=-total
Program.cs — filter and sort with the query string
This request means:
- Show only open orders.
- Sort by total value.
- Highest totals first.
The minus sign (-) commonly means descending order.
Examples:
sort=total
Smallest to largest.
sort=-total
Largest to smallest.
Using filters and sorting allows one endpoint to support many different requests.
Pagination
A small shop might have a few orders.
A real system could have thousands or millions.
Returning every record in one response would be slow and inefficient.
Pagination solves this problem by returning a small page of results at a time.
Pagination returns only part of a large result set and includes information needed to load more results.
Program.cs — pages with a size limit
Example: ?page=2&pageSize=2
The response includes:
- Current page
- Page size
- Total number of records
- Data for that page
The client can use the total count to know:
- How many records exist
- Whether more pages are available
Why Limit Page Size?
A client may try: ?pageSize=500 or even: ?pageSize=1000000
Returning that much data can overload the API. For this reason, APIs often enforce a maximum size. Example: Maximum page size = 100 Even if the client requests more, the server returns only 100 items.
Always cap the page size. Without a limit, any client can request every row in one call. This is a real security risk: it is one of the OWASP API Security Top 10, called unrestricted resource consumption.
Page Numbers vs Cursors
This lesson uses page numbers because they are easy to understand.
Example: ?page=2&pageSize=20
However, when new records are added frequently, page numbers can cause duplicate or missed results.
Many large APIs use cursor pagination instead.
A cursor says: Give me the next 20 records after this item.
This keeps the results stable even while new records are being created.
Check URLs Automatically
Teams often create API design rules.
A URL linter can automatically check those rules.
UrlLinter.cs — check URLs against the rules
Examples:
Bad:/getOrdersByCustomerId Bad: /menu_items Good: /orders Good: /customers/7/orders
Automatic checks help teams keep URL naming consistent.
Write your URL rules down once and share them with the team. A short style guide ("plural nouns, lowercase, hyphens, ids in the path, filters in the query string") stops a new endpoint from breaking the pattern.
Cursor Pagination
Cursor pagination avoids problems that happen when data changes between requests. Instead of requesting a page number, the client sends a cursor.
Program.cs — cursor pagination
Example:
First response: "nextCursor": "aWQ6MTA1"
The client sends that cursor back: ?after=aWQ6MTA1
The server then returns the next set of results. Even if a new order appears meanwhile, records are not duplicated or skipped. When no more data exists, the API returns: "nextCursor": null This tells the client that pagination has reached the end.
Treat the cursor as an opaque string. Clients should send it back exactly as they got it and never build one themselves. Then you can change what is inside it later without breaking anyone.
Where You See This
- Public APIs use resource-based URLs such as
/customers/{id}and/orders/{id}. - Admin dashboards use filtering, sorting, and pagination on data tables.
- Mobile applications load large lists one page at a time.
- E-commerce applications organize products, customers, and orders as resources.
Common mistakes
| Symptom | Cause | Fix |
|---|---|---|
| Dozens of near-identical endpoints | One endpoint for each question, named after an action | One resource URL, with filters in the query string |
| Clients cannot guess URLs | Mixed styles: /getMenu, /order_list, /Customers | Use plural nouns, lowercase and hyphens everywhere |
| A list request is slow and huge | There is no page size limit | Add pages, and cap the size |
| The app shows an item twice while scrolling | Page numbers are used on a list that changes fast | Use a cursor ("after id 105") for that list |
| Apps break when you change the cursor format | Clients build or decode cursors themselves | Treat nextCursor as opaque, and send it back unchanged |
Exercises
What to Remember
- A URL names a resource. The method says what to do with it.
- Use plural nouns, lowercase, hyphens and no verbs:
/orders,/orders/101,/customers/7/orders. - Filter, sort and page with the query string.
- Always cap the page size.
- Return
404when the URL names something that does not exist.
Next: HTTP Status Codes and Error Handling. You will make every reply say clearly whether it worked, and why not.