Error Handling refers to the process of catching and processing errors in code.
We can never assure that our web application is free of errors or bugs. Unintentional errors can cause our server to crash, and therefore it is crucial to handle the errors.
So, without further delay, let us see how we can handle errors in Express.
Error Handling in Express
When we are working with Express, Error Handling is done using middlewares. Express even comes with its own default Error Handler.
However, the default Error Handler only sends an HTML formatted error response. So in cases where you would like to process the errors differently, you need to use a custom Error Handler where you can put your Error Handling logic. We will see how to use a custom Error Handler in this blog, but first, let us look at Express’s default error handling.
Default/ Built-in Error Handler
The default Error Handler is very helpful in taking care of unhandled and unexpected errors in the code. This default Error Handler is a middleware function added to the end of the middleware function stack.
If the error is passed to next(), the default/built-in Error Handler will handle the error in the code (if a custom Error Handler does not handle the error). We will see Error Handling for both Synchronous and Asynchronous code.
Error Handling for Synchronous code
As the name suggests, Synchronous code is executed in sequence – each statement executes only after the previous statement has finished executing.
Whenever an error occurs in synchronous code, the built-in error handler in Express automatically handles the error with the help of a middleware function. After receiving the error, this middleware function processes the error and sends a response to the user.
Let us understand this with the help of an express app.
The code below creates a simple express app. This app listens on port 80 for connections and responds with “Hello World!” for requests to “/”.
Code:
// Importing express module
var express = require("express");
const port = 80;
const app = express();
// Handling the get request
app.get("/", (req, res) => {
res.send("Error Handling in Express");
});
// Starting the server on the 80 port
app.listen(port, () => {
console.log(`The application started successfully!`);
});
Now, if we go to http://localhost/, we see the following page saying “Error Handling in Express”.
Output:
Now let us throw an error and see how Express handles it.
Code:
// Importing express module
var express = require("express");
const port = 80;
const app = express();
// Handling the get request
app.get("/", (req, res) => {
// Express will catch this error on its own
throw new Error("ERROR!");
});
// Starting the server on the 80 port
app.listen(port, () => {
console.log(`The application started successfully!`);
});
When we again visit http://localhost/, the following error message shows up.
Output:
As we can see, Express has handled the error for us and has sent a response to the client with the error message along with the entire stack trace.
Custom Error Handler
But what if we want to use our Error Handling logic instead of the default one? With our custom Error Handler, we can redirect the user to a different page, show an alert message to the user, etc.
Defining the error handler function is the same as defining middleware functions.
In the case of middleware functions, we have three arguments for app.use() function -
req
res
Next
Example of a middleware function:
Code:
// Middleware function with .use() function and three arguments - req,res and next
app.use((req, res, next) => {
// Write this message to the console
console.log("Hello from Middleware!");
// Calling the next middleware function or handler
next();
});
For defining the error handler, we will use app.use() and these three arguments along with an additional argument for error. This error argument will receive the error object.
Now, if any error happens in synchronous code, the error will be handled by the custom error handler.
Syntax:
app.use(( error, req, res, next) => {
…
…
...
});
NOTE: The custom error handler has to be the last middleware function in the code.
Let us add a custom error handler in the code to see what happens.
Code:
// Importing express module
var express = require("express");
const port = 80;
const app = express();
// Handling the get request
app.get("/", (req, res) => {
// Express will catch this error on its own
throw new Error("ERROR!");
});
// Custom Error Handler
app.use((error, req, res, next) => {
// Write this message to the console
console.log("Custom Error Handler!");
console.log(error);
// Send the response to the client
res.status(500).send("ERROR!(from Custom Error Handler!)");
});
// Starting the server on the 80 port
app.listen(port, () => {
console.log(`The application started successfully!`);
});
In our custom handler, we are printing a message and also printing the error in the console. We are also setting the status of the response as 500 and sending a response with a custom error message.
We can see that the response sent by the server is the one that we mentioned in our Custom Error Handler. It means our Custom Error Handler is working fine.
NOTE: If we do not call next() in the error-handling function, we will have to write/end the response.
Error Handling for Asynchronous code
Now we will see how the error is handled in the case of Asynchronous code.
In Asynchronous code, unlike Synchronous code, statements can execute independently of the main program flow.
For Asynchronous code, errors are not automatically catched by Express as it happens for Synchronous Code. We will have to explicitly pass them to the next() function so Express will catch and process them using the default/ built-in error handler.
In the below code, the fs.readFile() function is used to read a file into the buffer in an asynchronous way.
The fs.readFile() takes three parameters,
filename
encoding
callback function
The callback function is called after the reading of the file is done. It again has two parameters - error and data.
The readFile() returns the content of the file or returns the error in case of an error.
Code:
// Importing express module
var express = require("express");
var fs = require("fs");
const port = 80;
const app = express();
// Handling the get request
app.get("/", function (req, res, next) {
fs.readFile("/file-does-not-exist", function (error, data) {
if (error) {const fs = require("fs");
next(error); // Passing error to Express with next()
} else {
res.send(data); // Sending data as response if no error is present
}
});
});
// Starting the server on the 80 port
app.listen(port, () => {
console.log(`The application started successfully!`);
});
As the file path does not exist, the error is sent to express with next(). The built-in error handler handles the error and sends the following response to the client. The error is ENOENT, which means no such file or directory is found.
Output:
Custom Error Handler
We can also use our previously used custom error handler to handle the error.
Code:
// Importing express module
var express = require("express");
var fs = require("fs");
const port = 80;
const app = express();
// Handling the get request
app.get("/", function (req, res, next) {
fs.readFile("/file-does-not-exist", function (error, data) {
if (error) {const fs = require("fs");
next(error); // Passing error to Express with next()
} else {
res.send(data); // Sending data as response if no error is present
}
});
});
// Custom Error Handler
app.use((error, req, res, next) => {
// Write this message to the console
console.log("Custom Error Handler!");
console.log(error);
// Send the response to client
res.status(500).send("ERROR!(from Custom Error Handler!)");
});
// Starting the server on the 80 port
app.listen(port, () => {
console.log(`The application started successfully!`);
});
Output:
Frequently Asked Questions
Q1. What is Error Handling?
Ans: Errors are objects that are thrown whenever a runtime error occurs.Error Handling refers to catching and processing these errors in code.
Q2. How is error handled in JavaScript?
Ans: Error is handled in JavaScript using try...catch block. The block of code inside it is executed first.
In case of an exception thrown by try block, catch block is executed.
Syntax:
try {
…
}
catch (error) {
…
}
Code:
error.html
<html>
<head>
Exception Handling
</head>
<body>
<script>
try {
throw new Error("Hi Error!"); // Throw error with user-defined error statement
} catch (error) {
document.write(error.message); // This will generate an error message
}
</script>
</body>
</html>
Output:
Q3. What is the difference between Synchronous and Asynchronous code?
Ans: In the case of Synchronous code, statements are executed in sequence. Every statement executes only after the previous statement has finished executing. So every statement is dependent on the main program flow.
Asynchronous code is exactly the opposite of Synchronous code. The statements of Asynchronous code are independent of the main program flow. The code doesn't have to wait for previous statements to finish executing, and the program can continue to run.
Q4. How does Express handle 404 responses?
Ans: 404 response is an HTTP status code. 404 status code indicates that the server cannot find the requested resource.
The express error handler cannot catch the 404 response as it is not an error. We have to add a custom middleware function at the bottom of our code to handle it.
For example, can use the following code snippet to handle 404 response,
app.use(function (req, res, next) {
res.status(404).send("The page you are looking for cannot be found!")
})
Key Takeaways
In this article, we discussed how error handling works in Express. We learned about default/built-in error handling and how we supply our error logic in case of custom error handlers.
To get started with Express.js, we recommend you go through this amazing blog.