REST API Design Made Simple with Express.js

APIs are just structured conversation between client and server
Every time an app loads your profile, saves a new post, or deletes an item from your cart, it’s having a conversation with a server — a request goes out, a response comes back. An API (Application Programming Interface) is simply the agreed-upon shape of that conversation: what to ask for, how to ask for it, and what kind of answer to expect. REST is one of the most common, widely understood ways to structure that conversation clearly and predictably — and this guide builds it from the ground up, using a single running example: a users resource.

What REST API Means
REST (Representational State Transfer) is a set of principles for designing APIs around resources — things like users, orders, or products — accessed and manipulated through a consistent, predictable set of URLs and HTTP methods.
The core idea: instead of inventing a custom, one-off structure for every single API action, REST gives you a shared, conventional pattern — so once you understand it, you can reasonably guess how any well-designed REST API is likely to be organized, even one you’ve never seen before.
Resources in REST Architecture
A resource is a “thing” your API exposes — a noun, not a verb. users, orders, products are all resources. REST APIs are organized around these nouns, with actions expressed through HTTP methods rather than baked into the URL itself.
Good (REST-style): GET /users
Not REST-style: GET /getAllUsers
The URL identifies what you’re working with (users); the HTTP method identifies what action you want to perform on it. This separation is the foundation everything else in REST design builds on.

HTTP Methods
REST relies on a small set of standard HTTP methods, each with a clear, conventional meaning.
GET — retrieve data
GET /users
Asks for data, without changing anything on the server. Safe to call repeatedly — it should never have side effects.
POST — create something new
POST /users
Sends data to the server, typically to create a new resource. Unlike GET, POST is expected to change something — in this case, adding a new user.
PUT — update an existing resource
PUT /users/42
Updates an existing resource, typically replacing it with the provided data. This targets one specific user (42), not the entire collection.
DELETE — remove a resource
DELETE /users/42
Removes the specified resource entirely — again, targeting one specific user by ID.

Status Codes Basics
Every response includes a status code — a short number summarizing what happened. A few genuinely essential ones to know first:


A simple habit worth building early: the status code should always genuinely match what actually happened — returning 200 for a failed request (with an error message buried in the body) makes an API confusing and harder to work with, even if the data itself is technically informative.
Designing Routes Using REST Principles
Good REST route naming follows a few consistent conventions:
Use plural nouns for resources
/users (not /user)
/orders (not /order)
Use the HTTP method to express the action, not the URL
Good: DELETE /users/42
Bad: GET /deleteUser?id=42
Use URL parameters to identify a specific resource
GET /users/42 — one specific user
GET /users — the entire collection
Keep nesting shallow and meaningful
GET /users/42/orders — that user's orders (a reasonable, meaningful nesting)
Nesting works well when it genuinely represents a real relationship (a user’s orders) — but avoid nesting purely for the sake of it, which quickly makes URLs unwieldy and hard to predict.

Example Resource: Users
Bringing everything together, here’s a complete, conventional REST design for a users resource, along with its Express.js implementation.
The route design

Implementing it in Express
const express = require("express");
const app = express();
app.use(express.json());
let users = [
{ id: 1, name: "Aarav" },
{ id: 2, name: "Priya" },
];
// GET /users — list all users
app.get("/users", (req, res) => {
res.status(200).json(users);
});
// POST /users — create a new user
app.post("/users", (req, res) => {
const newUser = { id: users.length + 1, name: req.body.name };
users.push(newUser);
res.status(201).json(newUser);
});
// GET /users/:id — get one specific user
app.get("/users/:id", (req, res) => {
const user = users.find(u => u.id === Number(req.params.id));
if (!user) return res.status(404).json({ error: "User not found" });
res.status(200).json(user);
});
// PUT /users/:id — update a specific user
app.put("/users/:id", (req, res) => {
const user = users.find(u => u.id === Number(req.params.id));
if (!user) return res.status(404).json({ error: "User not found" });
user.name = req.body.name;
res.status(200).json(user);
});
// DELETE /users/:id — delete a specific user
app.delete("/users/:id", (req, res) => {
const index = users.findIndex(u => u.id === Number(req.params.id));
if (index === -1) return res.status(404).json({ error: "User not found" });
users.splice(index, 1);
res.status(204).send();
});
app.listen(3000, () => console.log("Server running on port 3000"));The full request-response lifecycle

Final Takeaway
REST design comes down to a small, consistent set of decisions applied over and over: identify your resource as a plural noun, use HTTP methods to express what you’re doing to it rather than baking actions into the URL, use status codes that genuinely reflect what happened, and keep nesting shallow and meaningful. Once this pattern is internalized with something as simple as users, it scales naturally to any resource — orders, products, comments — without needing to reinvent the structure each time. That consistency is really the entire point of REST: a well-designed API should be at least partly guessable, even before you've read a single line of its documentation.
Frequently Asked Questions
What’s the difference between PUT and POST?
> POST typically creates a new resource, without you specifying its exact identifier in advance. PUT updates an existing resource at a specific, known location (like /users/42) — you're telling the server exactly which resource to modify.
Should GET requests ever change data on the server? No —
> GET should always be "safe," meaning it only retrieves data and never has side effects. Any action that changes data belongs to POST, PUT, DELETE, or similar methods, never GET.
Why use 204 instead of 200 for a successful DELETE?
> 204 No Content communicates "the request succeeded, and there's genuinely nothing meaningful to send back" — which is exactly the case after successfully deleting something. Using 200 with an empty body works too, but 204 communicates the situation slightly more precisely.
Do all REST APIs have to follow these conventions exactly?
> Not by strict technical requirement — but following them consistently is what makes an API predictable and easy for other developers (or your future self) to work with. Deviating without good reason usually just makes an API harder to use and reason about.
Originally published by Mr Madhukar
Read the complete article on Medium with full formatting & reader responses.