Node.js backend patterns with async I/O and process management. Trigger: When building backend services, CLI tools, or server scripts.
Async I/O, process management, backend services with Node.js runtime.
Examples use TypeScript. For JavaScript, remove type annotations (
: string,interface,<T>,Promise<T>) — patterns apply identically.
Don't use for:
All I/O operations must be asynchronous to avoid blocking the event loop.
// ❌ WRONG: Blocking synchronous I/O
import fs from 'fs';
const data = fs.readFileSync('/path/to/file'); // Blocks
// ✅ CORRECT: Non-blocking async I/O
import fs from 'fs/promises';
const data = await fs.readFile('/path/to/file', 'utf-8');
Never hardcode configuration. Use environment variables with validation.
// ✅ CORRECT: Validated environment variables
const config = {
port: parseInt(process.env.PORT || '3000', 10),
nodeEnv: process.env.NODE_ENV || 'development',
dbUrl: process.env.DATABASE_URL, // Required
};
if (!config.dbUrl) {
throw new Error('DATABASE_URL environment variable is required');
}
Handle SIGTERM and SIGINT signals for clean shutdowns.
// ✅ CORRECT: Graceful shutdown handler
const server = app.listen(3000);
process.on('SIGTERM', async () => {
console.log('SIGTERM received, closing server...');
server.close(() => {
console.log('HTTP server closed');
});
// Close database connections
await db.close();
process.exit(0);
});
process.on('SIGINT', () => process.emit('SIGTERM' as any));
Catch unhandled promise rejections globally.
// ✅ CORRECT: Global error handlers
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
// Optionally exit after logging
process.exit(1);
});
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
process.exit(1);
});
Building an HTTP server?
→ Use framework (express, hono, nest) or http.createServer for simple cases
→ See express, hono, nest skills for framework-specific patterns
Writing a CLI tool?
→ Use process.argv for simple args
→ Use commander or yargs for complex CLI with commands/options
→ Handle process.exit codes (0 = success, 1+ = error)
Long-running process (worker, daemon)?
→ Listen for SIGTERM/SIGINT for graceful shutdown
→ Implement health checks for process monitoring
→ Use PM2 or systemd for process management in production
File I/O operations?
→ Use fs/promises for async file operations
→ Use streams for large files to avoid memory issues
→ Handle ENOENT, EACCES errors explicitly
Child process management?
→ Use child_process.spawn() for streaming output
→ Use child_process.exec() for small output
→ Always handle 'exit', 'error', and 'close' events
Memory-intensive operations?
→ Use worker_threads for CPU-bound tasks
→ Monitor process.memoryUsage() for leaks
→ Implement backpressure for streams
Unhandled rejections: .catch() or try/catch. Use process.on('unhandledRejection') as fallback.
Memory leaks: --inspect flag with Chrome DevTools. Common: event listeners not removed, closures, unbounded caches.
Child processes: Don't auto-exit with parent. Listen for 'exit' event, kill explicitly.
File descriptors: OS limits (default ~1024). Use ulimit -n or connection pooling.
Event loop blocking: CPU tasks block loop. Use worker threads or setImmediate().
// server.ts - Complete Node.js HTTP server with best practices
import http from 'http';
import fs from 'fs/promises';
import path from 'path';
// Configuration from environment
const config = {
port: parseInt(process.env.PORT || '3000', 10),
dataDir: process.env.DATA_DIR || './data',
};
// HTTP server
const server = http.createServer(async (req, res) => {
if (req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
return;
}
try {
const filePath = path.join(config.dataDir, 'data.json');
const data = await fs.readFile(filePath, 'utf-8');
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(data);
} catch (error: any) {
if (error.code === 'ENOENT') {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
} else {
console.error('Server error:', error);
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('Internal Server Error');
}
}
});
server.listen(config.port, () => {
console.log(`Server listening on port ${config.port}`);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, closing server...');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});
process.on('SIGINT', () => process.emit('SIGTERM' as any));
// Global error handlers
process.on('unhandledRejection', (reason) => {
console.error('Unhandled Rejection:', reason);
process.exit(1);
});
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
process.exit(1);
});
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer