bhylu.com

Advertisements

Node.js Express Routing

Express registers middleware and route handlers in order. A handler should either produce its response or delegate deliberately, so a request does not hang or receive two responses.

How it works

Install the pinned Express 5.1.0 package in a separate lesson project. The complete source creates a Node HTTP server around the application and performs its own loopback checks. Explicit response shapes and a final not-found handler make the supported API surface visible. No framework package is added to Bhylu’s public JavaScript bundle or its plain browser playground.

Run the code

Run this example locally with Node.js. The exact sources are reviewed on Node 22.15.1 and Node 24.22.1 unless a specialised lab states otherwise. Save the complete source as express-routing.mjs in a separate empty exercises folder and run node express-routing.mjs. There is no Node.js browser runner on Bhylu; the plain JavaScript playground does not provide these filesystem, server or process APIs. In that separate project, install the reviewed packages with npm install --save-exact express@5.1.0. These are teaching dependencies, not changes to the Bhylu website package tree.

Download the complete source. Ordinary Node scripts need no Apache, PHP or MySQL service. File examples own their temporary folders; network fixtures use loopback and available temporary ports.

Example

express-routing.mjs

import express from 'express';
import http from 'node:http';
import { once } from 'node:events';
const app = express();
app.disable('x-powered-by');
app.get('/kits/:id', (request, response) => {
  if (request.params.id !== '701')
    return response.status(404).json({ error: 'NOT_FOUND' });
  response.json({ id: 701, name: 'tripod' });
});
app.use((request, response) => response.status(404).json({ error: 'NOT_FOUND' }));
const server = http.createServer(app);
server.listen(0, '127.0.0.1');
await once(server, 'listening');
const base = 'http://127.0.0.1:' + server.address().port;
try {
  const response = await fetch(base + '/kits/701');
  console.log(response.status, (await response.json()).name);
} finally {
  server.closeAllConnections();
  await new Promise((resolve, reject) =>
    server.close((error) => (error ? reject(error) : resolve())),
  );
}

Expected result

200 tripod

The route parameter arrives as text and only the single supported identifier is accepted. More general identifiers need a deliberate parser. The finally block owns shutdown even if parsing the response or an assertion fails.

Use the Node.js API reference for the precise version contract.