Als je in 2026 werkt aan moderne webapplicaties, kom je vrijwel zeker Node.js tegen. Van realtime chatapplicaties tot API's voor grote e-commerceplatformen: Node.js is uitgegroeid tot één van de meest gebruikte technologieën voor server-side JavaScript. Maar wat maakt het precies zo populair? En hoe werkt die mysterieuze event loop waar iedereen het over heeft?
In dit artikel duiken we in de kern van Node.js. Je leert wat het is, hoe het onder de motorkap werkt, en waarom de event loop het geheime wapen is achter razendsnelle, schaalbare applicaties.
Wat is Node.js?
Node.js is een open-source, cross-platform JavaScript-runtime, gebouwd op de V8 JavaScript-engine van Google Chrome. Ryan Dahl introduceerde het in 2009 om JavaScript buiten de browser bruikbaar te maken, bijvoorbeeld op een server of als command-line tool.
Voor Node.js bestond JavaScript vooral om webpagina's interactief te maken. Dahl realiseerde zich echter dat dezelfde non-blocking, event-driven aanpak die browsers gebruiken ook perfect zou werken voor netwerkservers die duizenden gelijktijdige verbindingen moeten afhandelen.
Wat Node.js uniek maakt
De echte innovatie van Node.js zit niet in de taal zelf, maar in het uitvoeringsmodel:
- Single-threaded: JavaScript draait op één hoofdthread.
- Non-blocking I/O: operaties zoals bestanden lezen of database-queries blokkeren de thread niet.
- Event-driven: reacties op gebeurtenissen worden afgehandeld via callbacks, promises of async/await.
- Package ecosystem: via npm heb je toegang tot miljoenen kant-en-klare modules.
Dit maakt Node.js bijzonder geschikt voor I/O-intensieve applicaties, zoals REST API's, realtime diensten en streaming-platformen.
De architectuur van Node.js
Om te begrijpen hoe de event loop werkt, moet je eerst de bouwstenen van Node.js kennen. Het platform bestaat uit drie hoofdcomponenten:
1. V8 Engine
V8 compileert en voert je JavaScript-code uit. Het vertaalt JavaScript direct naar machinecode met just-in-time compilatie, wat voor hoge uitvoeringssnelheid zorgt.
2. Libuv
Libuv is een C-bibliotheek die de asynchrone, non-blocking I/O verzorgt. Het beheert onder andere de event loop, een thread pool en de abstractielaag voor netwerk- en bestandsoperaties.
3. Node.js bindings en standaardbibliotheek
De JavaScript-API's die je gebruikt (zoals fs, http, crypto) zijn wrappers rond C++-implementaties. Via deze bindings praat jouw JavaScript-code met libuv en het onderliggende besturingssysteem.
Wat is de event loop?
De event loop is het hart van Node.js. Het is een oneindige loop die callbacks en events afhandelt zodra ze klaar zijn om uitgevoerd te worden. Zonder de event loop zou Node.js simpelweg niet kunnen functioneren als asynchrone runtime.
Stel je een restaurant voor met één ober (de event loop). In plaats van bij elke tafel te wachten totdat de keuken klaar is, neemt de ober bestellingen op, geeft ze door aan de keuken, en serveert gerechten zodra ze klaar zijn. Terwijl de keuken kookt, bedient de ober andere tafels. Zo werkt Node.js ook: terwijl een database-query wordt uitgevoerd, handelt de event loop andere verzoeken af.
De fasen van de event loop
De event loop doorloopt per iteratie ("tick") zes fasen. Elke fase heeft een eigen callback queue:
- Timers, voert callbacks uit van
setTimeout()ensetInterval()waarvan de drempel is bereikt. - Pending callbacks, verwerkt I/O-callbacks die in de vorige iteratie zijn uitgesteld.
- Idle, prepare, interne Node.js-operaties.
- Poll, haalt nieuwe I/O-events op en voert bijbehorende callbacks uit. Hier brengt de loop meestal de meeste tijd door.
- Check, voert callbacks uit van
setImmediate(). - Close callbacks, handelt callbacks af voor gesloten resources, zoals
socket.on('close', ...).
Tussen elke fase controleert Node.js de microtask queue: promises en process.nextTick() worden direct verwerkt, nog vóór de volgende fase begint.
Een praktisch voorbeeld
Bekijk deze code:
console.log('1');
setTimeout(() => console.log('2'), 0);
setImmediate(() => console.log('3'));
Promise.resolve().then(() => console.log('4'));
process.nextTick(() => console.log('5'));
console.log('6');
De output is: 1, 6, 5, 4, 2, 3.
Waarom? console.log('1') en console.log('6') draaien synchroon. Daarna wordt process.nextTick uitgevoerd (hoogste prioriteit), gevolgd door de promise (microtask). Vervolgens verwerkt de event loop de timers-fase (setTimeout) en daarna de check-fase (setImmediate).
Non-blocking I/O in de praktijk
Het grootste voordeel van Node.js is het non-blocking model. Bekijk dit verschil:
Blocking (synchroon)
const fs = require('fs');
const data = fs.readFileSync('/grote-file.txt');
console.log(data);
console.log('Dit wacht tot de file gelezen is');
De thread blokkeert totdat het bestand volledig is ingelezen. Tijdens deze tijd kan je server geen andere verzoeken afhandelen.
Non-blocking (asynchroon)
const fs = require('fs/promises');
async function readFile() {
const data = await fs.readFile('/grote-file.txt');
console.log(data);
}
readFile();
console.log('Dit draait direct, zonder te wachten');
De thread blijft vrij om andere taken af te handelen. Zodra het bestand gelezen is, plaatst libuv de callback in de juiste queue en verwerkt de event loop hem op het juiste moment.
De rol van de thread pool
Node.js is single-threaded, maar libuv gebruikt intern een thread pool (standaard 4 threads) voor bepaalde taken die niet native asynchroon zijn in het besturingssysteem:
- Bestandssysteem-operaties (
fs) - DNS-lookups (
dns.lookup) - Cryptografische bewerkingen (
crypto.pbkdf2,crypto.randomBytes) - Compressie (
zlib)
Netwerkoperaties gebruiken daarentegen de asynchrone API's van het besturingssysteem (zoals epoll op Linux of kqueue op macOS) en zitten dus niet in de thread pool.
Je kunt de grootte van de thread pool aanpassen met UV_THREADPOOL_SIZE, wat nuttig kan zijn voor workloads met veel cryptografie of bestandsoperaties.
Veelgemaakte valkuilen
Hoewel Node.js krachtig is, zijn er enkele valkuilen die beginners vaak tegenkomen.
CPU-intensieve taken blokkeren de event loop
Omdat JavaScript op één thread draait, kan een zware berekening (zoals het hashen van een groot wachtwoord of complexe beeldverwerking) de event loop volledig blokkeren. Alle inkomende verzoeken moeten dan wachten.
Oplossing: verplaats CPU-intensief werk naar worker threads of een aparte microservice.
process.nextTick recursief aanroepen
Omdat process.nextTick-callbacks vóór elke andere queue worden verwerkt, kan een recursieve aanroep de event loop laten verhongeren. Geen enkele I/O-callback komt dan nog aan de beurt.
Synchrone methoden in productie
Methoden zoals fs.readFileSync of crypto.pbkdf2Sync zijn handig voor scripts, maar dodelijk in een HTTP-server. Gebruik altijd de asynchrone varianten.
Wanneer kies je Node.js?
Node.js is een uitstekende keuze voor:
- REST en GraphQL API's met veel gelijktijdige requests
- Realtime applicaties zoals chat, gaming en collaboration tools
- Microservices die veel I/O doen maar weinig rekenen
- Streamingdiensten voor video, audio of data
- CLI tools en build tooling (webpack, Vite, esbuild)
- Server-side rendering van frameworks zoals Next.js en Nuxt
Voor CPU-intensieve workloads (zoals machine learning inference of video-encoding) zijn talen als Go, Rust of Python vaak geschikter.
Aan de slag met Node.js
Wil je zelf experimenteren? Installeer de LTS-versie via de officiële Node.js website of gebruik een versiemanager zoals nvm of fnm. Zodra Node.js is geïnstalleerd, maak je met één commando een project aan:
npm init -y
Vanaf daar kun je modules toevoegen, een server starten, of scripts schrijven. In volgende artikelen van deze serie duiken we dieper in onderwerpen als modulesystemen, Express, async patterns en performance-optimalisatie.
Veelgestelde vragen
Wat is Node.js precies?
Node.js is een open-source JavaScript-runtime gebouwd op de V8-engine van Chrome. Hiermee kun je JavaScript buiten de browser uitvoeren, bijvoorbeeld op een server. Het is bij uitstek geschikt voor schaalbare netwerkapplicaties.
Wat is de event loop in Node.js?
De event loop is het mechanisme waarmee Node.js non-blocking I/O-operaties uitvoert op een single thread. Het verwerkt asynchrone callbacks in verschillende fasen, zodat je server duizenden verbindingen tegelijk kan afhandelen zonder te blokkeren.
Is Node.js single-threaded of multi-threaded?
Node.js draait JavaScript op één hoofdthread, maar gebruikt onder water een thread pool (libuv) voor zware I/O-taken. Voor CPU-intensief werk kun je worker threads of child processes inzetten.
Waarvoor gebruik je Node.js het beste?
Node.js is ideaal voor API's, realtime applicaties zoals chat en gaming, microservices en streamingdiensten. Voor CPU-zware berekeningen zijn talen als Go of Rust vaak een betere keuze.
Wat is het verschil tussen Node.js en een browser?
Beide gebruiken de V8-engine om JavaScript uit te voeren, maar Node.js heeft geen DOM of window-object. In plaats daarvan biedt het modules voor bestandssystemen, netwerken en processen, wat het geschikt maakt voor server-side werk.
Meer leren
Node.js is een breed platform, en dit artikel is slechts het begin. In onze serie behandelen we binnenkort onderwerpen zoals async/await in Node.js, werken met Express en performance-tuning van Node.js applicaties. Heb je specifieke vragen over je eigen project? Neem gerust contact met ons op, we denken graag mee.