Production-Grade Microservices Framework for Node.js
Sub-millisecond Latency • Zero Configuration • Battle-Tested
Zeronode is a lightweight, high-performance framework for building distributed systems in Node.js. Each Node can simultaneously act as both a server (binding to an address) and a client (connecting to multiple remote nodes), forming a flexible peer-to-peer mesh network.
Traditional Client-Server Zeronode Mesh Network
------------------------- ------------------------
+--------+ +---------+
|Client 1|---+ +--| Node A |--+
+--------+ | | +---------+ |
| | <-> |
+--------+ | +------+ | +---------+ |
|Client 2|---+--->|Server| +--| Node B |--+
+--------+ | +------+ | +---------+ |
| | <-> |
+--------+ | | +---------+ |
|Client 3|---+ +--| Node C |--+
+--------+ +---------+
One-way only Each node is both
client AND server!
Unlike traditional client-server architectures, Zeronode provides:
- N:M Connectivity: One Node can bind as a server while connecting to N other nodes as a client
- Automatic Health Management: Built-in ping from clients to server and server's heartbeat check protocol keeps track of live connections and failures.
- Intelligent Reconnection: Automatic recovery from network failures with exponential backoff
- Sub-millisecond Latency: Average 0.3ms request-response times for low-latency applications
- Smart Routing: Route messages by ID, and by filters or predicate functions based on each node's options, automatic smart load balancing and "publish to all" is built in
- Zero Configuration: No brokers, no registries, no complex setup—just bind and connect
Perfect for: High-frequency trading systems, AI model inference clusters, multi-agent AI systems, real-time analytics, microservices and more.
npm install zeronode
Zeronode automatically installs required dependencies for supported platforms.
// A Node can: // - bind to an address (accept downstream connections) // - connect to many other nodes (act as a client) // - do both simultaneously import Node from 'zeronode' // Create a Node and bind const server = new Node({ // Node id id: 'api-server', // Node metadata — arbitrary data used for smart routing options: { role: 'api', version: 1 } }) // Bind to an address await server.bind('tcp://127.0.0.1:8000') // Register a request handler server.onRequest('user:get', (envelope, reply) => { // The envelope wraps the underlying message buffer const { userId } = envelope.data // Simulate server returning user info const userInfo = { id: userId, name: 'John Doe', email: '[email protected]' } // Return response back to the caller return userInfo // or: reply(userInfo) }) console.log('Server ready at tcp://127.0.0.1:8000')
// Create a new Node const client = new Node({ id: 'web-client' }) // Connect to the first Node await client.connect({ address: 'tcp://127.0.0.1:8000' }) // Now we can make a request from client to server const requestObject = { to: 'api-server', // Target node ID event: 'user:get', // Event name data: { userId: 123 }, // Request payload timeout: 5000 // Optional timeout in ms } // Read user data by id from server const user = await client.request(requestObject) console.log(user) // Output: { id: 123, name: 'John Doe', email: 'john@example.com' }
What does client.connect() do?
- Establishes a transport connection to the server address
- Performs a handshake to exchange identities and options
- Starts periodic client→server pings and server-side heartbeat tracking
- Manages automatic reconnection with exponential backoff
Use when you need a response from the target service.
+------------+ +------------+
| Client | | Server |
+------+-----+ +------+-----+
| |
| request('calculate:sum', [1,2,3,4,5]) |
+----------------------------------------------------->|
| |
| Processing... |
| sum = 15 |
| |
|<-----------------------------------------------------+
| reply({ result: 15 }) |
| |
[~0.3ms latency]
// Server: Register a handler server.onRequest('calculate:sum', ({ data }, reply) => { const { numbers } = data // Perform calculation const sum = numbers.reduce((a, b) => a + b, 0) // Return result (or call reply({ result: sum })) return { result: sum } }) // Client: Make a request const response = await client.request({ to: 'calc-server', event: 'calculate:sum', data: { numbers: [1, 2, 3, 4, 5] } }) console.log(response.result) // 15
Use when you don't need a response (logging, notifications, analytics).
+------------+ +------------+
| Client | | Server |
+------+-----+ +------+-----+
| |
| tick('log:info', { message: 'User login' }) |
+----------------------------------------------------->|
| |
| <- Returns immediately (non-blocking) |
| |
| Process async
| +-> Log to DB
| +-> Send to monitoring
// Server: Register a tick handler server.onTick('log:info', ({data}) => { // envelope.data contains the log data const { message, metadata } = data // Process asynchronously (no response expected) console.log(`[INFO] ${message}`, metadata) logToDatabase(message, metadata) }) // Client: Send a tick (non-blocking, returns immediately) client.tick({ to: 'log-server', event: 'log:info', data: { message: 'User logged in', metadata: { userId: 123, timestamp: Date.now() } } })
Send to multiple nodes simultaneously.
+-------------+
| Scheduler |
+------+------+
|
tickAll('config:reload', { version: '2.0' })
|
+---------------------+---------------------+
| | |
v v v
+----------+ +----------+ +----------+
| Worker 1 | | Worker 2 | | Worker 3 |
|role:worker |role:worker |role:worker
|status:ready |status:ready |status:ready
+----------+ +----------+ +----------+
| | |
+-------> All receive config update <-------+
// Send to ALL nodes matching a filter await node.tickAll({ event: 'config:reload', data: { version: '2.0', config: newConfig }, filter: { role: 'worker', // Only workers status: 'ready' // That are ready } })
+---------+
| Gateway | request({ to: 'user-service-1' })
+----+----+
|
| Direct route by ID
|
v
+--------------+
|user-service-1| <- Exact match
+--------------+
+--------------+
|user-service-2| <- Not selected
+--------------+
// Route to a specific node by ID const response = await node.request({ to: 'user-service-1', // Exact node ID event: 'user:get', data: { userId: 123 } })
+---------+
| Gateway | requestAny({ filter: { role: 'worker', status: 'idle' } })
+----+----+
|
| Smart routing picks ONE matching node
| (automatic load balancing)
|
+--------------+--------------+
v v v
+---------+ +---------+ +---------+
|Worker 1 | |Worker 2 | |Worker 3 |
|idle (Y) | |busy (N) | |idle (Y) |
+---------+ +---------+ +---------+
^ |
| |
+---- One is selected ---------+
(round-robin)
// Route to ANY node matching the filter (automatic load balancing) const response = await node.requestAny({ event: 'job:process', data: { jobId: 456 }, filter: { role: 'worker', // Must be a worker status: 'idle', // Must be idle region: 'us-west', // In the correct region capacity: { $gte: 50 } // With sufficient capacity } })
Automatic service discovery through routers - nodes find each other without direct connections!
Payment Service Router Auth Service
| | |
| No direct connection between them |
| | |
| requestAny() | |
| filter: auth | |
+------------------>| |
| | |
| | Discovers Auth |
| | Forwards request |
| +------------------->|
| | |
| | Response |
| |<-------------------+
| | |
| Response | |
|<------------------+ |
| | |
Basic Router Setup:
import { Router } from 'zeronode' // 1. Create a Router (special Node with router: true) const router = new Router({ id: 'router-1', bind: 'tcp://127.0.0.1:3000' }) await router.bind() // 2. Services connect to router (not to each other!) const authService = new Node({ id: 'auth-service', options: { service: 'auth', version: '1.0' } }) await authService.bind('tcp://127.0.0.1:3001') await authService.connect({ address: router.getAddress() }) const paymentService = new Node({ id: 'payment-service', options: { service: 'payment' } }) await paymentService.bind('tcp://127.0.0.1:3002') await paymentService.connect({ address: router.getAddress() }) // 3. Services discover each other automatically via router! const result = await paymentService.requestAny({ filter: { service: 'auth' }, event: 'verify', data: { token: 'abc-123' } }) // ✅ Router automatically finds auth service and forwards request!
Or use the CLI:
# Start a router from command line npx zeronode --router --bind tcp://0.0.0.0:8087 # With statistics npx zeronode --router --bind tcp://0.0.0.0:8087 --stats 5000
See docs/CLI.md for complete CLI reference.
How Router Discovery Works:
- Local First - Node checks direct connections
- Router Fallback - If not found locally, forwards to router(s)
- Router Discovery - Router finds service in its network
- Response Routing - Response flows back automatically
Router Features:
// Monitor routing activity const stats = router.getRoutingStats() console.log(stats) // { // proxyRequests: 150, // proxyTicks: 30, // successfulRoutes: 178, // failedRoutes: 2, // uptime: 3600, // requestsPerSecond: 0.05 // } // Reset statistics router.resetRoutingStats()
Multi-Hop Routing (Router Cascading):
Routers can forward to other routers for distributed service discovery!
Client → Router1 → Router2 → Service
(no match) (found!)
// Create multiple routers const router1 = new Router({ bind: 'tcp://127.0.0.1:3000' }) const router2 = new Router({ bind: 'tcp://127.0.0.1:3001' }) // Chain routers together await router1.connect({ address: router2.getAddress() }) // Client → Router1 → Router2 → Service (automatic!)
Use Cases:
- ✅ Microservices - Dynamic service discovery without hardcoded IPs
- ✅ Multi-Region - Routers in different regions find services across network
- ✅ Load Balancing - Multiple service instances discovered automatically
- ✅ Failover - Services can restart/relocate, router finds them
- ✅ Zero Config - No service registries, no DNS, just connect to router
Router Example: See examples/router-example.js for complete working code.
Performance: Router adds ~0.5ms overhead (1.0ms vs 0.5ms direct). See docs/BENCHMARKS.md for details.
Zeronode supports pattern-based handlers using strings or RegExp. With RegExp you can register
one handler for a family of events that share a common prefix. The incoming event name is available
as envelope.event, so you can branch on the action and keep code DRY and fast.
// Handle multiple events with a single handler using RegExp server.onRequest(/^api:user:/, ({data, tag }, reply) => { // Matches: 'api:user:get', 'api:user:create', 'api:user:update', etc. const action = tag.split(':')[2] // 'get', 'create', 'update' switch (action) { case 'get': return getUserData(data) case 'create': return createUser(data) // ... } })
Use metadata (Node options) for service discovery and routing.
Metadata for Smart Routing
===========================
+------------------------------+
| Worker Node |
+------------------------------+
| id: 'worker-12345' |
| |
| options: { |
| role: 'worker' | <--- Route by role
| region: 'us-east-1' | <--- Geographic routing
| version: '2.1.0' | <--- Version matching
| capacity: 100 | <--- Load-based routing
| features: ['ml', 'image'] | <--- Capability routing
| status: 'ready' | <--- State-based routing
| } |
+------------------------------+
// Worker node with metadata const worker = new Node({ id: `worker-${process.pid}`, options: { role: 'worker', region: 'us-east-1', version: '2.1.0', capacity: 100, features: ['ml', 'image-processing'], status: 'ready' } }) // workShedulerNode routes based on metadata const response = await workShedulerNode.requestAny({ event: 'process:image', data: imageData, filter: { role: 'worker', features: { $contains: 'image-processing' }, capacity: { $gte: 50 }, status: 'ready' } }) // Update options dynamically await worker.setOptions({ status: 'busy' }) // Process work... await worker.setOptions({ status: 'ready' })
Advanced Filtering Operators:
filter: { // Exact match role: 'worker', // Comparison capacity: { $gte: 50, $lte: 100 }, priority: { $in: [1, 2, 3] }, // String matching region: { $regex: /^us-/ }, name: { $contains: 'prod' }, // Array matching features: { $containsAny: ['ml', 'gpu'] }, excluded: { $containsNone: ['deprecated'] } }
Zeronode provides Express.js-style middleware chains for composing request handling logic with automatic handler chaining.
Middleware Chain Flow
=====================
Request arrives
|
v
+---------------------+
| Logging Middleware | <- 2-param: auto-continue
| (2 parameters) |
+----------+----------+
| next() automatically called
v
+---------------------+
| Auth Middleware | <- 3-param: manual control
| (3 parameters) |
+----------+----------+
| next() manually called
v
+---------------------+
| Business Handler | <- Final handler
| Returns data |
+----------+----------+
|
v
Response
+========================+
| If error occurs: |
| -> Error Handler |
| (4 parameters) |
+========================+
// 2-parameter: Auto-continue (logging, metrics) server.onRequest(/^api:/, (envelope, reply) => { console.log(`Request: ${envelope.event}`) // Auto-continues to next handler }) // 3-parameter: Manual control (auth, validation) server.onRequest(/^api:/, (envelope, reply, next) => { if (!envelope.data.token) { return reply.error('Unauthorized') } envelope.user = verifyToken(envelope.data.token) next() // Explicitly continue }) // 4-parameter: Error handler server.onRequest(/^api:/, (error, envelope, reply, next) => { reply.error({ code: 'API_ERROR', message: error.message }) }) // Business logic server.onRequest('api:user:get', async (envelope, reply) => { return await database.users.findOne({ id: envelope.data.userId }) })
See docs/MIDDLEWARE.md for complete middleware patterns, error handling, and best practices.
Zeronode provides comprehensive production-ready examples for common distributed system patterns:
Common Architecture Patterns
============================
API Gateway Pattern Distributed Logging
------------------- -------------------
+---------+ +--------+
| Gateway | |Services|
+----+----+ +---+----+
| |
+------+------+ |
v v v v
+----+ +----+ +----+ +----------+
|API1| |API2| |API3| |Log Server|
+----+ +----+ +----+ +-----+----+
|
Task Queue +-----+-----+
---------- v v
[Store] [Monitor]
+-------+
|Queuer |
+---+---+
|
+------+------+ Microservices Mesh
v v v ------------------
+-----++-----++-----+
|Wrkr1||Wrkr2||Wrkr3| +----+ +----+
+-----++-----++-----+ |Auth|<->|User|
+-+--+ +--+-+
| |
+----+----+
|
+---+---+
|Payment|
+-------+
- API Gateway - Load-balanced workers with automatic routing
- Distributed Logging - Centralized log aggregation system
- Task Queue - Priority-based task distribution
- Microservices - Service discovery and inter-service communication
- Analytics Pipeline - Real-time data processing
- Distributed Cache - Multi-node caching system
See docs/EXAMPLES.md for complete working code and usage instructions.
Monitor node connections, disconnections, and state changes:
import { NodeEvent } from 'zeronode' // Peer joined the network node.on(NodeEvent.PEER_JOINED, ({ peerId, peerOptions, direction }) => { console.log(`Peer joined: ${peerId} (${direction})`) // direction: 'upstream' or 'downstream' }) // Peer left the network node.on(NodeEvent.PEER_LEFT, ({ peerId, direction }) => { console.log(`Peer left: ${peerId}`) }) // Handle errors node.on(NodeEvent.ERROR, ({ code, message }) => { console.error(`Error [${code}]: ${message}`) })
See docs/EVENTS.md for complete event reference including ClientEvent, ServerEvent, and error handling patterns.
- Quick Start Guide - Get up and running in minutes
- Core Concepts - Understanding Zeronode fundamentals
- Middleware System - Express-style middleware chains
- Smart Routing - Service discovery and load balancing
- Events Reference - All events and lifecycle hooks
- Real-World Examples - Production-ready example code
- Architecture Guide - Deep dive into internals
- Envelope Format - Binary message format specification
- Benchmarks - Performance testing and analysis
- Testing Guide - Testing distributed systems
- Configuration - All configuration options
Zeronode delivers sub-millisecond latency with high throughput:
- Latency: ~0.3ms average request-response time
- Efficiency: Zero-copy buffer passing, lazy parsing
# Run benchmarks
npm run benchmarkSee docs/BENCHMARKS.md for detailed benchmark methodology and results.
- 🐛 Issue Tracker - Bug reports and feature requests
- 🔧 Examples - Code examples
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
git clone https://github.com/sfast/zeronode.git cd zeronode npm install npm test
MIT