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 zeronodeZeronode 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: 'john@example.com' }
// 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) // 15Use 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
}
})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 testMIT
