Skip to main content

Command Palette

Search for a command to run...

REST API Design Made Simple with Express.js

Updated
9 min read

If you’ve ever wondered how your phone gets the latest posts from Instagram or how a website saves your profile information, you’re looking at the work of an API.

Specifically, most modern apps use something called a REST API. While it sounds technical, the concept is as simple as ordering food at a restaurant. Let’s break it down using Express.js, the most popular tool for building these in the world of JavaScript.

What is a REST API?

Imagine you're at a restaurant. You (the client) look at a menu and tell the waiter (the API) what you want. The waiter goes to the kitchen (the server), grabs your food, and brings it back. You never need to go into the kitchen yourself.

A REST API works the same way. It's a set of rules that lets two applications talk to each other — your browser or app asks for something, and the server responds with data.

💡 Simple Definition

REST stands for Representational State Transfer. It's just a style of building APIs that is clean, predictable, and easy to understand. When an API follows REST rules, developers know exactly how to use it — no guesswork.

The key rules REST follows are: use URLs to identify things, use HTTP methods to say what to do, and return data in JSON format.

Resources in REST Architecture

In REST, everything is a resource. A resource is simply a "thing" your API can work with — like a user, a product, an order, or a post.

Resources are identified by URLs (also called endpoints). Think of a URL as a home address for your data.

Resource URL Examples

A /users resource might represent all your users. When you go to:

  • /users  →  All users

  • /users/42  →  The user with ID 42

  • /users/42/posts  →  All posts by user 42

Golden Rule:

Always use nouns (things) in URLs, never verbs (actions). The action is expressed by the HTTP method instead. So it's /users — never /getUsers or /deleteUser.

HTTP Methods

Once you have a resource URL, you need to tell the server what to do with it. That's what HTTP methods are for. There are four main ones, and they map directly to four database operations called CRUD.

CRUD vs HTTP Methods Mapping

CRUD Operation HTTP Method What it does Example URL
Create POST Add new data POST /users
Read GET Fetch existing data GET /users
Update PUT Replace existing data PUT /users/1
Delete DELETE Remove data DELETE /users/1

GET — Fetching Data

GET is the most common method. It's like reading a book — you look at the data, but you don't change anything. Safe to call as many times as you want.

// GET all users
const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ]

app.get('/users', (req, res) => {
  res.json(users);
});

// GET a single user by ID
app.get('/users/:id', (req, res) => {
  const userId = Number(req.params.id);
  const user = users.findIndex(user => user.id === userId)
  res.json({ user });
});

POST — Creating Data

POST sends new data to the server to be created. Think of it like filling out a form and hitting Submit.

// POST — Create a new user
app.post('/users', (req, res) => {
  const { name, email } = req.body;

  // Basic validation
  if (!name || !email) {
    return res.status(400).json({ message: "Name and email are required" });
  }

  // Create new user
  const newUser = {
    id: users.length + 1,
    name,
    email
  };

  // 🔥 Save to array
  users.push(newUser);

  res.status(201).json(newUser);
});

PUT — Updating Data

PUT replaces an existing item completely. You're saying "replace everything about this user with what I'm sending now."

// PUT — Update a user
app.put('/users/:id', (req, res) => {
  const { name, email } = req.body;
  const userId = Number(req.params.id);

  const index = users.findIndex(user => user.id === userId);

  if (index === -1) {
    return res.status(404).json({ message: "User not found" });
  }

  // 🔥 Update the actual data
  users[index] = {
    ...users[index],  // keep existing fields
    name,
    email
  };

  res.status(200).json(users[index]);
});

DELETE — Removing Data

DELETE removes an item from the server. Simple and permanent — like tossing something in the bin.

app.delete('/users/:id', (req, res) => {
  const userId = Number(req.params.id);

  const index = users.findIndex(user => user.id === userId);

  if (index === -1) {
    return res.status(404).json({ message: "User not found" });
  }

  // 🔥 Remove user from array
  users.splice(index, 1);

  // 204 = success, no content
  res.status(204).send();
});

Status Codes Basics

Every time your server responds, it sends a status code — a 3-digit number that tells the client what happened. Think of them like emoji reactions: the server is telling you "thumbs up", "not found", or "something broke".

💡 Analogy

Status codes are like delivery status notifications — 200 is "Delivered!", 404 is "Address not found", and 500 is "Delivery truck broke down."

Common Status Codes You Must Know

✅ Success Codes (2xx)

Code Name Meaning
200 OK Request successful, data returned
201 Created New resource created successfully
204 No Content Success, but nothing to return
GET /users → 200 OK
POST /users → 201 Created
DELETE /users/1 → 204 No Content

❌ Client Errors (4xx)

Code Name Meaning
400 Bad Request Invalid data sent
401 Unauthorized Login required
403 Forbidden No permission
404 Not Found Resource doesn’t exist
GET /users/999 → 404 Not Found
POST /users (missing name) → 400 Bad Request

💥 Server Errors (5xx)

Code Name Meaning
500 Internal Server Error Something broke on server
GET /users → 500 Server Error (bug in code)

Designing Routes Using REST Principles

A route is a URL + HTTP method combination. Great REST routes are clean, consistent, and instantly understandable to any developer.

REST Route Checklist

  • Use plural nouns for collections: /users, not /user

  • Use IDs in the path for single items: /users/5

  • Never put verbs in routes: ❌ /getUser, ❌ /createUser

  • Keep them lowercase and use hyphens for multi-word resources: /blog-posts

Good Routes vs ❌ Bad Routes

✅ Good ❌ Bad Why it matters
GET /users GET /getUsers Verb belongs in the HTTP method, not the URL
POST /users POST /createUser POST already means "create"
DELETE /users/5 GET /deleteUser?id=5 Use DELETE method + ID in path
GET /blog-posts GET /BlogPosts URLs should be lowercase, hyphens for spaces

Full Example: Users API in Express.js

Let's put it all together. Here's a complete, working Express.js REST API for a Users resource. This is what a real beginner-level API looks like:

👥 Users API — All Routes at a Glance

Method Route What it does Status Code
GET /users Get all users 200 OK
GET /users/:id Get one user by ID 200 OK
POST /users Create a new user 201 Created
PUT /users/:id Update existing user 200 OK
DELETE /users/:id Delete a user 204 No Content
const express = require('express');
const app = express();

app.use(express.json()); // Parse JSON request bodies

// Fake in-memory "database"
let users = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob',   email: 'bob@example.com' },
];

// ─────────────────────────────────────────
// GET /users — Fetch all users
// ─────────────────────────────────────────
app.get('/users', (req, res) => {
  res.status(200).json(users);
});

// ─────────────────────────────────────────
// GET /users/:id — Fetch one user
// ─────────────────────────────────────────
app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id === +req.params.id);
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }
  res.status(200).json(user);
});

// ─────────────────────────────────────────
// POST /users — Create a new user
// ─────────────────────────────────────────
app.post('/users', (req, res) => {
  const { name, email } = req.body;
  const newUser = { id: users.length + 1, name, email };
  users.push(newUser);
  res.status(201).json(newUser);
});

// ─────────────────────────────────────────
// PUT /users/:id — Update a user
// ─────────────────────────────────────────
app.put('/users/:id', (req, res) => {
  const index = users.findIndex(u => u.id === +req.params.id);
  if (index === -1) {
    return res.status(404).json({ error: 'User not found' });
  }
  users[index] = { ...users[index], ...req.body };
  res.status(200).json(users[index]);
});

// ─────────────────────────────────────────
// DELETE /users/:id — Delete a user
// ─────────────────────────────────────────
app.delete('/users/:id', (req, res) => {
  users = users.filter(u => u.id !== +req.params.id);
  res.status(204).send();
});

// ─────────────────────────────────────────
// Start server
// ─────────────────────────────────────────
app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

🎉 What You Just Built

That's a fully working REST API! It handles Create, Read, Update, and Delete for users — follows clean REST conventions — and returns the right status codes. This is the foundation every backend developer needs to know.

Run it Yourself in 3 Steps

# Step 1: Create a project and install Express
mkdir my-api && cd my-api
npm init -y
npm install express

# Step 2: Create your file and paste the code above
touch users-api.js

# Step 3: Run the server
node users-api.js
# → Server running on http://localhost:3000

Now open your browser or a tool like Postman/requestkit and visit http://localhost:3000/users — you'll see your users list in JSON!

What You Learned

🌐REST = Rules: A style that makes APIs predictable and easy to use by any developer.

📦Resources = URLs: Everything your API exposes is a named resource with its own URL.

🔧Methods = Actions: GET, POST, PUT, DELETE tell the server what to do with the resource.

📊Status Codes = Replies: 200s = success, 400s = client error, 500s = server error.

HaPPy CoDiNg