Node.js · Events & Data

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.

Intermediate 5 min Lesson 10 of 37Examples target Node.js 22 LTS.
Before you start:Node.js Architecture
0/37 in Node.js
On this page · 5 min · Intermediate

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#

JavaScript Illustrative
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#

JavaScriptevents.js Illustrative
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
});
Expected output
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#

  1. The server is created without a handler; the "request" listener is added with on.
  2. "listening" fires when listen succeeds; once removes the listener after the first call.
  3. fetch triggers a "request" event, and the listener writes the response.
  4. shutdown closes the server; when it is closed, the "close" event fires.
  5. 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#

No "error" listener

An emitted "error" with no listener crashes the process. Attach error handlers to streams, sockets and emitters.

Adding listeners repeatedly

Registering a listener inside a request handler adds one per request and leaks memory (MaxListenersExceededWarning).

Exiting immediately on SIGTERM

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?
process.on("exit", ...) and process.on("SIGINT", () => { ...; process.exit(0); }).
Show solution
JavaScript Illustrative
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#

Events quizMultiple choice Question 1 of 3

Which method registers a listener that runs only the first time?

Score so far: 0/0

Want more? Practise all Node.js MCQs · Node.js interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.