CodingNic

Express.js Fundamentals

JSON Responses and HTTP Status Codes

Express.js Fundamentals 12 min read

JSON Responses and HTTP Status Codes

Objectives

By the end of this lesson, you should be able to:

  • Send JSON responses with res.json()
  • Choose the correct HTTP status code for a given outcome
  • Combine status codes and JSON to signal success and failure clearly

💡 Why this matters: An API client (a frontend app, another service) relies on the status code to know whether a request succeeded, without ever reading the response body. Getting this right is what separates a working API from one that technically returns data but is unpleasant and unreliable to build against.

⚠️ A note on verification: every snippet and every response shown below was actually run and tested with real HTTP requests.

A Small Books API

javascript
const express = require('express');
const app = express();
app.use(express.json());

const books = [
  { id: 1, title: 'The Pragmatic Programmer' },
  { id: 2, title: 'Clean Code' }
];

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

app.get('/books/:id', (req, res) => {
  const book = books.find(b => b.id === Number(req.params.id));
  if (!book) {
    return res.status(404).json({ error: 'Book not found' });
  }
  res.status(200).json(book);
});

app.post('/books', (req, res) => {
  const { title } = req.body;
  if (!title) {
    return res.status(400).json({ error: 'title is required' });
  }
  const newBook = { id: books.length + 1, title };
  books.push(newBook);
  res.status(201).json(newBook);
});

app.listen(4006);
bash
curl -i http://localhost:4006/books/99
text
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8

{"error":"Book not found"}
bash
curl -i -X POST http://localhost:4006/books -H "Content-Type: application/json" -d '{"title":"Refactoring"}'
text
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8

{"id":3,"title":"Refactoring"}
bash
curl -i -X POST http://localhost:4006/books -H "Content-Type: application/json" -d '{}'
text
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8

{"error":"title is required"}

Every response here pairs a status code with a JSON body that matches it, 200 with the requested data, 404 with an error explaining what wasn’t found, 201 with the newly created resource, 400 with an error explaining what was wrong with the request. The return before res.status(404).json(...) matters, without it, execution would continue to the line below and try to send a second response, which throws an error.

Common Status Codes to Know

200 OK, a successful request with a body to return. 201 Created, a resource was successfully created (typically after POST). 400 Bad Request, the client sent something wrong, a missing field, invalid data. 404 Not Found, the requested resource doesn’t exist. 500 Internal Server Error, something failed on the server’s side, not the client’s fault (Module 8 covers this in depth). Status codes are grouped by their first digit: 2xx means success, 4xx means the client made a mistake, 5xx means the server did.

Try It

  1. Add a PUT /books/:id route updating a book’s title, returning 200 with the updated book if found, or 404 if not.
  2. Add a DELETE /books/:id route removing a book, returning 200 with a confirmation message if found, or 404 if not.
  3. Add validation to the PUT route rejecting an empty title with 400, matching the POST route’s validation.
  4. Explain, in your own words, why an API returning 200 OK with a body like {"error": "not found"} is worse design than returning 404 with the same body.

Recap

  • res.status(code).json(data) is the standard pattern, pairing a status code with a matching JSON body.
  • 2xx means success, 4xx means the client’s request was wrong, 5xx means the server failed.
  • return before sending an error response prevents execution from continuing to send a second, conflicting response.

Next lesson: this module’s exercises, building a small multi-route Express API.