Building REST APIs

By Syncfusion®, Inc. · Last updated

REST basics, idempotency, API routers and versioning, collection routes with filtering, search, sorting, pagination and field selection, create, read, update and delete, PUT versus PATCH, status codes, consistent JSON and route structure.

What you will learn

  • Design resource URLs and versioned API routers
  • List collections with filters, sorting and pages
  • Create, read, update and delete resources
  • Choose the right REST status codes
  • Structure routes with auth, validation and central errors

REST APIs Introduction

API = a set of URLs that programs call to read and change data.

  • REST = a common style for web APIs, built on plain HTTP.
  • Resource = one kind of thing, such as a menu item or an order.
  • Collection = the list of all of them: /api/menu.
  • Single resource = one of them: /api/menu/3.
  • HTTP methods say what to do. JSON carries the data.

Simple lesson: A REST API is nouns in the URL and verbs in the method.

A Real-World Example

The canteen app on students' phones needs data, not web pages.

  1. The app asks GET /api/menu. It gets a JSON list.
  2. A student orders: POST /api/orders with the items.
  3. The kitchen marks it ready: PATCH /api/orders/41 with { "status": "ready" }.
  4. A wrong order is cancelled: DELETE /api/orders/41.

Every action is a method plus a URL. The phone app, a website and a kitchen screen can all use the same API.

In this lesson, you will learn:

  1. REST basics: resources, statelessness and idempotency.
  2. How to organise API routers and versions.
  3. How to list, filter, sort and page collections.
  4. How to create, read, update and delete resources.
  5. Which status codes to send and when.
  6. How to structure routes, validation and errors.
  7. How to document your API with OpenAPI.

REST Fundamentals

  • Resource endpoints: GET /api/menu, GET /api/menu/3.
  • Stateless: each request carries everything needed. The server does not remember the last request.
  • Methods map to actions: GET reads, POST creates, PUT replaces, PATCH changes, DELETE removes.
  • Idempotent = repeating the request has the same effect as doing it once.
  • GET, PUT and DELETE are idempotent. POST is not, and PATCH may not be.

A table mapping HTTP methods on /api/menu and /api/menu/:id to create, read, update and delete

Method URL Action Success GET/api/menuList all200 POST/api/menuCreate one201 + Location GET/api/menu/:idRead one200 PUT/api/menu/:idReplace one200 PATCH/api/menu/:idChange part200 DELETE/api/menu/:idRemove one204

Two URLs and five methods cover every basic action on a resource.

Idempotency matters when networks fail. If a phone loses signal and resends a DELETE, nothing extra happens. If it resends a POST, a second order may be created, so apps often add an idempotency key header for important POSTs.

Simple lesson: Collections and single resources, with methods for actions.

Organising API Routes

  • One router per resource: routes/menu.js, routes/orders.js.
  • Base path: mount APIs under /api, so they never clash with pages.
  • Versioning: /api/v1/.... A breaking change goes to /api/v2 while old apps keep working.
  • Nested resources: items inside an order: /api/v1/orders/:orderId/items.
  • Use mergeParams: true for nested routers. See Routing and Routers.
  • Every answer has the same shape: { data } for success, { error } for problems.

Simple lesson: /api/v1/<resource> with one router per resource.

Collection Routes

A good list endpoint lets clients shape the list with query options.

  • Filter: ?veg=true.
  • Search: ?q=dosa.
  • Sort: ?sort=price or ?sort=-price for high to low.
  • Paginate: ?page=2&limit=10.
  • Select fields: ?fields=name,price.
  • Send page details too, so clients know how many pages exist.
  • An empty result is still 200 with data: []. "Nothing matched" is not an error.
  • limit is capped at 50, so nobody can ask for a million rows.

Simple lesson: Lists answer 200 with data and page details, even when empty.

Creating Resources

  • Read the JSON from req.body, and validate it. See Input Processing and Validation.
  • Create a new identifier, usually from the database or crypto.randomUUID().
  • Answer 201 Created.
  • Send the new resource back, so the client sees its ID.
  • Set the Location header to the new resource's URL.

Reading, Updating and Deleting

  • Read one: 200 with the resource, or 404 when it does not exist.
  • PUT = replace the whole resource. Missing fields are a mistake.
  • PATCH = change only the fields sent. Others stay as they are.
  • 409 Conflict = the change clashes with current data, such as a duplicate name.
  • Hard delete = remove it for good. Soft delete = mark it with deletedAt and hide it.
  • DELETE answers 204 No Content with an empty body.
  • Look at Headers on the first POST: Location: /api/v1/menu/3.
  • Adding Idli again gives 409.
  • PUT without a price gives 422. PATCH with only a price works.
  • After DELETE, Idli is gone from the list, but still in the array with a deletedAt date.

Easy way to remember: PUT = the whole plate. PATCH = just add more chutney.

Simple lesson: 201 + Location for create, 200 for read and update, 204 for delete.

JSON Responses

  • Use res.json(), or res.status(code).json() when the code is not 200.
  • Return objects at the top level, such as { data: [...] }, not bare arrays. You can add meta later.
  • Empty results: { data: [] } for lists, 204 for "done, nothing to say".
  • Keep one shape everywhere: { data, meta } for success, { error: { code, message } } for failure.
  • Never send internal fields, such as password hashes, deletedAt or staff notes.
  • The unsafe route leaks the password hash, the fees and a private note.
  • toPublic makes the list of public fields clear, and new database columns stay private by default.

Simple lesson: One response shape for the whole API, with nothing private inside.

REST Status Codes

Success

  • 200 OK: read or update worked.
  • 201 Created: a new resource exists. Add Location.
  • 202 Accepted: queued for later, such as sending a report by email.
  • 204 No Content: worked, and there is nothing to send back.

Errors

  • 400 Bad Request: the request cannot be read, such as broken JSON.
  • 401 Unauthorized: not logged in. 403 Forbidden: logged in, not allowed.
  • 404 Not Found: no such resource.
  • 409 Conflict: clashes with current data.
  • 422 Unprocessable Content: readable, but breaks the rules.
  • 429 Too Many Requests: slow down. See Express Security.
  • 500 Internal Server Error: your code failed. Send a safe message only.

A 500 answer must never contain error stacks, SQL or file paths. Log the details on the server. See Error Handling.

Simple lesson: The status code is part of your API. Pick it as carefully as the data.

Route Implementation Structure

A finished API route is a short chain of small parts:

  1. Router definition: the method and path.
  2. Authentication middleware: who is calling?
  3. Access-control middleware: may they do this?
  4. Validation middleware: is the input correct?
  5. Route handler: do the work and send the answer.
  6. Central error middleware: turn any thrown error into a safe JSON answer.
  • Watch the codes: 401, 201, 422, 403, 204.
  • Each concern lives in its own file, and the route reads like a sentence.

Simple lesson: Authenticate, authorise, validate, handle, and catch errors in one place.

Documenting the API with OpenAPI

Other developers cannot read your code. They read your API docs.

  • OpenAPI = a standard JSON or YAML file that lists every route, its input and its answers.
  • Tools turn it into a web page where people can try each route. The most common is Swagger UI.
  • It can also make client code, mock servers and tests.
  • Keep it next to your routes and update it in the same commit.
  • Code-first: write the routes, then the spec. Design-first: write the spec, then build to match it.
import swaggerUi from "swagger-ui-express";

app.use("/api/docs", swaggerUi.serve, swaggerUi.setup(openapi)); // a "try it" web page
  • $ref reuses one schema in many places, like a variable.
  • Open /api/docs in the browser on your computer to see and try every route.

Simple lesson: An OpenAPI file is the user manual of your API. Serve it, and keep it up to date.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
POST /api/createItemVerbs belong in URLsNouns in URLs, verbs in methodsA messy, guessable-only API
200 for a new resourceAny 2xx is fine201 with LocationClients cannot find the new item
404 for an empty listNo results is "not found"200 with data: []Apps show an error for no matches
PUT that keeps missing fieldsPUT and PATCH are the samePUT replaces, PATCH changes partData silently changes
Sending the whole database rowExtra fields are harmlessPick the fields to sendPassword hashes and notes leak
No limit capClients ask for sensible sizesCap page sizeOne request downloads everything
A different error shape per routeEach route can chooseOne shape everywhereClients need special code per route

Check Yourself

Exercises

What to Remember

  • REST: nouns in URLs, verbs in methods, JSON in bodies.
  • Mount APIs under /api/v1, one router per resource.
  • Lists support filter, search, sort, page and fields, and answer 200 even when empty.
  • Create: 201 + Location. Read and update: 200. Delete: 204.
  • PUT replaces, PATCH changes part. 409 for conflicts, 422 for rule breaks.
  • One response shape, no private fields, and errors handled in one place.
  • Describe every route in an OpenAPI file, and serve it.

Next: Error Handling. You will catch every kind of error and answer safely.