SyntaxStudy
Sign Up
Express.js Designing a RESTful API with Express
Express.js Beginner 1 min read

Designing a RESTful API with Express

A RESTful API maps HTTP methods to CRUD operations on resources identified by URLs. The conventions are: GET for retrieval (collection and single item), POST for creation, PUT or PATCH for full or partial updates, and DELETE for removal. Status codes communicate the outcome: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 422 Unprocessable Entity, 500 Internal Server Error. Versioning the API in the URL path (`/api/v1/`) allows you to introduce breaking changes in `/api/v2/` without disrupting existing clients. Keeping resource URLs as nouns (not verbs) and using nested paths sparingly keeps the API intuitive — prefer `/posts/:postId/comments` only when comments cannot exist without a post. Always return a consistent response envelope so clients can rely on the shape of success and error responses. A simple convention: successes return `{ data: ... }` and errors return `{ error: { message, code } }`. Enforcing this through a response helper function ensures no route accidentally breaks the contract.
Example
// routes/api/v1/posts.js
const express = require('express');
const router  = express.Router();

// Response helpers
const ok      = (res, data, status = 200) => res.status(status).json({ data });
const fail    = (res, message, status = 400) =>
    res.status(status).json({ error: { message } });

// In-memory store (replace with DB)
let posts = [{ id: 1, title: 'Hello World', body: 'First post.' }];
let nextId = 2;

router.get('/',      (req, res) => ok(res, posts));
router.get('/:id',   (req, res) => {
    const post = posts.find(p => p.id === +req.params.id);
    if (!post) return fail(res, 'Post not found', 404);
    ok(res, post);
});
router.post('/',     (req, res) => {
    const { title, body } = req.body;
    if (!title) return fail(res, 'title is required', 422);
    const post = { id: nextId++, title, body };
    posts.push(post);
    ok(res, post, 201);
});
router.patch('/:id', (req, res) => {
    const post = posts.find(p => p.id === +req.params.id);
    if (!post) return fail(res, 'Post not found', 404);
    Object.assign(post, req.body);
    ok(res, post);
});
router.delete('/:id', (req, res) => {
    posts = posts.filter(p => p.id !== +req.params.id);
    res.status(204).end();
});

module.exports = router;