axiontrix-event
v1.0.1
Published
An advanced, highly resilient Event Emitter built on top of the native Node.js events module. It is designed to facilitate seamless, secure, and reliable event communication between two or more distinct Node.js applications across a network.
Maintainers
Readme
Axiontrix/Event
Advanced Node.js Event Emitter for Distributed Applications
An advanced, highly resilient Event Emitter built on top of the native Node.js events module. It is designed to facilitate seamless, secure, and reliable event communication between two or more distinct Node.js applications across a network.
💡 The Evolution: From Practice to Production
The project began as a personal challenge to completely rebuild the built-in Node.js events module from scratch. It was an engaging and successful exercise, but I unfortunately lost the initial codebase—a heartbreaking experience that involved losing other projects as well.
This setback, however, led to an idea: What if an Event Emitter could be used for inter-application communication?
While I was aware of established patterns like polling and webhooks, I recognized their drawbacks for simple, real-time eventing:
Webhooks require time-consuming setup and maintenance.
Polling can quickly drain server resources and consume excessive bandwidth, making it an unreliable choice for simple event coordination.
This realization prompted the second iteration: I rebuilt the core Event Emitter and significantly expanded its capabilities by integrating TCP networking (using the Node.js net module) and TLS/SSL for secure, encrypted connections. The result is a simple, lightweight, and robust solution for application-to-application event communication.
| Feature | Description | | :---- | ----: | | SSL/TLS Support | Ensures all inter-application communication is encrypted for enhanced security.| | Simplicity | Offers an API familiar to anyone who has used the native Node.js EventEmitter.| | Scalability | Designed to handle connections from multiple applications, acting as a central event daemon.| |IP Whitelisting | Built-in authentication mechanism for whitelisting authorized client IP addresses.|
🛡️ Security & Experience Gained: The DevSecOps Mindset
This project was a major lesson in building production-ready, resilient applications. As a DevSecOps professional, I actively attempted to break the application to ensure its stability.
Resiliency Testing with nmap
During initial testing, I used nmap to scan the local network. Although nmap localhost -p 5000 verified the daemon's port was open, the probing packets caused the application to crash.
The Cause: The issue stemmed from inadequate error handling within the core
netmodule interaction. My initial approach was to simply re-throw the errors, which is fatal for a long-running process like a server daemon.The Fix: I implemented a robust error handling layer. After the fix, the application could gracefully handle the network probing without crashing, demonstrating much-needed resilience against low-level network disruption.
Handling Malformed Data with curl
To further test the application's stability, I simulated a non-standard client request using curl:
curl http://localhost:5000This HTTP request against a TCP-based JSON service caused another crash.
- The Cause: The application was hard-coded to expect only valid JSON data. An unexpected HTTP request led to a parsing error because I failed to wrap the
JSON.parse()operation in a safeguard. - The Fix: I implemented a
try...catchblock around the data parsing logic, ensuring the daemon could safely discard malformed or unexpected data without service interruption.
Key Lessons Learned
Error Handling: For production services, avoid re-throwing fatal errors that break the event loop. Instead, use
console.error()to log the error tostderrand keep the application running, maximizing uptime.Code Organization &
thisContext: Managing thethiscontext when using code splitting with JavaScript classes was challenging. I found methods like.call()and.bind()to be invaluable for maintaining a clean, modular, and organized codebase.The DRY Principle: Successfully battling the DRY (Don't Repeat Yourself) principle was a major win. Implementing well-structured helper functions proved to be the most effective method for abstracting recurring logic and preventing code duplication.
📱 Development Environment
This entire project was developed on an Android phone using Termux
This non-traditional environment was a deliberate personal challenge to demonstrate my resourcefulness, commitment to skill development, and ability to build complex, low-level networking applications regardless of hardware constraints.
📄 axiontrix-event Documentation
This package provides a simple, resilient TCP-based event emitter for inter-process communication (IPC) or microservices communication in Node.js. It features automatic packet queuing to ensure events are delivered even if emitted before the daemon is connected.
Prerequisites
You need Node.js LTS installed. Since you're using Termux, the installation command might be:
# Termux installation (or use 'sudo apt install nodejs-lts' on Debian/Ubuntu)
pkg install nodejs-ltsGetting Started/Installation
1. Create and Install
First, create your project directory, navigate into it, and install the package from the NPM repository (owned by oluwagifted):
mkdir my-app
cd my-app
npm install axiontrix-event2. Setup Files
Create your client and daemon setup files:
touch client.js server.js3. Get Test Assets (Optional)
If you plan to test the TLS/SSL integration, run the included script to copy the non-production test certificates to your project root:
cp ./node_modules/axiontrix-event/test/private-key.pem . & cp ./node_modules/axiontrix-event/test/public-cert.pem .Note: This runs the grab.sh script included in the package. These files are for testing only and must never be used in production.
Usage
Basic Setup (Resilient TCP Communication)
This setup uses default configurations (host: 127.0.0.1, port: 5000).
1. Daemon (server.js)
// import the EventEmitter class from daemon
const EventEmitter = require("axiontrix-event/daemon");
// Construct the EventEmitter class with default config
const myEvent = new EventEmitter();
// Listen for the event named "my-event"
myEvent.on("my-event", (data) => {
console.log("[DAEMON] Event received from client data: %s", data);
});
// Implement basic error handling
myEvent.error = (e) => {
console.error("[DAEMON] Error:", e.message);
}
// Start the daemon and log the port it is listening on
myEvent.listen((port) => {
console.log("[DAEMON] Daemon running on port %s", port);
});2. Client (client.js)
This demonstrates the core resilience feature: emitting events before the connection is established. The package automatically queues the events and flushes them once connected.
// import the EventEmitter class from client
const EventEmitter = require("axiontrix-event/client");
// Construct the EventEmitter class with default config
const myEvent = new EventEmitter();
// --- RESILIENCE FEATURE: Emit NOW! ---
// These events are QUEUED internally and sent as soon as the connection succeeds.
myEvent.emit("my-event", "hello world (queued)");
// Start the connection process
myEvent.connect();
// Handle successful connection (optional, but good practice)
myEvent.on("connect", () => {
console.log("[CLIENT] Connection established. Queued events flushed!");
// Emit a live event after connection
myEvent.emit("my-event", "hello world (live)");
});
// Implement basic error handling
myEvent.error = (e) => {
console.error("[CLIENT] Error:", e.message);
}Running the Example
In one terminal, start the daemon:
node server.jsIn a separate terminal, start the client:
node client.jsAdvanced Setup (Custom Host/Port & Security)
This setup demonstrates a custom port and critical security features like IP whitelisting and TLS/SSL.
Advanced Configuration Options
| Options | Type | Default | Description |
| :------ | :--- | :------ | :---------- |
| host | String | "127.0.0.1" | Daemon bind address (0.0.0.0 for all interfaces) or Client target address.|
| port | Number | 5000 | The TCP port to use.|
| ipaddress | Array<String> | ["127.0.0.1"] | Daemon Only: A list of client IP addresses allowed to connect (IP Whitelisting). |
| key | String | undefined | Client/Daemon: Path to the private key file (e.g., path/to/key.pem) for TLS/SSL. If present on the daemon, TLS is enabled. |
| cert | String | undefined | Client/Daemon: Path to the public certificate file (e.g., path/to/cert.pem) for TLS/SSL. |
Daemon (server.js) with Whitelisting and TLS
// import the EventEmitter class from daemon
const EventEmitter = require("axiontrix-event/daemon");
// Config object with security features
const config = {
host: "0.0.0.0", // Listen on all network interfaces
port: 5002,
ipaddress: ["127.0.0.1", "192.168.1.10"], // Critical: Only allow these clients
key: "private-key.pem", // Enable TLS
cert: "public-cert.pem"
}
const myEvent = new EventEmitter(config);
myEvent.on("my-event", (data) => {
console.log("[DAEMON] Secure event received: %s", data);
});
myEvent.error = (e) => {
console.error("[DAEMON] Error:", e.message);
}
myEvent.listen((port) => {
console.log("[DAEMON] Running securely on port %s", port);
});Client (client.js) with TLS
The client must also provide the certificate paths to negotiate the secure connection.
// import the EventEmitter class from client
const EventEmitter = require("axiontrix-event/client");
const config = {
host: "127.0.0.1",
port: 5002,
key: "private-key.pem",
cert: "public-cert.pem"
}
const myEvent = new EventEmitter(config);
// Emit event immediately (will be queued until secure connection is complete)
myEvent.emit("my-event", "Secure event payload (before)");
myEvent.connect();
// Emit after Secure connection is established
myEvent.emit("my-event", "Secure event payload (after)");
myEvent.error = (e) => {
console.error("[CLIENT] Error:", e.message);
}💡 Package Import Behavior (The "Native" Emitter)
For maximum compatibility and ease of use in scenarios that do not require networking, importing the base axiontrix-event package will give you a standard, non-networked event emitter.
If you import the package without specifying either /client or /daemon in your require() statement, it will automatically import an EventEmitter class that is functionally similar to the native Node.js events module.
This allows you to use axiontrix-event as a drop-in replacement for native event handling within a single process.
Example: Using axiontrix-event as a Native Emitter
// This imports the standard EventEmitter class (no network features)
const EventEmitter = require("axiontrix-event");
const myEmitter = new EventEmitter();
myEmitter.on('test-local', (message) => {
console.log(`[LOCAL EMITTER] Received: ${message}`);
});
myEmitter.emit('test-local', 'This event stays within the process.');
// Output: [LOCAL EMITTER] Received: This event stays within the process.Summary of Imports
| Import Statement| Class Behavior | Primary Use Case |
| :-------------- | :------------- | :--------- |
| require("axiontrix-event/client") | Resilient Network Client | Sending events over TCP (with queuing)|
| require("axiontrix-event/daemon") | Resilient Network Daemon | Listening for events over TCP (with whitelisting/TLS) |
| require("axiontrix-event") | Native EventEmitter | Standard in-process, non-networked event handling |
