This episode introduces Express as a refinement of the pure Node.js server patterns: routing with app.get and app.post, the express.json middleware, 404 handling, and error handlers. You build your first REST API with Express.

The manual routing with nested if statements from episode 8 can survive two or three endpoints. For a real API with dozens of routes, you need a framework. Express is the most popular web framework for Node.js — lightweight, minimal, and built on the patterns you already know: handlers, middleware, and next.
Episode 9 introduces Express practically: installing it, defining routes with app.get and app.post, using the express.json middleware, and handling 404s and errors centrally. By the end of the episode, you'll have your first REST API ready to be developed further.
Start from the project folder initialized in episode 2, then add Express:
npm install expressnpm install express adds Express to dependencies in package.json and downloads all of its dependencies. Verify the installation with npm ls express — the output shows the installed version, e.g. express@5.x.x.
Express turns the if pattern into method calls. Each route receives the same handler as in pure Node.js, plus extra features like route parameters:
import express from "express";
const app = express();
app.use(express.json());
app.get("/", (req, res) => {
res.send("Halo dari Express");
});
app.get("/pengguna/:id", (req, res) => {
res.json({ id: req.params.id, nama: "Arman" });
});
app.post("/pengguna", (req, res) => {
res.status(201).json({ diterima: req.body });
});
app.listen(3000, () => {
console.log("API berjalan di http://localhost:3000");
});app.get("/pengguna/:id", ...) defines a route with the dynamic parameter :id, accessed via req.params.id. For other methods, use app.post, app.put, and app.delete. res.json sends an object as JSON with Content-Type: application/json.
In pure Node.js, reading the body requires data and end events. Express provides the express.json() middleware that does everything:
import express from "express";
const app = express();
app.use(express.json({ limit: "1mb" }));
app.use((req, res, next) => {
console.log(req.method, req.url);
next();
});
app.post("/data", (req, res) => {
res.json({ body: req.body });
});app.use(express.json({ limit: "1mb" })) parses the JSON body while limiting its size. Custom middleware is mounted with app.use — order of mounting matters: express.json must be mounted before the routes that read req.body.
Without a 404 handler, Express answers unknown requests with a default HTML page. Define the fallback after all routes:
app.use((req, res) => {
res.status(404).json({ error: "Route tidak ditemukan" });
});
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).json({ error: "Terjadi kesalahan server" });
});The 404 handler is mounted at the end and answers every request that doesn't match any route. The error handler is recognized by its four arguments (err, req, res, next) — Express routes errors here automatically when a handler throws an error or calls next(err).
Start the server and test your endpoints:
node app.js
curl -i http://localhost:3000/pengguna/42
curl -X POST http://localhost:3000/pengguna -H "Content-Type: application/json" -d '{"nama":"Arman"}'curl -X POST http://localhost:3000/pengguna -H "Content-Type: application/json" -d '{...}' sends JSON data as the body. A correct response must show status 201 for the POST route and 200 for GET. We'll refine the JSON response format details in episode 10.
Express is the most common choice, but not the only one. Fastify offers higher performance with built-in schema validation, NestJS brings structured architecture for large teams, and Hono lightens applications running on the edge. For this series we focus on Express because its ecosystem is the largest and its documentation is the most complete, but the concepts you learn — middleware, handlers, and responses — apply to all of them.
Here's what to take away:
req.params accesses route parameters like :id.res.json sends JSON responses with the correct headers.app.use(express.json()) parses the body with a size limit.In the next episode, episode 10, we'll discuss JSON APIs, CORS, and response formats — designing consistent JSON payloads, choosing the right status codes, CORS headers for frontend applications, and handling preflight OPTIONS. You'll build an API ready to be consumed by a frontend.