Skip to content

Handle exceptions properly

Naomi Cheung edited this page Jun 23, 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.

Catching errors

Errors that occur in sychronous code inside route handlers and middleware require no extra work. If sychronous code throws an error, Express will catch and process it, for example:

app.get('/', function (req, res) {
  throw new Error('BROKEN') // Express will catch this on its own.
})

However Brian used it in a async function for the authentication:

const auth = async (req, res, next) => {
  //
  try {
    //
    // If there is no user throw an Errorrr
    if (!user) {
      throw new Error()
    }
    //
    // End of middlewareeeee
    next()
  } catch (e) {
    // If authentication fails, send error to authenticate to user
    console.log('Eerst inloggguuuh')
    res.status(401)
    res.redirect('/register')
  }
}

As you can see Brian uses throw in a try...catch block. The try..catch block will pass the error to Express since this is an async function. If he did not use the try...catch block, Express would not catch the rrors since it is not part of a sychronous handler code. But what does throw do? throw does two things:

  1. It stops the program, which prevents any more functions from running
  2. It executes the catch statement

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.

Other ways to handle asynchronous errors is with promises or callbacks.

  • Promises allows asynchronous code to be written to look like sychronous code and to catch errors using try...catch
  • Callback is a basic way of delivering an error asychronously

Brian used promises in his register/login feature, here is a part of his code in register.hbs:

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

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
    }
  },
)

He catches the error in register.js with a try...catch block.