npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

zeronode

v2.0.10

Published

Minimal building block for NodeJS microservices

Readme

Zeronode


What is Zeronode?

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 vs Zeronode Architecture

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.


Installation

npm install zeronode

Zeronode automatically installs required dependencies for supported platforms.

Basic Example


// 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: '[email protected]' }

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

Core Concepts

Messaging Patterns

1. Request/Reply (RPC-Style)

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

2. Tick (Fire-and-Forget)

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() }
  }
})

3. Broadcasting

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
  }
})

Smart Routing

Direct Routing (by ID)

+---------+
| 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 }
})

Filter-Based Routing / Load balancing

+---------+
| 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
  }
})

Router-Based Discovery (Service Mesh)

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:

  1. Local First - Node checks direct connections
  2. Router Fallback - If not found locally, forwards to router(s)
  3. Router Discovery - Router finds service in its network
  4. 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.

Pattern Matching

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)
    // ...
  }
})

Node Options and Metadata

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'] }
}

Middleware System

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.


Real-World Examples

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.


Lifecycle Events

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.


Documentation

Getting Started

Feature Guides

Advanced Topics


Performance

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 benchmark

See docs/BENCHMARKS.md for detailed benchmark methodology and results.


Community & Support


Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

git clone https://github.com/sfast/zeronode.git
cd zeronode
npm install
npm test

License

MIT