Error handling

  1. Method 1: try catch wraps the async request

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    async function addStudent(req, res){
    const {firstName, lastName, email} = req.body;
    const student = new Student({ firstName, lastName, email});
    try{
    await student.save();
    } catch (e){
    console.log(e);
    return res.send(e);
    }
    res.status(201).json(student);
    }

    Catch errors when writing async await.


  1. Method 2: Use .catch to get the error of promise

    1
    2
    3
    4
    5
    6
    7
    8
    9
    async function addStudent(req, res){
    const {firstName, lastName, email} = req.body;
    const student = new Student({ firstName, lastName, email});
    student.save().then().catch(e => { //promise.catch
    console.log(e);
    return res.send(e);
    })
    res.status(201).json(student);
    }

    Catch errors when writing promises.


  1. Method 3: Callback function

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    async function addStudent(req, res){
    const {firstName, lastName, email} = req.body;
    const student = new Student({ firstName, lastName, email});
    student.find(error, result) => { //callback function, similar to promise style
    if(error){
    console.log(e);
    return res.send(e);
    }
    }
    res.status(201).json(student);
    }

    It is used to catch errors when writing callback functions.


  1. Method selection

    If you are writing a new project yourself, you can choose the writing method at will. It is recommended to use the async await writing method. If it is to take over the existing code, write it in the way of the company’s current code.


  2. Writing in actual development (try catch method extension)

    The try catch method wraps the await code. And try catch can expand the scope, that is, wrap the entire C/R/U/D function of the route handler (such as addStudeny()), that is, catch the error of the route handler. Likewise, this error handling applies to all route handlers.

    In the following writing method, the purpose of the function is to wrap the function passed in as a parameter in a try catch, and register it on the path as a middleware after wrapping.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function tryCatch(routeHandler){ //Definition, in the student folder in the controllers file
    return async (req, res, next) => { //Return a middleware, because this middleware needs to be registered on the path.
    try{
    await routeHandler(req, res, next);
    } catch(e){
    next(e); //Pass the error object to the error handling middleware.
    }
    }
    }
    tryCatch(addStudent);

    Common errors are usually passed to the error handling middleware with next(e). And more specific errors will be handled directly in the function. For example, method one is often mixed with this method. Common errors are passed into the error handling middleware in this method, and specific errors are handled in the function using the method of method 1.


  3. Fetch errors directly with the express-async-errors package

    It enters the express framework for try catch operation. Install: npm i express-async-errors --save

    This package is for express@4 version. In the express@5 version, the official has built this function. (@4.18 for p3)

    This package should be imported under the index.js file.

    1
    2
    3
    4
    const express = require('express');
    require('express-async-errors'); //Use the express-async-errors package immediately after using the express package
    const User = require('./models/user');
    ...

    In addition, const a = require('a') is written because a is exported in the file. But it should be noted that whether the file is exported or not has no effect on the execution of the code inside the file, that is, when require('a'), the code inside the a file has been executed once. And express-async-errors has made changes to the express object in the code, no need to export.

    The error returned at this time is an error interpretation of express, which is difficult to read and needs further processing.


    For handling captured errors, you can create an errorHandler.js file in the middleware folder (Node.js@4.5):

    1
    2
    3
    4
    5
    6
    module.exports = (error, req, res, next) => {
    console.log(error);
    return res.status(500).json({
    error:'Error! Please try later',
    })
    }

    ErrorHandler.js is referenced in index.js and registered on the app. (Written before connectToDB)

    1
    app.use(errorHandler);

    At this time, an error is returned, which is status code 500, and the returned JSON content is customized. Therefore, how to handle errors is actually based on the logic in errorHandler.js. 500 is an under-the-radar error, and a series of if/else or switch should actually be used to handle various errors. The logic in it is to detect the type of error and then handle it. like:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    module.exports = (error, req, res, next) => {
    if(error.name === 'validationError'){ //Original method, because it is unknown whether there is a name attribute in error.
    //To find the error property by breakpoint or documentation.
    return res.status(400).json(error); //This is to pass the error object to the front end. But actually only the message is needed.
    } //In fact, the development environment returns all the information, and the production environment only needs to return the message.

    console.log(error);
    return res.status(500).json({
    error:'Error! Please try later',
    })
    }

    Compatible with the production environment and the development environment:

    1
    2
    3
    4
    5
    6
    7
    module.exports = (error, req, res, next) => {
    if(error.name === 'validationError'){
    if(process.env.NODE_ENV === 'production'){
    return res.status(400).json({ error: error.message});
    }
    return res.status(400).json(error);
    }

    A better way to detect:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    module.exports = (error, req, res, next) => {
    if(error instanceof CustomError){}
    }

    next(new CustomError('xxxxx')); // throw error

    class CustomError extends Error {
    constructor(message){
    super(message);
    }
    }


  1. handling more bugs

    If there are too many errors, the errorHandler.js file will be too long. The file should be removed at this time. The structure should then be:

    1
    2
    3
    4
    5
    |-- middleware
    |-- error
    |-- index.js //Introduce all error handling files and export them uniformly
    |-- validationErrorHandler.js //If the error is a validation error, return it; if not, use `next()` to pass to the next file.
    |-- xxxxErrorHandler.js

    At this time, it should also be imported with app.use() in index.js in the root directory.


Share