Extend your events across processes, threads, and machines. Demitter brings the familiar Node.js event API (via emittery) API to distributed systems, enabling real-time event communication between multiple Node.js processes with near-zero configuration.
Live auction system with multiple bidders across separate terminal processes - all synchronized in real-time
- π Distributed Events: Emit events across processes, threads, or machines
- β‘ Zero Configuration: Works out of the box with sensible defaults
- π― Familiar API: Extends the battle-tested sindresorhus/emittery library
- π‘οΈ Type Safe: Full TypeScript support
- π¦ Lightweight: Efficient binary serialization with minimal overhead
- π§ Flexible: Embedded or standalone message proxy/forwarder deployment options
npm install demitter
# or
pnpm install demitter
# or
yarn add demitterGet distributed events running in 3 steps:
import { createForwarder } from "demitter";
const forwarder = await createForwarder();
console.log("Forwarder ready - events can now flow between processes!");import { createDistributedEmitter } from "demitter";
// In Process A
const emitterA = await createDistributedEmitter();
// In Process B
const emitterB = await createDistributedEmitter();// Process A: Listen for events
emitterA.on("user:login", (data) => {
console.log("User logged in:", data);
});
// Process B: Emit events that Process A will receive
emitterB.emit("user:login", { userId: 123, timestamp: Date.now() });That's it! Events emitted in one process are automatically received by all other connected processes.
Demitter extends the powerful emittery API with distributed capabilities. All emittery methods work exactly the same:
// All the familiar emittery methods work across processes:
await emitter.emit(eventName, data);
emitter.on(eventName, listener);
emitter.off(eventName, listener);
await emitter.once(eventName);
emitter.onAny(listener);
emitter.clearListeners();
// ... and many moreπ Full API Documentation: See the complete emittery API docs for all available methods, TypeScript usage, and advanced features.
Creates a new distributed emitter instance.
const emitter = await createDistributedEmitter({
xsubAddress: "tcp://localhost:5555", // Connect to forwarder
xpubAddress: "tcp://localhost:5556", // Connect to forwarder
});Creates and starts a message forwarder to enable event distribution.
const forwarder = await createForwarder({
xsubPort: 5555, // Default port for subscribers
xpubPort: 5556, // Default port for publishers
});Closes the distributed emitter and cleans up connections.
await emitter.close();distributed:error: Emitted when distributed operations fail (network issues, serialization errors, etc.)
emitter.on("distributed:error", (error) => {
console.error("Distributed operation failed:", error);
});- Microservices Communication: Event-driven architecture between services
- Multi-Process Applications: Coordinate events across worker processes
- Real-Time Systems: Live updates, notifications, and state synchronization
- Distributed Logging: Centralized event collection from multiple sources
- Game Development: Real-time multiplayer game state management
- IoT Networks: Device communication and sensor data distribution
Explore practical examples in the examples/ directory:
basic-usage.ts- Simple distributed events setuperror-handling.ts- Robust error handling patternslive_auction/- Complete live auction system demo- Multi-process auction with bidders, controller, and spectator dashboard
- Real-time terminal UIs with charts and analytics
- Intelligent bidding strategies and conflict resolution
# Basic usage
pnpm run example:basic
# Error handling
pnpm run example:errors
# Live auction demo (see examples/live_auction/auction-demo/README.md)
cd examples/live_auction/auction-demo
pnpm install
pnpm run demoStart the forwarder within your application:
import { createForwarder, createDistributedEmitter } from "demitter";
const forwarder = await createForwarder();
const emitter = await createDistributedEmitter();Run the forwarder as a separate service:
# Using the CLI
npx demitter-forwarder
# Or start with custom ports
XSUB_PORT=7777 XPUB_PORT=8888 npx demitter-forwarderFROM node:22-alpine
RUN npm install -g demitter
EXPOSE 5555 5556
CMD ["demitter-forwarder"]The system consists of two main components:
- Pub/Sub Forwarder - A standalone message broker that forwards events between distributed processes
- DistributedEmitter - An extension of
Emitteryclass in the emittery library for distributed event emission
The forwarder acts as a central message broker using ZeroMQ's XSUB-XPUB proxy pattern.
After installing the package globally:
npm install -g demitter
# or
pnpm install -g demitterYou can run the forwarder from anywhere:
# Start with default ports (XSUB: 5555, XPUB: 5556)
demitter-forwarder
# With custom ports
XSUB_PORT=6000 XPUB_PORT=6001 demitter-forwarder
# With debug logging
LOG_LEVEL=debug demitter-forwarder
# Show help
demitter-forwarder --helpFor projects with demitter as a dependency:
npx demitter-forwarder# Start with default ports (XSUB: 5555, XPUB: 5556)
node src/forwarder.js
# Or using the CLI script
node src/cli.js
# With custom ports
XSUB_PORT=6000 XPUB_PORT=6001 node src/forwarder.js
# With debug logging
LOG_LEVEL=debug node src/forwarder.jsimport { createForwarder } from "./src/forwarder.js";
// Start forwarder with default configuration
const forwarder = await createForwarder();
// Custom configuration
const forwarder = await createForwarder({
xsubPort: 6000,
xpubPort: 6001,
loggerOptions: { level: "debug" },
});
// Graceful shutdown
await forwarder.close();The forwarder can be configured using environment variables:
| Variable | Default | Description |
|---|---|---|
XSUB_PORT |
5555 | Port for XSUB socket (receives from publishers) |
XPUB_PORT |
5556 | Port for XPUB socket (sends to subscribers) |
XSUB_ADDRESS |
tcp://*:${XSUB_PORT} |
Full address for XSUB socket |
XPUB_ADDRESS |
tcp://*:${XPUB_PORT} |
Full address for XPUB socket |
LOG_LEVEL |
info | Logging level (debug, info, warn, error) |
demitter-forwarder --help
# or when running locally
node src/cli.js --helpSee LICENSE file.
I'm open to contributions but the details for contributions are not yet finalized. If you have ideas or improvements, feel free to open an issue or start a discussion before you raise a pull request.
