Skip to content

Handle exceptions properly

Naomi Cheung edited this page Jun 29, 2020 · 5 revisions

Error handling refers to how Express catches and processes errors that occur both sychronously and asynchronously. It is important to know how to handle the errors and that the user also knows what went wrong. A few things to know first.

An error object

This is an error object (users.js, line 21):

throw new Error('Je bent te jong voor deze applicatie')

The error object is an implementation of a constructor function that uses a set of instructions (the arguments and constructor body itself) to create an object. The built-in error constructor is simply a unified way of creating an error object.

The first argument for a native error object is its description. The discription is the human-readable string or your error object: 'Je bent te jong voor deze applicatie'.

Second error objects also have a name property, which is the computer-readable part of the object. The name property defaults to the generic "Error", but you can create your own.

Now I haven't explained throw yet. Throw does two things:

  1. It stops the program
  2. It finds a catch to execute

When JavaScript finds a throw keyword, it stops in its tracks, which prevents any more functions from running. By stopping like this, it reduces risks of any further errors occuring. With the program halted, JavaScript will begin to look back up the call stack of functions that were called in order to reach a catch statement. The nearest catch that JavaScript finds is where the thrown exception will merge. If no try/catch is found, the exception throws, and the Node.js process will exit, causing the server to restart.

Handling async errors

We used almost only asynchronous code, so the error handling should also be working in our asynchronous code. There are two main ways you can handle async in JavaScript:

  1. Promises
  2. Callbacks

A promise in JavaScript is an object that represents a future value. Promises allow us to model asynchronous code like sychronous code through the use of the Promise API. A promise usually comes in a chain, where one action executes, then another and another and another.

Promises will catch any errors that preceded it in the chain, this means that we can handle many errors in many functions in a single handler, for example Brain's code in his register/login feature:

fetch('/users/login', {
	//
}).then((res) => {
	//
}).then((data) => {
		if (!data.error) {
				//
    } else {
        loginError.textContent = data.error
    }
})

Instead of handling functions, we are handling a request and data here. As you can see he did not use catch in the promise, and why is that? He did not use catch because it is not needed anymore with loginError.textContent = data.error. He catches the error somewhere else, and that is in register.js:

register.post(
  '/user',
  upload.fields([{ name: 'baby-img' }, { name: 'old-image' }]),
  async (req, res) => {
    //

    try {
      await user.save() // Save user in database
      console.log({ user, token })

      res.status(201).send({ user, token }) // Stuur object van user en token terug
    } catch (e) {
      console.log(e)
      console.log(e.message)
      res.status(400).send({ error: e }) // If something goes wrong, send an error back to client
    }
  },
)

Try and Catch

Express will catch the error on its own. For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function. Then Express will catch and process them, here is an example of Jo-Ann her code in her match feature:

async function match(req, res, next) {
  //

  try {
		//
    if (match[0]) {
      //
      })
    } else {
      //
    }
  } catch (error) {
    next(error)
  }
}

As you can see Jo-Ann uses a try...catch block to catch errors in the asychronous code. This works the same as I explained it with Brian's code.

Error route

Handling errors can be done in many ways, but how do we send the error to the user? First of all we got in our index.js:

app.get('/*', error)

error is the error.js, and errors.js renders error.hbs. So when a user types a route in the url that does not exist, the error.hbs will be send to the user.

Sources: