Error Handling
-
Error handling ensures application stability by managing errors centrally and using custom error classes for consistent API responses.
Why Handle Errors?
Errors can occur due to invalid input, system failures, or programming mistakes. Handling them properly ensures your app doesn't crash unexpectedly.
Key Points:
Prevents applications from crashing
Provides meaningful feedback to users
Makes debugging easier
Maintains production stability
Ensures proper resource cleanup
🔹 Common Error Types in Node.js
Standard JavaScript Errors
Example:
Common Error Types in Node.js
Node.js errors include standard JavaScript errors like SyntaxError, TypeError, and ReferenceError, as well as system errors such as ENOENT (file not found) and ECONNREFUSED (connection refused), which occur during file or network operations.
// SyntaxError
JSON.parse('{invalid json}');
// TypeError
null.someProperty;
// ReferenceError
unknownVariable;
System Errors
// ENOENT: File not found
const fs = require('fs');
fs.readFile('nonexistent.txt', (err) => {
console.error(err.code); // 'ENOENT'
});
// ECONNREFUSED: Connection refused
const http = require('http');
const req = http.get('http://nonexistent-site.com', (res) => {});
req.on('error', (err) => {
console.error(err.code); // 'ECONNREFUSED'
});
🔹 Basic Error Handling
Error-First Callbacks
Node.js core modules follow this pattern where the first argument of a callback is an error object.
Example:
Basic Error Handling in Node.js
Handles file read errors using error-first callbacks, ensuring safe execution without crashing.
const fs = require('fs');
function readConfigFile(filename, callback) {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) {
if (err.code === 'ENOENT') {
return callback(new Error(`Config file ${filename} not found`));
}
return callback(err);
}
try {
const config = JSON.parse(data);
callback(null, config);
} catch (parseError) {
callback(new Error(`Invalid JSON in ${filename}`));
}
});
}
readConfigFile('config.json', (err, config) => {
if (err) return console.error('Failed:', err.message);
console.log('Config loaded:', config);
});
🔹 Modern Error Handling
Using try…catch with Async/Await
Example:
Modern Error Handling with Async/Await
Uses try…catch with async/await to handle file read and JSON errors safely, ensuring proper error messages without crashing the app.
const fs = require('fs').promises;
async function loadUserData(userId) {
try {
const data = await fs.readFile(`users/${userId}.json`, 'utf8');
const user = JSON.parse(data);
if (!user.email) throw new Error('Missing email');
return user;
} catch (error) {
if (error.code === 'ENOENT') throw new Error(`User ${userId} not found`);
if (error instanceof SyntaxError) throw new Error('Invalid JSON format');
throw error;
} finally {
console.log(`Finished processing user ${userId}`);
}
}
(async () => {
try {
const user = await loadUserData(123);
console.log('User loaded:', user);
} catch (error) {
console.error('Error:', error.message);
}
})();
🔹 Global Error Handling
Example:
Global Error Handling in Node.js
Handles uncaught exceptions and unhandled promise rejections globally to prevent crashes and log errors before safely exiting the process.
process.on('uncaughtException', (error) => {
console.error('UNCAUGHT EXCEPTION!', error);
process.exit(1);
});
Unhandled Promise Rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('UNHANDLED REJECTION!', reason);
process.exit(1);
});
Promise.reject(new Error('Something went wrong'));
🔹 Error Handling Best Practices
Do’s
Handle errors at appropriate levels
Log errors with context
Use custom error types
Clean resources in finally
Validate inputs early
Don’ts
Ignore errors or use empty catch blocks
Expose sensitive info to clients
Use try/catch for flow control
Swallow errors without logging
🔹 Custom Error Classes
Example:
Custom Error Classes in Node.js
Defines custom error classes like ValidationError and NotFoundError to standardize error handling with specific messages and status codes.
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
this.statusCode = 400;
}
}
class NotFoundError extends Error {
constructor(resource) {
super(`${resource} not found`);
this.name = 'NotFoundError';
this.statusCode = 404;
}
}
// Usage
function getUser(id) {
if (!id) throw new ValidationError('User ID required', 'id');
}