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.

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.
The url module helps break a URL into comparable parts. Combine it with req.method for full routing:
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.
The second argument parse(req.url, true) enables automatic query string parsing. Access parameters through url.query:
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.
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:
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.
A request with the POST method carries data in the body. Since the body is a stream, collect its data chunks and then parse:
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.
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.
Here's what to take away:
req.method and url.pathname.parse(req.url, true) gives access to pathname and query.next function.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.