Phase 2 · Web Servers & HTTP APIsModule 11~56 min read

Express 5 Fundamentals & Routers

Move from native HTTP mechanics to focused Express applications with routes, routers, JSON parsing, and async handlers.

What you'll learn

Express builds on Node's HTTP server with a routing and middleware model. You will create an Express 5 application, parse JSON deliberately, organize routers, and keep application construction separate from network startup.

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

  • Create an Express 5 application using ES modules
  • Define routes, parameters, and query input
  • Configure JSON parsing and router modules
  • Use async route handlers with centralized error flow

Core mental model

Node.js becomes easier when you separate the JavaScript language from the runtime and the operating-system capabilities it exposes. Use this table as a decision guide.

ConceptWhat it meansDecision rule
ApplicationAn ordered middleware and route pipelineBuild and export it without listening
RouterA mountable group of related routesGroup by resource or capability, not by HTTP method
Route parameterA named dynamic path segment such as :taskIdUse for resource identity; use query parameters for optional views
Async handlerA handler returning a promiseIn Express 5, return/await the promise so rejection reaches error middleware

Professional workflow

Build and verify Node.js programs from the terminal in small, observable steps.

  1. Install Express and declare ES module mode
  2. Create app.js without opening a port
  3. Register body parsing with an explicit limit
  4. Mount resource routers under stable prefixes
  5. Place the 404 handler after all routes
  6. Place error middleware last
  7. Listen and handle signals only in server.js

Keep the feedback loop short

Run the smallest useful command after every meaningful change. Read the complete error message before editing again, and keep inputs and outputs visible while you learn.

Guided code lab

Create an application without listening

Application construction remains importable by tests. express.json is middleware and should use a deliberate limit.

src/app.js
import express from "express";
import { tasksRouter } from "./routes/tasks-router.js";

export function createApp() {
  const app = express();
  app.disable("x-powered-by");
  app.use(express.json({ limit: "100kb" }));
  app.get("/health", (request, response) => {
    response.json({ status: "ok" });
  });
  app.use("/api/tasks", tasksRouter);
  return app;
}

Group task routes in a Router

The mounted prefix stays in app.js while the router owns resource-relative paths.

src/routes/tasks-router.js
import { Router } from "express";

export const tasksRouter = Router();

tasksRouter.get("/", (request, response) => {
  const completed = request.query.completed;
  response.json({ data: [], filter: { completed } });
});

tasksRouter.get("/:taskId", async (request, response) => {
  const task = await Promise.resolve({
    id: request.params.taskId,
    title: "Learn Express routers",
  });
  response.json({ data: task });
});

Own network startup in one entry point

The entry point chooses host and port. The application module remains free of an open socket.

src/server.js
import { createApp } from "./app.js";

const port = Number(process.env.PORT ?? 3000);
const host = process.env.HOST ?? "127.0.0.1";
const app = createApp();

const server = app.listen(port, host, () => {
  console.log("API listening at http://" + host + ":" + port);
});

process.once("SIGTERM", () => server.close());
process.once("SIGINT", () => server.close());

Production practice

Export the app

Tests should start the app on an ephemeral port; importing application code must not create a hidden listener.

Order deliberately

Express evaluates middleware in registration order, so parsers, routes, 404, and errors must be placed intentionally.

Keep handlers thin

Route handlers translate HTTP to application calls; business rules should remain reusable outside Express.

Common failure mode

A middleware that neither sends a response nor calls next() leaves the request hanging. Every middleware path must make one of those choices.

Independent workshop

Rebuild the native in-memory task API with Express 5 and resource routers.

Your finished workshop must include:

  • createApp and server entry point separation
  • A tasks Router mounted under /api/tasks
  • Configured JSON limit
  • Async handler with a deliberate rejection path
  • JSON 404 and error handlers in the correct order

Definition of done

Run the happy path and at least two edge cases, keep responsibilities separated, and add a short README explaining how to run the program.

Recap & quick check

Key takeaways

  • Express is an ordered middleware pipeline
  • Routers group related endpoints
  • Path parameters identify resources
  • Query parameters shape optional views
  • Express 5 forwards rejected async handlers
  • App creation and listening are separate responsibilities

Quick check

1. Where should app.listen normally live?

2. What does express.json do?

3. How should routes usually be grouped?

4. What happens to a rejected returned promise in an Express 5 handler?

Next: REST API Design & CRUD