Error Handling in Node.js: The Missing Guide Most Node.js tutorials skip error handling. Here's what they don't tell you. The Three Types of Errors // 1. Operational Errors (expected, handle them) // - File not found, network timeout, invalid input // - These ARE going to happen. Plan for them. // 2. Programmer Errors (bugs, fix them) // - TypeError, ReferenceError, logic errors // - These should NOT happen. Fix the code. // 3. System Errors (infrastructure, retry them) // - Out of memory, connection refused, DNS failure // - Often transient. Retry with backoff. Enter fullscreen mode Exit fullscreen mode Synchronous Error Handling // Use try/catch for synchronous code function parseConfig(filePath) { try { const raw = fs.readFileSync(filePath, 'utf8'); return JSON.parse(raw); } catch (err) { if (err.code === 'ENOENT') { console.error(`Config file not found: ${filePath}`); return getDefaultConfig(); } if (err instanceof SyntaxError) { console.error(`Invalid JSON in ${filePath}: ${err.message}`); throw new ConfigError('Malformed config file', { filePath }); } throw err; // Unknown error, let it bubble } } Enter fullscreen mode Exit fullscreen mode Async Error Handling (The Right Way) ❌ Wrong: Callbacks // Unhandled rejection if readFile fails fs.readFile('data.json', (err, data) => { const parsed = JSON.parse(data); // What if data is malformed? }); Enter fullscreen mode Exit fullscreen mode ✅ Right: Promises with try/catch async function loadData() { try { const raw = await fs.promises.readFile('data.json', 'utf8'); return JSON.parse(raw); } catch (err) { if (err.code === 'ENOENT') { return getDefaultData(); } throw new DataError('Failed to load data', { cause: err }); } } Enter fullscreen mode Exit fullscreen mode ✅ Right: Express middleware // Async errors need special handling in Express! app.get('/api/users/:id', async (req, res, next) => { try { const user = await User.findById(req.params.id); if (!user) return res.status(404).json({ error: 'User not found' }); res.json({ data: user }); } catch (err) { next(err); // Pass to error handler! } }); // Global error handler (MUST have 4 parameters) app.use((err, req, res, _next) => { console.error(`[ERR] ${req.method} ${req.path}:`, err); // Don't leak stack traces in production const message = process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message; res.status(err.statusCode || 500).json({ error: { code: err.code || 'INTERNAL_ERROR', message, ...(process.env.NODE_ENV !== 'production' && { stack: err.stack }) } }); }); Enter fullscreen mode Exit fullscreen mode Custom Error Classes // Base application error class AppError extends Error { constructor(message, { statusCode = 500, code = 'APP_ERROR', details = null } = {}) { super(message); this.name = this.constructor.name; this.statusCode = statusCode; this.code = code; this.details = details; Error.captureStackTrace(this, this.constructor); } } // Specific error types class NotFoundError extends AppError { constructor(resource, id) { super(`${resource} not found`, { statusCode: 404, code: 'NOT_FOUND', details: { resource, id } }); } } class ValidationError extends AppError { constructor(errors) { super('Validation failed', { statusCode: 422, code: 'VALIDATION_ERROR', details: errors // [{ field: 'email', issue: 'Invalid format' }] }); } } class AuthError extends AppError { constructor(message = 'Authentication required') { super(message, { statusCode: 401, code: 'AUTH_ERROR' }); } } class RateLimitError extends AppError { constructor(retryAfter) { super('Too many requests', { statusCode: 429, code: 'RATE_LIMITED', details: { retryAfter } }); } } // Usage if (!user) throw new NotFoundError('User', id); if (!isValidEmail(email)) throw new ValidationError([{ field: 'email', issue: 'Invalid format' }]); Enter fullscreen mode Exit fullscreen mode Retry with Exponential Backoff async function withRetry(fn, { maxAttempts = 3, baseDelay = 1000, jitter = true } = {}) { let lastError; for (let attempt = 1; attempt = 400 && err.statusCode setTimeout(resolve, delay + jitterAmount)); } } throw lastError; } // Usage const data = await withRetry( () => fetch('https://api.example.com/data').then(r => r.json()), { maxAttempts: 5, baseDelay: 500 } ); Enter fullscreen mode Exit fullscreen mode Graceful Shutdown const server = app.listen(3000); // Track active connections const connections = new Set(); server.on('connection', (conn) => { connections.add(conn); conn.on('close', () => connections.delete(conn)); }); // Handle shutdown signals function shutdown(signal) { console.log(`\n${signal} received. Shutting down gracefully...`); // Stop accepting new connections server.close(() => { console.log('HTTP server closed'); process.exit(0); }); // Force exit after timeout setTimeout(() => { console.error('Forced shutdown after timeout'); process.exit(1); }, 10000); } process.on('SIGTERM', () => shutdown('SIGTERM')); process.on('SIGINT', () => shutdown('SIGINT')); // Handle uncaught exceptions (last resort!) process.on('uncaughtException', (err) => { console.error('UNCAUGHT EXCEPTION:', err); // Log and restart — don't try to continue shutdown('UNCAUGHT_EXCEPTION'); }); process.on('unhandledRejection', (reason, promise) => { console.error('UNHANDLED REJECTION:', reason); // Log it, but don't crash for unhandled rejections (Node 15+ changed this) }); Enter fullscreen mode Exit fullscreen mode The Error Handling Checklist [ ] Every await in a try/catch or .catch() [ ] Express async routes pass errors to next(err) [ ] Global error handler doesn't leak stack traces in production [ ] Custom error classes for different error types [ ] Network calls use retry with backoff [ ] Database queries handle connection errors [ ] Input validation before processing [ ] Graceful shutdown on SIGTERM/SIGINT [ ] uncaughtException and unhandledRejection handlers [ ] Error logging with context (request ID, user, path) [ ] Client-friendly error messages (not raw stack traces) What's your approach to error handling? Any patterns I missed? Follow @armorbreak for more Node.js guides.
Error Handling in Node.js: The Missing Guide
Full Article
Original Source
Read the full article at Dev →KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.