Course Banner

Handling Errors

Introduction

As you write code, you will find that there are errors and exceptions. An exception is an anticipated error, while an error is unanticipated. In our code we have anticipated that a client may enter the wrong data during the registration process and have already added code to deal with this. However, in most of our code, we have not done anything really, to deal with the technical or run-time errors that are unanticipated. That's what this lesson deals with.

Express has built-in error handling, which we are going to take advantage of, but it needs to be informed of errors differently when the error occurs in the normal flow of the application versus when it occurs in a "promise-based" function. The normal flow is "Synchronous", while the promise-based is "Asynchronous". Typically, we would have to handle these errors differently, but by taking advantage of a "Higher-Order" function, we can make everything in the application behave "asynchronously", and use a common means of dealing with errors. What is a Higher-Order function? I encourage you to read Higher Order Functions in JavaScript - Beginner's Guide to learn more about these special functions.

Video Overview

The video provides a general overview of the activity, but does not contain the detail needed to complete each process. Watch the video to obtain a general idea, but follow the written steps to complete the activity. This is the Transcript of the video.

Implementation

To put this approach to error handling into effect, we will need to write two functions:

Express Error Handler

The built-in Express error handler is unique in that it is a regular arrow function, but uses four parameters, instead of the normal two or three. The code below shows a generic version of this function:

app.use((error, req, res, next) => {
... code to process the error
  })
Notice the "error" parameter. This should be an error object being passed into the function. The object contains information about the error. We will use this extensively. Let's now build the first version of the function for our application:

  1. Open the server.js file.
  2. Scroll down to near the bottom of the file.
  3. Just above the "Local Server Information" comment, create some empty lines.
  4. This placement is CRITICAL! This function must, as the comment says, "Place after all other middleware". If it appears above other middleware functions, they will never be reached to run and the application will break.
  5. Add the following in the empty lines:
    /* ***********************
    * Express Error Handler
    * Place after all other middleware
    *************************/
    app.use(async (err, req, res, next) => {
      let nav = await utilities.getNav()
      console.error(`Error at: "${req.originalUrl}": ${err.message}`)
      res.render("errors/error", {
        title: err.status || 'Server Error',
        message: err.message,
        nav
      })
    })
  6. Ensure there are no warnings or errors.
  7. Save the file.
  8. An Explanation

    • Lines 1-4 - a multi-line comment for the function.
    • Line 5 - app.use is an Express function, which accepts the default Express arrow function to be used with errors.
    • Line 6 - builds the navigation bar for the error view.
    • Line 7 - a console statement to show the route and error that occurred. This is helpful to you to know what the client was doing when the error occurred.
    • Line 8 - calls the "error.ejs" view (you will build that next) in an "errors" folder.
    • Line 9 - sets the value of the "title" for the view. It will use the status code or "Server Error" as the title if no status code is set.
    • Line 10 - sets the message to be displayed in the error view to the message sent in the error object. We will alter this later.
    • Line 11 - sets the navigation bar for use in the error view.
    • Line 12 - ends the render function.
    • Line 13 - ends the arrow and app.use functions.

If you look closely at line 6, you'll notice that we're calling a function from the utilities > index file. This file is not in the scope of the server.js file. If you test without it being in scope, the application will crash. Add the file to the scope of the page before moving on! Talk with your learning team if you need help.

Build the Error View

  1. Click the "views" folder.
  2. Click the "New file" icon, type errors/error.ejs, press "Enter".
  3. A new errors folder will be created and a new error.ejs file inside it.
  4. As with other views, the "title" being passed in via the render function should be used for the main page <h1> heading. Add this code to the view. Consult previous views to see the code that you need to add.
  5. Make sure the file clearly displays the message being passed in through the render function. Add a EJS unescaped code block and add the "message" variable to it to be displayed.
  6. Nobody likes to receive an error message. But, it can be offset if we add humor to the message. This is often done with 404 - File Not Found errors, but don't have to be limited to them. If you're interested, here are some funny 404 pages. Feel free to make your error view fun too!
  7. Make sure there are no warnings or errors.
  8. Save the file.

Time to Test

404 - File Not Found

The "404 - File Not Found" error is the most common error on the Internet. It can be caused by any number of factors, such as: the website being shutdown, the page being renamed, or the path to the file being changed, to name only a few. We will deal with this error using a custom route, the Express Error Handler and the error view.

  1. Find and open the server.js file.
  2. Scroll down and find the "routes" section.
  3. Add the following as the last route. Note: order is very important in this instance. The routes to the other router documents must be above this route, or they will never be reachable!
    // File Not Found Route - must be last route in list
    app.use(async (req, res, next) => {
      next({status: 404, message: 'Sorry, we appear to have lost that page.'})
    })
  4. Ensure there are no warnings or errors.
  5. Save the file.
  6. An Explanation

    • Line 1 - a comment pertaining to the route
    • Line 2 - the Express use function containing an asynchronous arrow function.
    • Line 3 - the next function to pass control to the next function in the processing chain. An object (in our case, an error object) with a status and message is sent. Remember that the Error Handler watches for an error object. We are sending an error object as identified by the error status and message.
    • Line 4 - ends the arrow and use functions.
  7. Open or expand the VSC terminal.
  8. Start the server with "pnpm run dev", press "Enter".
  9. Open a browser tab and go to localhost:5500.
  10. Now, add a "/" to the end of the URL and type a string that does NOT match any existing path in the application (e.g. localhost:5500/chewbacca).
  11. If things are working, you should be viewing your error view with "404" as both the title and h1 heading, and the message, "Sorry, we appear to have lost that page." displayed.
  12. If it worked, play the song "Celebrate" by Earth, Wind and Fire and dance along. If it failed, review your code and the Node console, talk to a learning team member, the TA or the professor. But, get it working before moving forward.

Conclusion

With the Express error handler in place, the error view built and a route for catching routes that don't exist functioning, we are ready to write and implement the Higher Order function for dealing with errors in all the other locations of our application. We'll do that in the next activity.