Events
Node is built around events: “a request arrived”, “data was read”, “the user pressed Ctrl+C”. Instead of waiting in a loop, your code registers listeners and Node calls them when things happen.
On this page · 5 min · Intermediate
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Intermediate
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Explain event-driven programming
- Listen to events with on and once
- Recognise events on servers, streams and the process
- Shut down gracefully on SIGINT/SIGTERM
Explanation#
Event-driven code says “when X happens, run this function” — like addEventListener in the browser. The event loop calls listeners when the event is emitted.
Many Node objects emit events: an HTTP server emits "request" and "listening", a stream emits "data", "end" and "error", the process emits "exit" and signals like "SIGINT".
obj.on(name, fn) listens every time; obj.once(name, fn) listens only the first time; obj.off(name, fn) stops listening.
The "error" event is special: if nobody listens and an error is emitted, Node throws and the process crashes. Always handle errors on streams and emitters.
Graceful shutdown: listen for SIGINT (Ctrl+C) and SIGTERM (sent by Docker, Kubernetes, PM2), stop accepting new requests, finish current ones, close the database, then exit.
Syntax#
server.on('request', (req, res) => { … });
stream.on('data', chunk => { … });
stream.once('end', () => { … });
process.on('SIGTERM', shutdown);
Code example: Events on a server and on the process#
import http from 'node:http';
const server = http.createServer();
server.on('request', (req, res) => { // runs for every request
res.end(`You asked for ${req.url}`);
});
server.once('listening', () => { // runs only once
console.log('Event: listening');
});
server.on('close', () => console.log('Event: close'));
process.once('beforeExit', () => console.log('Event: beforeExit (nothing left to do)'));
async function shutdown(signal) {
console.log(`Received ${signal}, closing server…`);
server.close();
}
process.on('SIGINT', shutdown); // Ctrl+C
process.on('SIGTERM', shutdown); // docker stop, kubectl, PM2
server.listen(3001, async () => {
const body = await (await fetch('http://localhost:3001/products')).text();
console.log('Response:', body);
await shutdown('demo-signal'); // simulate a shutdown signal
});
Event: listening Response: You asked for /products Received demo-signal, closing server… Event: close Event: beforeExit (nothing left to do)
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The server is created without a handler; the "request" listener is added with on.
- "listening" fires when listen succeeds; once removes the listener after the first call.
- fetch triggers a "request" event, and the listener writes the response.
- shutdown closes the server; when it is closed, the "close" event fires.
- With no more work, the process emits "beforeExit" and exits.
Real-world use case#
In Kubernetes, pods receive SIGTERM before they are stopped. An API that listens for it finishes in-flight requests and closes its MySQL pool, so deployments never cut off customers mid-payment.
Common mistakes#
An emitted "error" with no listener crashes the process. Attach error handlers to streams, sockets and emitters.
Registering a listener inside a request handler adds one per request and leaks memory (MaxListenersExceededWarning).
process.exit() in the handler drops active requests. Close servers and pools first.
Best practices#
- Handle "error" on every emitter and stream
- Use once for one-off events
- Implement graceful shutdown for SIGINT/SIGTERM
- Remove listeners you no longer need
Remember this#
on = every time. once = first time only. off = stop.
Unhandled "error" event = crash.
SIGTERM → stop accepting, finish work, close, exit.
Practice exercise#
Your task: Write a script that logs "Bye!" when the process is about to exit, and "Got Ctrl+C" on SIGINT before exiting with code 0.
Need a hint?
Show solution
process.on('exit', code => console.log(`Bye! (exit code ${code})`));
process.on('SIGINT', () => {
console.log('Got Ctrl+C');
process.exit(0);
});
console.log('Running… press Ctrl+C to stop (or wait for the script to end)');
Mini quiz#
Which method registers a listener that runs only the first time?
Want more? Practise all Node.js MCQs · Node.js interview questions
My notes#
Saved in this browser.