First Express 5 Server: Routes, Requests, and Responses

First lesson of the API with Express course, free to read: about 15 minutes, 3 exercises and 3 quiz questions. This is the catalogue's base lesson; in the student space, it is rewritten for your profile.

Objectives

  • Create an Express 5 application and define routes
  • Read path parameters, query parameters, and the request body
  • Return JSON responses with the right status code

Express applicationRoutingHTTP status codes

Express is the most widely used web framework in the Node.js ecosystem. It builds on the http module and adds readable routing, middleware, and handy helpers like res.json. Version 5 is the current version: it is the one npm install express installs.

A minimal server

// src/server.js (ES modules project: "type": "module" in package.json)
import express from "express";

const app = express();
app.use(express.json());           // parses JSON bodies -> req.body

app.get("/health", (req, res) => {
  res.json({ status: "ok" });      // status 200 by default, JSON Content-Type
});

const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => console.log(`API at http://localhost:${port}`));

Run it with node --watch src/server.js and test with curl http://localhost:3000/health.

Reading the request

const notes = [{ id: 1, titre: "Groceries", fait: false }];

// Path parameter: /notes/42 -> req.params.id === "42" (a STRING)
app.get("/notes/:id", (req, res) => {
  const id = Number(req.params.id);
  const note = notes.find((n) => n.id === id);
  if (!note) return res.status(404).json({ error: "Note not found" });
  res.json(note);
});

// Query string: /notes?fait=true -> req.query.fait === "true"
app.get("/notes", (req, res) => {
  const { fait } = req.query;
  const resultat = fait === undefined ? notes : notes.filter((n) => String(n.fait) === fait);
  res.json(resultat);
});

// JSON body: requires app.use(express.json())
app.post("/notes", (req, res) => {
  const note = { id: notes.length + 1, titre: req.body.titre, fait: false };
  notes.push(note);
  res.status(201).location(`/notes/${note.id}`).json(note);
});

Three sources of data, three objects: req.params, req.query, req.body. All of them come from the client: never trustworthy, always to be validated (lesson 3).

Essential status codes

Code Usage
200 OK Successful read or update
201 Created Resource created (with the Location header)
204 No Content Successful deletion, no body (res.status(204).end())
400 Bad Request Invalid data
401 / 403 Not authenticated / not authorized
404 Not Found Resource does not exist
500 Server error

Each request must receive a single response. The return in front of res.status(404)... avoids sending a second one (the Cannot set headers after they are sent error).

Grouping with express.Router

// src/routes/notes.js
import { Router } from "express";
export const notesRouter = Router();
notesRouter.get("/", listerNotes);
notesRouter.get("/:id", lireNote);
notesRouter.post("/", creerNote);

// src/server.js
app.use("/api/notes", notesRouter);   // GET /api/notes/3 -> lireNote

What changes with Express 5

  • Errors from async handlers are automatically passed to the error handler (lesson 4).
  • Paths are stricter: a wildcard must be named (/fichiers/*chemin), and an optional segment is written /livres{/:format}.
  • Without express.json(), req.body is undefined (and no longer {}).
  • req.query is read-only; app.del() and res.sendfile() are gone.

Key takeaways

A route = method + path + handler. Read params, query, and body while converting them, respond only once with the right status, and organize resources with Router.

Exercises

Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.

  1. CodeDifficulty 2JavaScript in the browser2 hints

    Match a path against a route pattern

    To understand routing, write matchRoute(pattern, path): pattern is a path like /notes/:id/commentaires/:cid. Return an object of the parameters ({ id: "3", cid: "7" }) if path matches, otherwise null. Segments without : must be identical; the number of segments must be the same; values are decoded with decodeURIComponent. Ignore any query string (?...).

    function matchRoute(pattern, path) {
      // À compléter
    }
  2. CodeDifficulty 1JavaScript in the browser2 hints

    GET /notes/:id route handler

    Write the handler getNote(req, res) used like this: app.get("/notes/:id", getNote). It looks in the notes array for the note whose id (a number) matches req.params.id (a string):

    • found: res.json(note);
    • non-integer id: res.status(400).json({ error: "Identifiant invalide" });
    • missing: res.status(404).json({ error: "Note introuvable" }).
    const notes = [
      { id: 1, titre: "Courses" },
      { id: 2, titre: "Réunion" },
    ];
    
    function getNote(req, res) {
      // À compléter
    }
  3. Multiple choiceDifficulty 11 hint

    The right status code

    A POST /api/notes request successfully creates a note. Which status should you return?

    • 200 OK
    • 201 Created
    • 204 No Content
    • 302 Found

End-of-lesson quiz

  1. Where is 42 in GET /notes/42?tri=date with the route /notes/:id?

    • req.query.id
    • req.params.id
    • req.body.id
  2. What is req.body in Express 5 if no body parser is installed?

    • {}
    • undefined
    • An empty string
  3. How do you write a path wildcard in Express 5?

    • /fichiers/*
    • /fichiers/*chemin
    • /fichiers/(.*)

The rest of the course

  1. Middleware: Express's Processing Chain18 min
  2. Validating Incoming Data18 min
  3. Handling Errors with Express 515 min
  4. Structuring a Complete CRUD REST API20 min

API with Express · Intermediate · 3 h 26 min