Beyond the event loop itself, Node.js has an explicit "events" pattern used throughout its core APIs, streams, servers, and processes all emit named events you can listen for. This lesson introduces that pattern before the EventEmitter lesson goes deeper.
What Are Node.js Events?
Many Node.js objects, HTTP servers, streams, child processes, are "event emitters": objects that can announce ("emit") named events at various points in their lifecycle, and let any number of listeners react to those events without tight coupling between the emitter and its listeners.
This pattern shows up constantly: an HTTP server emits 'request' and 'close', a readable stream emits 'data' and 'end', and process itself emits 'exit' and 'SIGINT'. Understanding events is essential to reading real Node.js code.
import http from 'node:http';
const server = http.createServer((req, res) => res.end('ok'));
server.on('listening', () => {
console.log('Server is now listening for connections');
});
server.on('close', () => {
console.log('Server has stopped');
});
server.listen(3000);
server is an EventEmitter under the hood, .on(eventName, listener) registers a function to run whenever that named event fires.
The Emitter Pattern
emitter.on('eventName', (payload) => { ... }); // register a listener
emitter.emit('eventName', payload); // fire the event
emitter.once('eventName', listener); // listen only once
emitter.off('eventName', listener); // remove a listener
.on() registers a listener that runs every time the event fires.
.once() registers a listener that automatically removes itself after firing one time.
.emit() synchronously calls every listener registered for that event, in registration order.
Multiple listeners can subscribe to the same event independently.
Node.js Events Cheatsheet
Where you'll encounter the events pattern throughout Node's core APIs.
Object
Common Events
http.Server
'request', 'listening', 'close', 'error'
Readable stream
'data', 'end', 'error', 'close'
Writable stream
'drain', 'finish', 'error'
process
'exit', 'SIGINT', 'uncaughtException'
Child process
'exit', 'error', 'message'
Custom classes
Extend EventEmitter to emit your own events
Why Events Instead of a Single Callback?
A single callback only supports one "this is done" notification. Real objects like servers and streams have many distinct moments worth reacting to (started listening, received data, hit an error, closed), and any number of independent listeners might care about each one. The event pattern supports all of that naturally, where a single callback parameter could not.
The 'error' Event Is Special
If an EventEmitter emits an 'error' event and there is no listener registered for it, Node.js throws the error and crashes the process. This is intentional, errors must not be silently ignored, but it means you should always attach an 'error' listener to anything that might emit one.
Always attach an 'error' listener to emitters that might emit one, missing it crashes the process.
The events pattern is foundational to streams, servers, and process lifecycle handling in Node.js.
Pro Tip
When exploring an unfamiliar Node.js library, search its source or docs for .emit( calls, it's often the fastest way to discover every meaningful lifecycle moment the library exposes, beyond what's documented in prose.
You now understand the events pattern conceptually. Next, learn the EventEmitter class itself and how to build your own event-driven objects.