Introduction
In this blog we are going to add exception handling to JavaScript REST API by coding our own Exception Middleware. We will also learn how to add multiple exception handlers to our API and build a reusable error module that can be handy to when having multiple repositories in an API. Lastly, we will also see how we can add error logging capabilities to our code which logs errors to a file.
Setup
In this blog I will not be covering how to build a REST API in JavaScript, and I will use the exisiting GroceryAPI that I built in my other blog. It covers all the coding required to build an API from scratch in JavaScript using the Express framework, so do check it out:- Beginner's Guide to REST API in JavaScript using Express
The completed code for this tutorial can be found in the following GitHub Repo in the exception-handling branch.
Code
We'll start by creating a reusable error module.
Create a file named errorHelper.js
in the /helpers
directory
We will write our first helper method errorBuilder
inside of the errorHelpers
object which we will export.
errorBuilder
will return a custom object to client with status 500 and statusText as "Internal Server Error", the rest of the object will contain tht error message, error number and error syscall. This errorBuilder will accept an error object. We can then use this errorBuilder to build error messages for logging errors to console, logging errors to file etc.
let errorHelpers = {
errorBuilder: (err) => {
return {
status: 500,
statusText: "Internal Server Error",
message: err.message,
error: {
errno: err.errno,
call: err.syscall,
code: "INTERNAL_SERVER_ERROR",
message: err.message,
},
};
},
};
Now, we create a logErrorsToConsole
helper function inside errorHelpers which will use errorBuilder
and print the error to the server console whenever it occurs.
In logErrorsToConsole
function we simple do a console.error
of the error by passing our err object to previously created errorBuilder
we simply add * to distinguish the error in the console and then we use the next(err);
to move onto the next middleware available.
Now let's configure our middleware in index.js
Simply import the error helper by adding:
let errorHelper = require("./helpers/errorHelpers");
Then, we will add the middleware just after the router configuration.
// Configure router so all routes are prefixed with /api/
app.use("/api/", router);
// Configure exception logger to console
app.use(errorHelper.logErrorsToConsole);
Now to see our error logger middleware work we will change the spelling of the gorceries.json file in the file name variable in groceryRepo.js
by removing the s from the const FILE_NAME = "./assets/grocerie.json";
Finally, when we hit the GET request to fetch all groceries we should see the error logged in our terminal like below:
Adding Error Logging to a log file
So far we setup our own middleware that logs error to console whenever an Internal Server Error occurs.
What if we want the error logged out in a log file that stores all logs of all the errors occured for fututre refernce?
We will achieve this in three steps:
1) Create a logRepo.js file to handle the writes to a log file.
2) Add a helper method to write errors to the log file using the logRepo.
3) Add the helper method as a middleware to our index.js file.
Let's get started!
First step is to create a logRepo.js
file in the /repo
directory. The code for this will be very similar to the calls to get, update, delete and write data to our grocery store repo.
We create a write function which takes data, req and res as the parameters. On line 7 we create a toWrite
variable which will store the information to be logged.
Notice that we have assigned a file to the FILE_NAME
field on line 3, make sure to create an empty log.txt file in a logs folder.
We just append all the information that we need to see in our log file such as the Date and Time the error occured, Exception Info and some formatting of the log by appending * at the start and end of the message.
After that we simply use the fs
package to write the exception message to the log file on our local file system.
Next, we configure a helper method to log errors to log file in the errorHelpers.js
file.
We add the logErrorsToFile
method object just after logErrorsToConsole
method. The implementation of which is pretty straight-forward. We build the errorObject using our errorBuilder
and assign some extra properties to it from the request object. Then we call in the logRepo.write
and pass in this errorObject.
NOTE: Don't forget to import the logRepo in errorHelpers.js file at the top.
logErrorsToFile: (err, req, res, next) => {
let errorObject = errorHelpers.errorBuilder(err);
errorObject.requestInfo = {
hostName: req.hostName,
path: req.path,
app: req.app,
};
logRepo.write(
errorObject,
(data) => {
console.log(data);
},
(err) => console.error(err)
);
next(err);
}
Lastly, one final step is to add our middleware to handle this logging functionality in index.js
file just after our logErrorsToConsole middleware.
// Configure router so all routes are prefixed with /api/
app.use("/api/", router);
// Configure exception logger to console
app.use(errorHelper.logErrorsToConsole);
// Configure exception logger to console
app.use(errorHelper.logErrorsToFile);
Testing logging to text file
Similar to how we tested our logErrorsToConsole
, just remove the appending 's' from the file name in groceryRepo.js and send a GET request to http://localhost:5000/api/
You should see the error logged to console as well as the exception logged to the log.txt file as seen below with Date and Time.
If you have any doubts you can ask in the comments section below, I will be happy to help.
Happy Coding!
References
https://expressjs.com/en/guide/using-middleware.html
https://expressjs.com/en/guide/error-handling.html
Top comments (1)
Logging to a file is too 20th century. Better to use, for example,
serilogger
with an instance of Seq running in Docker.