REST API design best practices voor Node.js

Leer de belangrijkste REST API design best practices voor Node.js. Checklist met praktische tips voor endpoints, versioning, errors en security.

8 juli 20268 min leestijdDoor We Develop Communication

Een goed ontworpen REST API voelt voor developers als een cadeau: voorspelbaar, consistent en zelfverklarend. Een slecht ontworpen API levert juist eindeloze Slack-berichten, workarounds en frustratie op. In dit overzicht krijg je een praktische checklist met REST API design best practices voor Node.js, zodat jouw API schaalbaar, veilig en plezierig in gebruik is.

Of je nu een interne service bouwt of een publieke API lanceert: de principes zijn hetzelfde. Laten we de belangrijkste punten doorlopen.

1. Gebruik resources, geen acties

Een REST API draait om resources (dingen), niet om acties (werkwoorden). Een URL beschrijft wat je aanspreekt, de HTTP-methode bepaalt wat je ermee doet.

Slecht:

POST /getUserById
POST /createNewOrder
GET  /deleteProduct?id=42

Goed:

GET    /users/:id
POST   /orders
DELETE /products/:id

Houd resource-namen in meervoud en lowercase: /users, /orders, /products. Dit maakt je API direct herkenbaar voor iedereen die ooit een REST API heeft aangeroepen.

Nesting met mate

Geneste resources mogen, maar overdrijf niet. /users/:id/orders is prima. /users/:id/orders/:orderId/items/:itemId/reviews/:reviewId wordt onleesbaar. Houd het bij maximaal twee niveaus diep en gebruik anders query-parameters of een aparte endpoint.

2. Respecteer HTTP-methodes en hun semantiek

Elke HTTP-methode heeft een duidelijke betekenis. Gebruik ze zoals bedoeld:

  • GET, data ophalen, nooit muteren. Moet idempotent en safe zijn.
  • POST, nieuwe resource aanmaken of een niet-idempotente actie uitvoeren.
  • PUT, volledige resource vervangen. Idempotent.
  • PATCH, gedeeltelijke update van een resource.
  • DELETE, resource verwijderen. Idempotent.

Een veelgemaakte fout is een GET die in de database schrijft (bijvoorbeeld een view-counter). Browsers, proxies en crawlers mogen ervan uitgaan dat GET veilig is, houd je daaraan.

Meer over hoe je routes opzet lees je in het artikel over HTTP servers en routing in Node.js.

3. Gebruik statuscodes consistent

HTTP-statuscodes zijn je eerste communicatiemiddel met de client. Gebruik ze bewust:

Code Wanneer
200 OK, succesvolle GET, PUT, PATCH
201 Created, succesvolle POST met nieuwe resource
204 No Content, succesvolle DELETE of PUT zonder body
400 Bad Request, validatiefout, malformed input
401 Unauthorized, geen of ongeldige auth
403 Forbidden, wel ingelogd, geen rechten
404 Not Found, resource bestaat niet
409 Conflict, bijvoorbeeld een duplicate key
422 Unprocessable Entity, semantisch ongeldig
429 Too Many Requests, rate limit bereikt
500 Internal Server Error, onverwachte fout

Stuur nooit een 200 OK met een foutmelding in de body. Dat dwingt clients om elke response te parsen voordat ze weten of het goed ging.

4. Ontwerp een uniforme error-structuur

Errors zijn een design-keuze, geen bijzaak. Definieer één format en gebruik het overal:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Email is verplicht.",
    "details": [
      { "field": "email", "issue": "required" }
    ],
    "requestId": "req_8f3a7c"
  }
}

Voordelen: clients kunnen op code reageren (taal-onafhankelijk), message is voor developers, en requestId koppelt de fout aan je server-logs. Dit is een format dat RFC 9457 Problem Details losjes volgt, een prima basis.

Combineer dit met robuuste input-validatie. In validatie met Zod zie je hoe je type-safe schema's opstelt die direct bruikbare error-details opleveren.

5. Versie je API vanaf dag één

Zelfs als je denkt dat je API "alleen intern" is: ooit breek je iets. Begin direct met versioning.

De drie gangbare aanpakken:

  1. URL-versioning, /api/v1/users. Simpel, zichtbaar, makkelijk te cachen. Meest gebruikt.
  2. Header-versioning, Accept: application/vnd.company.v1+json. Schoner, maar minder zichtbaar in logs.
  3. Query-versioning, /users?version=1. Wordt afgeraden, breekt caching.

Kies er één en blijf consistent. Breaking changes? Nieuwe versie. Additieve wijzigingen (nieuw veld, nieuw endpoint)? Gewoon toevoegen binnen dezelfde versie.

6. Gebruik query-parameters voor filtering, sortering en paginering

Filter- en sorteeropties horen niet in het pad, maar in de query-string:

GET /products?category=laptops&minPrice=500&sort=-price&page=2&limit=20

Conventies die goed werken:

  • Filtering: ?status=active&role=admin
  • Sortering: ?sort=createdAt (ascending), ?sort=-createdAt (descending)
  • Paginering: ?page=2&limit=20 of cursor-based ?cursor=abc123&limit=20
  • Veldselectie: ?fields=id,name,email

Voor grote datasets is cursor-based paginering stabieler dan offset-based, omdat nieuwe records de pagina-indeling niet verschuiven.

7. Maak responses voorspelbaar

Stuur altijd JSON, altijd met Content-Type: application/json. Gebruik camelCase voor veldnamen (past bij JavaScript-clients) en houd de structuur consistent:

{
  "data": [
    { "id": "u_1", "name": "Eva" },
    { "id": "u_2", "name": "Sam" }
  ],
  "meta": {
    "total": 42,
    "page": 1,
    "limit": 20
  }
}

Wikkel lijsten niet de ene keer in { "data": [...] } en de andere keer direct als array. Consistentie is belangrijker dan elegantie.

8. Authenticatie en autorisatie goed regelen

Scheid de twee concepten:

  • Authenticatie, wie ben je? (JWT, sessie, OAuth2)
  • Autorisatie, wat mag je? (rollen, scopes, permissies)

Voor API's gebruik je meestal Bearer tokens in de Authorization-header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Sla tokens nooit op in de URL of in logs. Gebruik korte lifetimes (bijvoorbeeld 15 minuten) met refresh-tokens voor langere sessies. Bekijk de OWASP API Security Top 10 voor een actuele lijst met veelvoorkomende fouten.

9. Valideer elke input, zonder uitzondering

Vertrouw nooit input van de client. Valideer op drie niveaus:

  1. Type-validatie, is het een string, number, array?
  2. Formaat-validatie, is de email geldig, is de datum parseerbaar?
  3. Business-validatie, mag deze user deze actie uitvoeren op deze resource?

Dit voorkomt niet alleen bugs, maar ook hele klassen van security-issues. Een goede validatielaag in Node.js bouw je met tools als Zod, Joi of Valibot. Bekijk het artikel over validatie met Zod voor concrete voorbeelden.

10. Gebruik pagination, rate limiting en caching

Een API zonder limits is een API die vroeg of laat omvalt. Drie checks:

  • Paginering standaard aan, geen endpoint dat potentieel 100.000 records teruggeeft.
  • Rate limiting, bijvoorbeeld 100 requests per minuut per IP of per token. Gebruik express-rate-limit of vergelijkbaar.
  • HTTP-caching, gebruik Cache-Control, ETag en Last-Modified voor data die niet elke seconde verandert.

Een goed gezette Cache-Control: public, max-age=300 op een productcatalogus kan je database-load met 90% verlagen.

11. Documenteer met OpenAPI

Een API zonder documentatie bestaat niet. OpenAPI (Swagger) is de standaard: één YAML- of JSON-bestand dat al je endpoints, parameters en responses beschrijft.

Voordelen:

  • Auto-gegenereerde docs (Swagger UI, Redoc)
  • Client-SDK's in tientallen talen
  • Contract-tests tegen je implementatie
  • Mock-servers voor frontend-developers

Tools als express-openapi of fastify-swagger laten je de spec naast je code genereren, zodat deze niet verouderd raakt.

12. Log, meet en monitor

Elk inkomend request krijgt een unieke requestId die je meestuurt in responses en logs. Dit maakt debuggen van productie-incidenten vele malen sneller.

Wat minimaal loggen:

  • Method, pad, statuscode, duration
  • User ID (indien geauthenticeerd)
  • RequestId
  • Relevante business-events (bijvoorbeeld "order created")

Voeg metrics toe (p50/p95/p99 latency, error-rate, throughput) en koppel die aan alerts. Een API zonder observability is een black box die je alleen debugt via klachten van gebruikers.

13. Denk aan idempotentie bij schrijvende requests

Wat als een client een POST /payments doet, geen response krijgt (timeout), en opnieuw probeert? Zonder idempotentie krijg je een dubbele betaling.

De oplossing: accepteer een Idempotency-Key-header. Sla het resultaat van de eerste call op en geef bij een tweede call met dezelfde key het opgeslagen resultaat terug.

POST /payments
Idempotency-Key: 7f3e2d9a-1b4c-4e8f-9a2b-3d6e8f1a4c5b

Vooral voor betalingen, boekingen en andere zijeffecten onmisbaar.

14. Denk aan databases en async-gedrag

REST-design staat niet los van hoe je Node.js runtime werkt. Elke request is een kans om de event loop te blokkeren. Gebruik daarom altijd async/await bij I/O en vermijd synchrone file- of database-calls in request handlers.

Voor database-toegang: gebruik connection pooling, prepared statements en transacties waar nodig. Meer hierover in het artikel over werken met databases in Node.js.

15. Wees backwards-compatible

Breaking changes zijn duur. Vuistregels:

  • Velden toevoegen mag altijd
  • Velden verwijderen of hernoemen = nieuwe versie
  • Statuscodes of error-codes wijzigen = breaking
  • Betekenis van een bestaand veld wijzigen = breaking

Deprecate eerst (met een Deprecation- en Sunset-header), communiceer via release notes, en verwijder pas in een volgende major-versie.

Veelgestelde vragen

Wat is het belangrijkste verschil tussen REST en RESTful?

REST is de architectuurstijl met principes zoals statelessness en resource-oriëntatie. RESTful beschrijft een API die die principes consequent volgt. In de praktijk gebruiken veel teams beide termen door elkaar, maar een echte RESTful API houdt zich strak aan HTTP-semantiek en resource-naming.

Moet ik altijd versioning toepassen op mijn REST API?

Ja, zeker als je API door externe partijen of meerdere clients gebruikt wordt. Zonder versioning breek je bestaande integraties bij elke aanpassing. De meest gangbare aanpak is URL-versioning (/api/v1/users), omdat dit simpel, zichtbaar en makkelijk te cachen is.

Welke HTTP-statuscodes moet ik minimaal ondersteunen?

Minimaal 200, 201, 204, 400, 401, 403, 404, 409 en 500. Gebruik ze consistent zodat clients op basis van de status kunnen reageren zonder de body te parsen. Voeg 422 en 429 toe zodra je validatie en rate limiting serieus neemt.

Is GraphQL niet gewoon beter dan REST?

Niet per se. GraphQL is sterk bij complexe, genestelde data en flexibele clients. REST blijft uitstekend voor resource-gebaseerde CRUD, HTTP-caching en eenvoudige integraties. Kies op basis van je use case en team-ervaring, niet op basis van hype.

Hoe bescherm ik een REST API tegen misbruik?

Gebruik authenticatie (JWT of OAuth2), rate limiting, input-validatie en HTTPS overal. Log verdachte patronen, beperk payload-grootte en gebruik CORS bewust. Security is geen losse laag maar onderdeel van elk endpoint.

Tot slot

Een goede REST API is geen kunstwerk, het is een contract. Hoe voorspelbaarder en consistenter dat contract is, hoe minder tijd jij en je gebruikers kwijt zijn aan verrassingen. Loop deze checklist door voordat je een API live zet, en je voorkomt 80% van de pijn die anders pas in productie opduikt.

Begin klein, wees consistent, documenteer alles, en behandel je API alsof er altijd iemand anders mee gaat werken, want dat is bijna altijd zo.

Veelgestelde vragen

Klaar om digitaal te groeien?

Wij helpen Nederlandse bedrijven met webtechnologie en SEO-strategieën die écht werken. Neem vrijblijvend contact op.