Originally published byDev.to
Node.js is amazing for shipping fast. It's also amazing at letting you ship broken things fast.
I've built a lot of broken things. Here are the patterns that make Node.js systems not break.
Pattern 1: EventEmitter for Internal Pub/Sub
import { EventEmitter } from 'events';
const orderEvents = new EventEmitter();
async function createOrder(userId, items) {
const order = await db.orders.create({ userId, items });
orderEvents.emit('order:created', order);
return order;
}
orderEvents.on('order:created', async (order) => {
await sendConfirmationEmail(order).catch(err => logger.error('Email failed', err));
});
orderEvents.on('order:created', async (order) => {
await logAnalytics('order_created', order).catch(err => logger.error('Analytics failed', err));
});
Result: Order creation returns in 50ms. Side effects async. If email fails, order still succeeded.
Pattern 2: Worker Threads for CPU-Bound Tasks
import { Worker } from 'worker_threads';
const workers = Array(os.cpus().length).fill(null).map(() => new Worker('./worker.js'));
let currentWorker = 0;
function processImage(imageBuffer) {
return new Promise((resolve, reject) => {
const worker = workers[currentWorker];
currentWorker = (currentWorker + 1) % workers.length;
const timer = setTimeout(() => reject(new Error('Worker timeout')), 30000);
worker.once('message', (result) => { clearTimeout(timer); resolve(result); });
worker.once('error', reject);
worker.postMessage({ imageBuffer });
});
}
Result: Heavy computation doesn't block the main thread.
Pattern 3: Domain-Specific Error Classes
class AppError extends Error {
constructor(message, statusCode, code) {
super(message);
this.statusCode = statusCode;
this.code = code;
}
}
class NotFoundError extends AppError {
constructor(message) { super(message, 404, 'NOT_FOUND'); }
}
class ValidationError extends AppError {
constructor(message) { super(message, 400, 'VALIDATION_ERROR'); }
}
app.get('/users/:id', async (req, res, next) => {
try {
const user = await getUser(req.params.id);
res.json(user);
} catch (err) {
if (err instanceof AppError) {
res.status(err.statusCode).json({ error: err.message });
} else {
res.status(500).json({ error: 'Internal server error' });
}
}
});
Pattern 4: Connection Pooling
const pool = new pg.Pool({
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
async function getUser(id) {
const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
return result.rows[0];
}
Pattern 5: Circuit Breaker
class CircuitBreaker {
constructor(fn, options = {}) {
this.fn = fn;
this.failureThreshold = options.failureThreshold || 5;
this.resetTimeout = options.resetTimeout || 60000;
this.state = 'CLOSED';
this.failureCount = 0;
this.lastFailureTime = null;
}
async call(...args) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.resetTimeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await this.fn(...args);
this.failureCount = 0;
this.state = 'CLOSED';
return result;
} catch (err) {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) this.state = 'OPEN';
throw err;
}
}
}
Pattern 6: Graceful Shutdown
async function gracefulShutdown() {
server.close(async () => {
logger.info('Server closed');
});
const forceShutdown = setTimeout(() => process.exit(1), 30000);
try {
await db.close();
await redis.quit();
clearTimeout(forceShutdown);
process.exit(0);
} catch (err) {
process.exit(1);
}
}
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
The Meta Pattern: Async Error Handling
function asyncHandler(fn) {
return (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
}
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await db.getUser(req.params.id);
res.json(user);
}));
app.use((err, req, res, next) => {
logger.error('Unhandled error', err);
res.status(500).json({ error: 'Internal server error' });
});
These aren't fancy patterns. They're the ones that keep systems running at 3am without pages.
🇺🇸
More news from United StatesUnited States
NORTH AMERICA
Related News
How Braze’s CTO is rethinking engineering for the agentic area
10h ago
Amazon Employees Are 'Tokenmaxxing' Due To Pressure To Use AI Tools
21h ago

Implementing Multicloud Data Sharding with Hexagonal Storage Adapters
15h ago

DeepMind’s CEO Says AGI May Be ~4 Years Away. The Last Three Missing Pieces Are Not What Most People Think.
15h ago

CCSnapshot - A Claude Code Configs Transfer Tool
21h ago