Skip to main content

Command Palette

Search for a command to run...

REST API Design Made Simple with Express.js

Updated
6 min read
REST API Design Made Simple with Express.js
M

Full-stack developer with a good foundation in frontend, now specializing in backend development. Passionate about building efficient, scalable systems and continuously sharpening my problem-solving skills. Always learning, always evolving.

A few months into building a backend, most developers hit the same wall. The API works, but it feels messy. Routes like /getUserData and /createNewUserNow start piling up, and suddenly nothing feels predictable. Every new endpoint becomes a small design decision, and inconsistencies creep in fast.

This is exactly where REST becomes useful. Not as theory, but as a constraint system that forces clarity.


What REST API Really Means

At a practical level, REST is about using HTTP the way it was designed.

An API is just a contract between a client and a server:

  • The client sends a request

  • The server processes it

  • The server responds with data and a status

HTTP already defines:

  • How to identify resources using URLs

  • How to express intent using methods like GET or POST

  • How to communicate outcomes using status codes

REST simply says: use these primitives consistently instead of inventing your own conventions.

Why this matters:

  • Clients can predict behavior without reading documentation

  • APIs scale better across teams

  • Debugging becomes easier because everything follows known patterns

A key detail from HTTP semantics: Request methods define the intended action on a resource, not the resource itself (RFC Editor)

That distinction drives everything in REST.


Resources: The Core Idea

Stop thinking in terms of actions. Start thinking in terms of things.

In REST, everything revolves around resources.

Examples:

  • users

  • orders

  • products

A resource is just:

Something your system manages and exposes via a URL

Bad mental model:

  • “I need an endpoint to create a user”

Better model:

  • “I have a users resource, and I want to create one”

That shift changes your entire API design.

Instead of:

POST /createUser
GET /getUsers

You get:

POST /users
GET /users

Cleaner, consistent, and scalable.


HTTP Methods: Intent Over Implementation

HTTP methods are not just verbs. They carry strict semantics defined by the protocol.

GET — Retrieve data

  • No side effects

  • Safe and idempotent (W3C)

  • Used for reading resources

Example:

GET /users
GET /users/123

POST — Create a new resource

Example:

POST /users

Typical behavior:

  • Creates a new user

  • Returns 201 Created with location of new resource (API7.ai)


PUT — Replace or update a resource

Example:

PUT /users/123

Important nuance:

  • PUT replaces the full resource, not partial updates

DELETE — Remove a resource

Example:

DELETE /users/123

Calling it multiple times should not change the outcome after the first deletion.


Why This Matters

These semantics are not optional.

If you misuse them:

  • Caching breaks

  • Retries cause bugs

  • Clients behave unpredictably

This is where many APIs fail in production.


Status Codes: Communicating Outcomes

Status codes are not decoration. They are part of the contract.

Common ones you should actually use:

  • 200 OK Request succeeded

  • 201 Created Resource successfully created

  • 400 Bad Request Client sent invalid data

  • 404 Not Found Resource does not exist

  • 500 Internal Server Error Something broke on the server

HTTP defines status codes as a way to describe the result of a request clearly (Wikipedia)

Bad pattern:

Always return 200 with { success: false }

This breaks standard behavior and makes debugging harder.


Designing Routes the REST Way

Focus on nouns, not verbs.

Naming rules that scale:

  • Use plural resources:

    /users
    /orders
    
  • Use IDs for specific resources:

    /users/123
    
  • Avoid action-based routes:

    ❌ /getUser
    ❌ /createUser
    
  • Let HTTP methods define behavior:

    GET /users
    POST /users
    PUT /users/123
    DELETE /users/123
    

Consistency matters more than cleverness.


Example: Users Resource in Express.js

Let’s build a minimal but clean design.

Routes

GET    /users        -> get all users
GET    /users/:id    -> get single user
POST   /users        -> create user
PUT    /users/:id    -> update user
DELETE /users/:id    -> delete user

Express Implementation (simplified)

const express = require('express');
const app = express();

app.use(express.json());

// Get all users
app.get('/users', (req, res) => {
  res.status(200).json([]);
});

// Get one user
app.get('/users/:id', (req, res) => {
  res.status(200).json({ id: req.params.id });
});

// Create user
app.post('/users', (req, res) => {
  res.status(201).json({ message: 'User created' });
});

// Update user
app.put('/users/:id', (req, res) => {
  res.status(200).json({ message: 'User updated' });
});

// Delete user
app.delete('/users/:id', (req, res) => {
  res.status(200).json({ message: 'User deleted' });
});

app.listen(3000);

What’s important here

  • URLs represent resources, not actions

  • Methods define behavior

  • Status codes match intent

This structure is predictable for any client consuming your API.


Diagram: CRUD vs HTTP Methods Mapping

Think of it like this:

  • Create → POST → /users

  • Read → GET → /users or /users/:id

  • Update → PUT → /users/:id

  • Delete → DELETE → /users/:id

The key idea:

CRUD is your business logic HTTP methods are how you expose it


Diagram: REST Request–Response Lifecycle

Visualize this flow:

  1. Client sends request Example: POST /users

  2. Server receives request

    • Matches route

    • Reads method

    • Validates input

  3. Server performs operation

    • Creates user in database
  4. Server sends response

    • Status: 201 Created

    • Body: created resource

This cycle repeats for every request.


Final Thoughts: What Actually Makes a Good REST API

Most REST tutorials focus on syntax. That’s not the problem in real systems.

The real challenge is consistency under scale.

Good REST design means:

  • Every endpoint follows the same mental model

  • Clients don’t need special-case logic

  • New developers can predict behavior instantly

If your API requires explanation for every route, it’s not RESTful in practice.

Keep it simple:

  • Resources over actions

  • Methods over custom logic

  • Standards over shortcuts

That’s what holds up in production.