Node.js API compatibility - Events

The events API in Node.js is a core module that provides a way to work with event-driven programming. It allows developers to create and manage event emitters, which are objects that can emit named events and listen for those events through event listeners. This module is available in the Azion Runtime through Node.js compatibility, making it useful for decoupling logic in functions by emitting events and reacting to them with dedicated listeners.


Example: Basic event emission

The example below shows how to use the events module in a function:

/**
* An example of using the Node.js `events` module in Azion Functions.
* Support:
* - Partial support
* - Extended by library `events`
* @example
* // Execute with Azion Bundler:
* npx edge-functions build
* npx edge-functions dev
*/
import { EventEmitter } from "node:events";
/**
* Emit an event and listen to it.
* @param {*} event
* @returns {Response} Response
*/
const main = async (event) => {
const emitter = new EventEmitter();
emitter.on("hello-event", (...args) => {
console.log("an event occurred!", ...args);
});
emitter.emit("hello-event", 1, 2, 3);
return new Response("Event emitted", { status: 200 });
};
export default main;

Example: Request lifecycle events

Use EventEmitter to handle request processing stages:

import { EventEmitter } from "node:events";
const main = async (event) => {
const requestEmitter = new EventEmitter();
const results = [];
// Get request info with optional chaining for safety
const requestUrl = event.request?.url || "https://default.example.com";
const requestHeaders = event.request?.headers || {};
// Listen for request start
requestEmitter.on("start", (url) => {
console.log(`Processing request: ${url}`);
results.push(`Started: ${url}`);
});
// Listen for validation events
requestEmitter.on("validate", (data) => {
if (data.headers) {
console.log("Headers validated");
results.push("Headers validated");
}
});
// Listen for completion
requestEmitter.on("complete", (status) => {
console.log(`Request completed with status: ${status}`);
results.push(`Completed: ${status}`);
});
// Listen for errors
requestEmitter.on("error", (err) => {
console.error(`Error: ${err.message}`);
results.push(`Error: ${err.message}`);
});
// Emit events in sequence
requestEmitter.emit("start", requestUrl);
requestEmitter.emit("validate", { headers: requestHeaders });
requestEmitter.emit("complete", 200);
return new Response(JSON.stringify({ events: results }), {
headers: { "Content-Type": "application/json" }
});
};
export default main;

Example: One-time listeners and error handling

Use once() for single-execution listeners and handle errors properly:

import { EventEmitter } from "node:events";
const main = async (event) => {
const emitter = new EventEmitter();
const log = [];
// One-time listener - only executes once
emitter.once("init", () => {
console.log("Initialization complete");
log.push("Initialized");
});
// Multiple listeners for same event
emitter.on("data", (chunk) => {
console.log(`Processing chunk: ${chunk}`);
log.push(`Chunk: ${chunk}`);
});
emitter.on("data", (chunk) => {
console.log(`Logging chunk: ${chunk}`);
log.push(`Logged: ${chunk}`);
});
// Error handling - special 'error' event
emitter.on("error", (err) => {
console.error(`Error caught: ${err.message}`);
log.push(`Error: ${err.message}`);
});
// Emit events
emitter.emit("init");
emitter.emit("init"); // Won't trigger again - once() used
emitter.emit("data", "chunk-1");
emitter.emit("data", "chunk-2");
emitter.emit("error", new Error("Test error"));
return new Response(JSON.stringify({ log }), {
headers: { "Content-Type": "application/json" }
});
};
export default main;

Example: Async event handlers

Handle asynchronous operations in event listeners:

import { EventEmitter } from "node:events";
import { setTimeout } from "node:timers/promises";
const main = async (event) => {
const emitter = new EventEmitter();
const results = [];
// Note: EventEmitter does NOT await Promises returned by listeners.
// The emit() method returns immediately, without waiting for async operations.
// For async operations, it's recommended to use Promise-based patterns
// instead of relying on EventEmitter alone.
// Listen for fetch completion events
emitter.on("fetched", (result) => {
results.push(result);
console.log(`Fetched: ${result.url}`);
});
// Error handler
emitter.on("error", (err) => {
console.error(`Fetch error: ${err.message}`);
results.push({ error: err.message });
});
// Define async fetch function that emits events
const fetchData = async (url) => {
try {
const response = await fetch(url);
const data = await response.json();
const result = { url, status: response.status, data };
emitter.emit("fetched", result);
return result;
} catch (error) {
emitter.emit("error", error);
return { url, error: error.message };
}
};
// URLs to fetch
const urls = [
"https://jsonplaceholder.typicode.com/todos/1",
"https://jsonplaceholder.typicode.com/todos/2"
];
// Execute fetches in parallel and wait for completion
const fetchResults = await Promise.all(urls.map(fetchData));
return new Response(JSON.stringify({
results,
fetchResults
}), {
headers: { "Content-Type": "application/json" }
});
};
export default main;

Example: Custom EventEmitter class

Extend EventEmitter to create domain-specific event emitters:

import { EventEmitter } from "node:events";
// Custom class extending EventEmitter
class RequestHandler extends EventEmitter {
constructor() {
super();
this.requestCount = 0;
}
processRequest(request) {
this.requestCount++;
this.emit("request", { id: this.requestCount, url: request.url });
try {
// Simulate processing
const result = { processed: true, id: this.requestCount };
this.emit("success", result);
return result;
} catch (error) {
this.emit("error", error);
throw error;
}
}
}
const main = async (event) => {
const handler = new RequestHandler();
const logs = [];
// Attach listeners
handler.on("request", (data) => {
logs.push(`Request #${data.id}: ${data.url}`);
});
handler.on("success", (result) => {
logs.push(`Success: ${JSON.stringify(result)}`);
});
handler.on("error", (err) => {
logs.push(`Error: ${err.message}`);
});
// Process requests
handler.processRequest(event.request);
handler.processRequest({ url: "https://example.com/test" });
// Get listener counts
const listenerCount = handler.listenerCount("request");
logs.push(`Request listeners: ${listenerCount}`);
return new Response(JSON.stringify({ logs, totalRequests: handler.requestCount }), {
headers: { "Content-Type": "application/json" }
});
};
export default main;

Supported APIs

APIStatus
new EventEmitter()🟢 Supported
emitter.on()🟢 Supported
emitter.once()🟢 Supported
emitter.emit()🟢 Supported
emitter.off()🟢 Supported
emitter.removeListener()🟢 Supported
emitter.removeAllListeners()🟢 Supported
emitter.listenerCount()🟢 Supported
emitter.listeners()🟢 Supported
emitter.eventNames()🟢 Supported
emitter.prependListener()🟢 Supported
emitter.prependOnceListener()🟢 Supported
EventEmitter.listenerCount()🟢 Supported
EventEmitter.defaultMaxListeners🟢 Supported
captureRejections🟡 Partially supported