aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webontwikkeling

Express API: een REST API bouwen met Node.js

Een Express API is een van de snelste manieren om een backend te bouwen met Node.js: een handvol regels start een HTTP-server, beantwoordt verzoeken en valt netjes uiteen in lagen naarmate hij groeit. In dit artikel bouwen we een kleine maar echte REST API vanaf nul — we organiseren routes met routers, bundelen gedeeld werk in middleware, en sluiten af met centrale foutafhandeling die je verlost van verspreide try/catch-blokken.

Het project opstarten

Begin met een lege map en installeer Node en Express. Node.js 18 of hoger wordt aanbevolen; die versies ondersteunen fetch en moderne JavaScript-functies van nature.

mkdir blog-api && cd blog-api
npm init -y
npm install express
npm install --save-dev nodemon

Voeg "type": "module" toe aan je package.json om ES-modules in te schakelen, zodat je de import-syntaxis kunt gebruiken. Laten we ook een script definiëren dat de server herstart wanneer een bestand verandert tijdens het ontwikkelen:

{
  "type": "module",
  "scripts": {
    "dev": "nodemon server.js",
    "start": "node server.js"
  }
}

De eerste server

De kern van een Express-applicatie is een app-object. We voegen de express.json()-middleware toe om inkomende JSON-bodies te parsen en beginnen met een eenvoudige health-check-route.

// server.js
import express from "express";

const app = express();
app.use(express.json());

app.get("/health", (req, res) => {
  res.json({ status: "ok", uptime: process.uptime() });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`API draait: http://localhost:${PORT}`);
});

Voer npm run dev uit en bezoek http://localhost:3000/health — je zou een JSON-antwoord moeten zien. Dat is alles; je hebt een API. Laten we hem nu klaarmaken om te groeien.

Routes splitsen met een Router

Elke route in server.js stapelen werkt voor piepkleine projecten, maar wordt snel onleesbaar. Het Router-object van Express laat je gerelateerde routes in een apart bestand groeperen en onder één voorvoegsel koppelen. Stel dat we een posts-resource beheren.

// routes/posts.js
import { Router } from "express";

const router = Router();

const posts = [
  { id: 1, title: "Hallo wereld", body: "Eerste bericht" }
];

router.get("/", (req, res) => {
  res.json(posts);
});

router.get("/:id", (req, res) => {
  const post = posts.find(p => p.id === Number(req.params.id));
  if (!post) {
    return res.status(404).json({ error: "Bericht niet gevonden" });
  }
  res.json(post);
});

router.post("/", (req, res) => {
  const { title, body } = req.body;
  const post = { id: posts.length + 1, title, body };
  posts.push(post);
  res.status(201).json(post);
});

export default router;

Koppel deze router vervolgens in de hoofdapplicatie onder het voorvoegsel /posts:

import postsRouter from "./routes/posts.js";
app.use("/posts", postsRouter);

Nu werken GET /posts, GET /posts/1 en POST /posts allemaal. Elke nieuwe resource (gebruikers, reacties) krijgt zijn eigen routerbestand, en server.js blijft slank.

Middleware: gedeeld werk op één plek bundelen

Middleware is een keten van functies waar een verzoek doorheen gaat voordat het een antwoord bereikt. De signatuur is (req, res, next); als het klaar is, roept het next() aan om de controle aan de volgende door te geven. Logging, authenticatie, rate limiting en andere overkoepelende zaken horen hier thuis. Laten we een eenvoudige verzoeklogger schrijven:

// middleware/logger.js
export function logger(req, res, next) {
  const start = Date.now();
  res.on("finish", () => {
    const ms = Date.now() - start;
    console.log(`${req.method} ${req.originalUrl} ${res.statusCode} - ${ms}ms`);
  });
  next();
}

Voeg het toe vóór alle routes en elk verzoek wordt automatisch gelogd:

import { logger } from "./middleware/logger.js";
app.use(logger);

Validatie kan ook een middleware zijn. Bijvoorbeeld een kleine bewaker die de body controleert vóór POST /posts:

export function validatePost(req, res, next) {
  const { title } = req.body;
  if (!title || title.trim() === "") {
    return res.status(400).json({ error: "title is verplicht" });
  }
  next();
}

Koppel het daarna alleen aan die route: router.post("/", validatePost, handler). Dat is de kracht van middleware — herhaalde logica één keer schrijven en overal inpluggen.

Centrale foutafhandeling

Een van de handigste functies van Express is een speciale middleware met vier argumenten voor fouten: (err, req, res, next). Deze functie wordt na alle routes gedefinieerd en vangt elke fout die ergens wordt opgeworpen. Zo hoef je niet in elke handler een aparte try/catch te strooien.

Om ervoor te zorgen dat fouten in async-functies hem bereiken, is de schoonste aanpak een kleine wrapper (Express 5 stuurt async-fouten automatisch door, maar in 4.x is deze helper handig):

// utils/asyncHandler.js
export const asyncHandler = (fn) => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

We kunnen een foutklasse definiëren en die comfortabel opwerpen binnen asyncHandler:

// utils/ApiError.js
export class ApiError extends Error {
  constructor(status, message) {
    super(message);
    this.status = status;
  }
}

Ten slotte voegen we de foutmiddleware en een 404-vangnet voor onbekende routes toe. Deze moeten altijd als laatste komen:

// 404 — geen route komt overeen
app.use((req, res) => {
  res.status(404).json({ error: "Resource niet gevonden" });
});

// Centrale foutafhandeling
app.use((err, req, res, next) => {
  const status = err.status || 500;
  if (status === 500) console.error(err);
  res.status(status).json({ error: err.message || "Serverfout" });
});

Nu volstaat een enkele throw new ApiError(404, "Bericht niet gevonden") in een handler; dit ene punt regelt de rest. In productie is het een goede gewoonte om de melding voor 500-fouten generiek te houden, zoals hierboven, zodat je geen details naar de client lekt.

De structuur schoon houden

Naarmate het project groeit, houdt een eenvoudige mappenindeling alles onderhoudbaar:

  • routes/ — één routerbestand per resource.
  • controllers/ — de daadwerkelijke bedrijfslogica die een route afhandelt (houdt de router dun).
  • middleware/ — herbruikbare onderdelen zoals logger, auth, validatie.
  • utils/ — helpers zoals asyncHandler en ApiError.

Met deze scheiding beantwoordt de router alleen "welke URL gaat naar welke functie," en het echte werk leeft in de controller. Wanneer je een database toevoegt (bijvoorbeeld PostgreSQL of MongoDB), schaalt deze structuur bijna zonder wijzigingen.

Veelgestelde vragen

Kan ik niet gewoon de ingebouwde http-module gebruiken in plaats van Express?

Dat kan, maar dan moet je routing, body-parsing en de middlewareketen met de hand schrijven. Express biedt dat allemaal als een dunne laag; het is makkelijk te leren en het ecosysteem is enorm. Zelfs voor een kleine API bespaart het tijd.

Waarom is de volgorde van middleware belangrijk?

Express voert middleware uit in de volgorde waarin het met app.use wordt toegevoegd. Je kunt een body niet valideren voordat express.json() hem parst, en de foutmiddleware moet na alle routes komen zodat hij hun fouten kan opvangen.

Moet ik Express 4 of 5 gebruiken?

Express 5 is nu stabiel en stuurt fouten van async-handlers automatisch door naar de foutmiddleware. Voor nieuwe projecten kun je 5 verkiezen; op bestaande 4.x-projecten is het asyncHandler-patroon hierboven een veilige en gangbare oplossing.

Wil je je API naar een hoger niveau tillen? Als je authenticatie, database-integratie of een productieklare Express-basis nodig hebt, kunnen we er samen naar kijken. Neem contact met me op.

Bu kategorideki tüm yazılar →

Devamı için