@rabbx/cluster
v1.0.0
Published
Edge-native, cross-platform clustering for modern JavaScript runtimes. Zero-downtime reloads. Backpressure. IPC. Structured logging. One API.
Downloads
141
Readme
@rabbx/cluster
Why @rabbx/cluster
Traditional clustering solutions are runtime-specific and lack modern features. @rabbx/cluster provides:
- True cross-platform - Same API across Node, Bun, Deno, Cloudflare Workers, and Vercel Edge
- Built-in observability - Health checks, Prometheus metrics, auto-restart
- Zero dependencies - 15KB gzipped, no supply chain risk
- Production hardened - Graceful shutdown, error recovery, hot reload
- Full WebSocket support - Integrated with
@rabbx/wsfor real-time apps - KV integration - Built-in in-memory key-value store
- IPC support - Cross-process communication via native or KV channels
Installation
npm install @rabbxdev/cluster
yarn add @rabbxdev/cluster
pnpm add @rabbxdev/cluster
bun add @rabbxdev/clusterQuick Start
import { createRabbxCluster } from '@rabbx/cluster';
const fetchHandler = (request) => {
return new Response(JSON.stringify({ message: 'Hello World!' }), {
headers: { 'Content-Type': 'application/json' }
});
};
const { handler, workers, reload, shutdown, prometheus } = await createRabbxCluster(fetchHandler, {
port: 8080,
workerNums: 4
});
console.log('Server running on http://localhost:8080');
console.log('Health: http://localhost:8080/health');
console.log('Metrics: http://localhost:8080/metrics');Platform Usage
Node.js
import { createRabbxCluster } from '@rabbx/cluster';
const fetchHandler = (request) => {
return new Response(JSON.stringify({ message: 'Hello from node!' }), {
headers: { 'Content-Type': 'application/json' }
});
};
const { handler, workers, reload, shutdown } = await createRabbxCluster(fetchHandler, {
host: 'localhost',
port: 8080,
workerNums: require('os').cpus().length,
onError: (error) => console.error('Worker error:', error),
onReady: () => console.log('Node cluster ready!')
});
// Handle graceful shutdown
process.on('SIGTERM', () => shutdown());
process.on('SIGINT', () => shutdown());
process.on('SIGUSR2', () => reload()); // Hot reloadBun
import { createRabbxCluster } from '@rabbx/cluster';
const fetchHandler = (request) => {
return new Response(JSON.stringify({ message: 'Hello from Bun!' }), {
headers: { 'Content-Type': 'application/json' }
});
};
const { handler, workers, reload, shutdown, kv } = await createRabbxCluster(fetchHandler, {
port: 3000,
workerNums: navigator.hardwareConcurrency || 4,
onError: (error) => console.error('Bun error:', error),
onReady: () => console.log('Bun cluster ready!')
});
// Bun-specific process management
process.on('SIGTERM', () => shutdown());
process.on('SIGUSR2', () => reload());Deno
import { createRabbxCluster } from 'https://cdn.skypack.dev/@rabbxdev/cluster';
const fetchHandler = (request) => {
return new Response(JSON.stringify({ message: 'Hello from Deno!' }), {
headers: { 'Content-Type': 'application/json' }
});
};
const { handler, reload, shutdown, kv } = await createRabbxCluster(fetchHandler, {
port: 8000,
onError: (error) => console.error('Deno error:', error)
});Cloudflare Workers
import { createRabbxCluster } from '@rabbx/cluster';
const fetchHandler = async (request, env, ctx) => {
return new Response(JSON.stringify({
message: 'Hello from Cloudflare Workers!',
region: env.CF_REGION
}), {
headers: { 'Content-Type': 'application/json' }
});
};
export default await createRabbxCluster(fetchHandler, {
onError: (error) => console.error('Worker error:', error)
}).then(result => ({
fetch: result.handler
}));Vercel Edge Functions
// pages/api/edge.ts or app/api/edge/route.ts
import { createRabbxCluster } from '@rabbx/cluster';
const fetchHandler = async (request, env, ctx) => {
return new Response(JSON.stringify({
message: 'Hello from Vercel Edge!',
geo: request.headers.get('x-vercel-ip-country')
}), {
headers: { 'Content-Type': 'application/json' }
});
};
export default async (request) => {
const { handler } = await createRabbxCluster(fetchHandler, {});
return handler(request, {}, {});
};WebSocket Integration
import { createRabbxCluster } from '@rabbx/cluster';
import { WebSocket } from '@rabbx/ws';
const fetchHandler = (request) => {
return new Response('HTTP endpoint');
};
const { handler, websocket, kv } = await createRabbxCluster(fetchHandler, {
port: 8080,
wsOptions: {
path: '/ws',
maxPayload: 60 * 1024 // 60KB
},
setupWs: (wsServer) => {
wsServer.addEventListener('connection', ({ detail: { socket } }) => {
console.log('New WebSocket connection');
socket.addEventListener('message', (event) => {
// Process message and potentially store in KV
kv.set(`msg:${Date.now()}`, event.data);
socket.send(`Echo: ${event.data}`);
});
socket.addEventListener('close', () => {
console.log('WebSocket disconnected');
});
});
}
});API Reference
createRabbxCluster(fetchHandler, options?)
Returns { handler, workers, isPrimary, server, reload, shutdown, prometheus, kv, websocket, ipc }
Options
| Option | Type | Description |
|--------|------|-------------|
| host | string | Hostname to bind to (default: 'localhost') |
| port | number | Port to listen on (default: 8080) |
| workerNums | number | Number of worker processes (default: CPU cores) |
| setupServer | (server) => void | Hook to configure server after creation |
| setupWs | (ws) => void | Hook to configure WebSocket server |
| wsOptions | object | WebSocket configuration |
| onError | (error) => void | Error handler |
| onReady | () => void | Called when server is ready |
| onWorkerExit | (code, signal) => void | Called when worker exits |
| gracefulTimeout | number | Shutdown timeout in ms (default: 5000) |
Returns
| Property | Type | Description |
|----------|------|-------------|
| handler | Function | Request handler function |
| workers | array | Worker instances (platform-specific) |
| isPrimary | boolean | Whether current process is primary |
| server | Server | Server instance (null in primary Node) |
| reload | Function | Reload all workers |
| shutdown | Function | Graceful shutdown |
| prometheus | Function | Get Prometheus metrics string |
| kv | KV | Key-value store instance |
| websocket | WebSocketServer | WebSocket server instance |
| ipc | IPCBus | Inter-process communication bus |
Built-in Endpoints
GET /health- Health check returning{ status: 'ok', timestamp: '...' }GET /metrics- Prometheus-formatted metrics
Production Features
Graceful Shutdown
process.on('SIGTERM', async () => {
console.log('Gracefully shutting down...');
await shutdown();
process.exit(0);
});Hot Reloading
// Unix/Linux: kill -USR2 <pid>
// Programmatic: await reload();
process.on('SIGUSR2', async () => {
console.log('Hot reloading...');
await reload();
});Error Recovery
Workers automatically restart on unhandled errors. Configure with onError and onWorkerExit hooks.
Backpressure Handling
Automatic detection and response to high connection loads. Configurable threshold (default: 1000 connections).
Performance
- Node.js: Up to 4x more concurrent connections than single-threaded
- Bun: Native threading provides optimal performance
- Deno: Efficient subprocess communication
- Workers: Platform-managed scaling
- Edge: Optimized for global distribution
Integration with @rabbx Ecosystem
- @rabbx/ws - Zero-dep WebSocket for all runtimes
- @rabbx/colors - Terminal color utilities
- @rabbx/ms - Time parsing utilities
FAQ
Does it work with TypeScript?
Yes, full TypeScript support with strict typing.
How does WebSocket clustering work?
Each worker can handle WebSocket connections independently. For shared state, use the integrated KV store.
Can I use it with existing frameworks?
Yes, integrates with Express, Fastify, Hono, Next.js, etc.
What about hot reloading in production?
SIGUSR2 triggers graceful reload with zero downtime.
License?
MIT - completely free to use.
Sponsors
@rabbx/cluster is MIT licensed and free forever. If it saves you server costs or dev time, consider sponsoring.
Why sponsor
- Fund development - More platforms, better error handling, advanced metrics
- Priority issues - Sponsors get responses within 24h
- Your logo here - $100+/mo gets your logo in README
Companies using @rabbx/cluster: Add your logo by sponsoring at the $100 tier.
Top Sponsors
Want to become a sponsor? Join here
