Node JS error handling Node.js and Express
Error handling in Node.js and Express is crucial for building reliable and robust applications. It involves managing both synchronous and asynchronous errors, ensuring that errors are reported effectively, and preventing the application from crashing due to unhandled issues. Here’s a detailed guide on error handling in Node.js and Express:
1. Error Handling in Node.js
1.1 Synchronous Error Handling
In synchronous code, use try
and catch
blocks to handle errors:
Example:
try {
// Code that might throw an error
const result = someFunction();
console.log(result);
} catch (error) {
// Handle the error
console.error('An error occurred:', error.message);
}
1.2 Asynchronous Error Handling
1.2.1 Callbacks
For asynchronous code using callbacks, handle errors with the error-first callback pattern:
Example:
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (error, data) => {
if (error) {
// Handle the error
console.error('An error occurred:', error.message);
return;
}
// Process the data
console.log(data);
});
1.2.2 Promises
Handle errors in promises using .catch()
:
Example:
const fs = require('fs').promises;
fs.readFile('file.txt', 'utf8')
.then(data => {
console.log(data);
})
.catch(error => {
console.error('An error occurred:', error.message);
});
1.2.3 Async/Await
Use try
/catch
within async
functions:
Example:
const fs = require('fs').promises;
async function readFile() {
try {
const data = await fs.readFile('file.txt', 'utf8');
console.log(data);
} catch (error) {
console.error('An error occurred:', error.message);
}
}
readFile();
1.3 Global Error Handling
For uncaught exceptions and unhandled promise rejections:
Example:
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error.message);
process.exit(1); // Optional: Exit the process
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1); // Optional: Exit the process
});
2. Error Handling in Express.js
Express.js provides a structured approach to handle errors through middleware.
2.1 Basic Error-Handling Middleware
Define an error-handling middleware function with four arguments: err
, req
, res
, and next
.
Example:
const express = require('express');
const app = express();
const port = 3000;
// Define a route that may throw an error
app.get('/', (req, res) => {
throw new Error('Something went wrong!');
});
// Basic error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
2.2 Handling Specific Errors
Customize error handling based on error types or properties:
Example:
app.use((err, req, res, next) => {
if (err instanceof SyntaxError) {
res.status(400).send('Bad JSON format');
} else if (err.status === 404) {
res.status(404).send('Not Found');
} else {
console.error(err.stack);
res.status(500).send('Internal Server Error');
}
});
2.3 Asynchronous Error Handling in Routes
Handle errors in asynchronous route handlers by passing them to the next middleware:
Example:
app.get('/async', async (req, res, next) => {
try {
const data = await someAsyncFunction();
res.send(data);
} catch (error) {
next(error); // Pass the error to the error-handling middleware
}
});
2.4 Using Third-Party Middleware for Error Handling
You can use third-party middleware to simplify error handling. For example, express-async-errors
helps with handling asynchronous errors:
Installation:
npm install express-async-errors
Usage:
const express = require('express');
require('express-async-errors'); // Import this module before defining routes
const app = express();
const port = 3000;
app.get('/async', async (req, res) => {
const data = await someAsyncFunction();
res.send(data);
});
// Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something went wrong!');
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
3. Best Practices for Error Handling
- Graceful Degradation: Ensure your application can handle errors and continue operating where possible.
- Error Logging: Use logging mechanisms to capture error details for debugging and monitoring.
- User-Friendly Messages: Provide clear and informative error messages to end-users.
- Consistent Error Responses: Use a consistent format for error responses in APIs.
- Avoid Exposing Sensitive Information: Don’t expose stack traces or internal details in error responses.
- Test Error Scenarios: Test your error handling thoroughly to ensure robustness.