Learning Node.js - Routing, Middleware, and Request/Response
Episode 8 of 23

Learning Node.js - Routing, Middleware, and Request/Response

This episode builds manual routing with the url module, parses query strings, applies the next-based middleware pattern, and reads the request body. You create a multi-route API with pure Node.js without a framework.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

The HTTP server from episode 7 still answers every request the same way. But real applications have many endpoints: /pengguna, /artikel, /login, and so on. That requires routing — the mechanism for matching a URL and method to the right handler.

Episode 8 builds that foundation with pure Node.js: a manual router based on the url module, query string parsing, the middleware pattern with a next function, and reading the request body. This understanding matters because Express (episode 9) is just a refinement of the same pattern.

Manual Routing

Matching Method and Path

The url module helps break a URL into comparable parts. Combine it with req.method for full routing:

JSManual router
import http from "node:http";
import { parse } from "node:url";
 
const server = http.createServer((req, res) => {
  const url = parse(req.url, true);
 
  if (req.method === "GET" && url.pathname === "/") {
    res.end("Beranda");
  } else if (req.method === "GET" && url.pathname === "/pengguna") {
    res.end("Daftar pengguna");
  } else {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("Tidak ditemukan");
  }
});
 
server.listen(3000);

parse(req.url, true) produces an object with the pathname and query properties. By comparing both against conditions, you build routing that can be extended. At the end of the if chain, always provide a 404 fallback.

Parsing URL and Query

Getting Parameters from the Query String

The second argument parse(req.url, true) enables automatic query string parsing. Access parameters through url.query:

JSRead the query string
import http from "node:http";
import { parse } from "node:url";
 
const server = http.createServer((req, res) => {
  const url = parse(req.url, true);
  if (url.pathname === "/pengguna") {
    res.end("Detail pengguna dengan id " + url.query.id);
  } else {
    res.writeHead(404);
    res.end();
  }
});
 
server.listen(3000);

A request GET /pengguna?id=42 produces url.query.id with the value "42". Note that query values are always strings — convert them with Number(...) before using them in logic. This pattern is the seed of route parameters in Express.

The Middleware Pattern

A Chain of Functions with next

Middleware is a function that runs before the main handler, may modify req and res, and then hands control to the next function via next. This pattern was born in Node.js and fully adopted by Express:

JSMiddleware pattern
function logger(req, res, next) {
  console.log(req.method, req.url);
  next();
}
 
function auth(req, res, next) {
  const adaToken = req.headers["authorization"];
  if (!adaToken) {
    res.writeHead(401);
    res.end("Unauthorized");
    return;
  }
  next();
}

logger(req, res, next) logs the request and then calls next() to continue; auth checks the header and can stop the chain with a 401 response. The function calling next() is the key to the middleware chain — the order of invocation determines the application's behavior.

Reading the Request Body

Gathering Data from the Stream

A request with the POST method carries data in the body. Since the body is a stream, collect its data chunks and then parse:

JSRead the request body
import http from "node:http";
 
const server = http.createServer((req, res) => {
  let body = "";
  req.on("data", (chunk) => {
    body += chunk;
  });
  req.on("end", () => {
    const data = JSON.parse(body);
    console.log(data.nama);
    res.end("Data diterima");
  });
});
 
server.listen(3000);

req.on("data", ...) collects every chunk of the body, and after the end event the complete body is ready to be parsed. For JSON payloads, JSON.parse(body) converts the text into an object. In production, limit the body size so the server doesn't run out of memory — Express has a built-in solution we'll see in episode 9.

Bringing All Patterns Together

A Clean Handler Architecture

Routing, middleware, and body parsing work together: middleware runs first for logging and authentication, the router forwards to the right handler, and the handler reads the body when needed. This structure — a chain of functions before and during the response — is the foundation of Express and other frameworks.

In production code, handlers are usually split into separate files per resource, and middleware is assembled in a single list. The cleaner you separate these responsibilities, the easier the code is to test — something we'll do in episode 18.

Closing

Here's what to take away:

  • Manual routing compares req.method and url.pathname.
  • parse(req.url, true) gives access to pathname and query.
  • Query values are always strings; convert types before use.
  • Middleware hands control over through a next function.
  • Middleware can stop the chain with a direct response.
  • The request body is taken from the stream with the data and end events.

In the next episode, episode 9, we'll discuss Express.js or a minimal framework for APIs — installing Express, routing with app.get and app.post, the express.json middleware, and handling 404s and errors. All the patterns from episode 8 will appear in a cleaner form.

Learning Node.js - Routing, Middleware, and Request/Response | Learn Node.js