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 /orders reads orders.
  • POST /orders creates a new order.
  • GET /orders/101 reads a specific order.
  • DELETE /orders/101 removes 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, or delete in 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:

  • GET reads the orders.
  • POST creates 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:

  1. What a resource is.
  2. How to create REST-style URLs.
  3. How to use filtering and sorting.
  4. How pagination works.
  5. 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 URLREST URL
GET /getAllOrdersGET /orders
GET /getOrderById?id=101GET /orders/101
GET /getOrdersByCustomerId?id=7GET /customers/7/orders
POST /createOrderPOST /orders
POST /deleteOrderById?id=101DELETE /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

SymptomCauseFix
Dozens of near-identical endpointsOne endpoint for each question, named after an actionOne resource URL, with filters in the query string
Clients cannot guess URLsMixed styles: /getMenu, /order_list, /CustomersUse plural nouns, lowercase and hyphens everywhere
A list request is slow and hugeThere is no page size limitAdd pages, and cap the size
The app shows an item twice while scrollingPage numbers are used on a list that changes fastUse a cursor ("after id 105") for that list
Apps break when you change the cursor formatClients build or decode cursors themselvesTreat 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 404 when 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.