Express error page

Error Handling refers to how Express catches and processes errors that occur both synchronously and asynchronously. Express comes with a default error handler so you don’t need to write your own to get started.

Error Handling refers to how Express catches and processes errors that
occur both synchronously and asynchronously. Express comes with a default error
handler so you don’t need to write your own to get started.

Catching Errors

It’s important to ensure that Express catches all errors that occur while
running route handlers and middleware.

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

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

For errors returned from asynchronous functions invoked by route handlers
and middleware, you must pass them to the next() function, where Express will
catch and process them. For example:

app.get('/', (req, res, next) => {
  fs.readFile('/file-does-not-exist', (err, data) => {
    if (err) {
      next(err) // Pass errors to Express.
    } else {
      res.send(data)
    }
  })
})

Starting with Express 5, route handlers and middleware that return a Promise
will call next(value) automatically when they reject or throw an error.
For example:

app.get('/user/:id', async (req, res, next) => {
  const user = await getUserById(req.params.id)
  res.send(user)
})

If getUserById throws an error or rejects, next will be called with either
the thrown error or the rejected value. If no rejected value is provided, next
will be called with a default Error object provided by the Express router.

If you pass anything to the next() function (except the string 'route'),
Express regards the current request as being an error and will skip any
remaining non-error handling routing and middleware functions.

If the callback in a sequence provides no data, only errors, you can simplify
this code as follows:

app.get('/', [
  function (req, res, next) {
    fs.writeFile('/inaccessible-path', 'data', next)
  },
  function (req, res) {
    res.send('OK')
  }
])

In the above example next is provided as the callback for fs.writeFile,
which is called with or without errors. If there is no error the second
handler is executed, otherwise Express catches and processes the error.

You must catch errors that occur in asynchronous code invoked by route handlers or
middleware and pass them to Express for processing. For example:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('BROKEN')
    } catch (err) {
      next(err)
    }
  }, 100)
})

The above example uses a try...catch block to catch errors in the
asynchronous code and pass them to Express. If the try...catch
block were omitted, Express would not catch the error since it is not part of the synchronous
handler code.

Use promises to avoid the overhead of the try...catch block or when using functions
that return promises. For example:

app.get('/', (req, res, next) => {
  Promise.resolve().then(() => {
    throw new Error('BROKEN')
  }).catch(next) // Errors will be passed to Express.
})

Since promises automatically catch both synchronous errors and rejected promises,
you can simply provide next as the final catch handler and Express will catch errors,
because the catch handler is given the error as the first argument.

You could also use a chain of handlers to rely on synchronous error
catching, by reducing the asynchronous code to something trivial. For example:

app.get('/', [
  function (req, res, next) {
    fs.readFile('/maybe-valid-file', 'utf-8', (err, data) => {
      res.locals.data = data
      next(err)
    })
  },
  function (req, res) {
    res.locals.data = res.locals.data.split(',')[1]
    res.send(res.locals.data)
  }
])

The above example has a couple of trivial statements from the readFile
call. If readFile causes an error, then it passes the error to Express, otherwise you
quickly return to the world of synchronous error handling in the next handler
in the chain. Then, the example above tries to process the data. If this fails then the
synchronous error handler will catch it. If you had done this processing inside
the readFile callback then the application might exit and the Express error
handlers would not run.

Whichever method you use, if you want Express error handlers to be called in and the
application to survive, you must ensure that Express receives the error.

The default error handler

Express comes with a built-in error handler that takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.

If you pass an error to next() and you do not handle it in a custom error
handler, it will be handled by the built-in error handler; the error will be
written to the client with the stack trace. The stack trace is not included
in the production environment.

Set the environment variable NODE_ENV to production, to run the app in production mode.

When an error is written, the following information is added to the
response:

  • The res.statusCode is set from err.status (or err.statusCode). If
    this value is outside the 4xx or 5xx range, it will be set to 500.
  • The res.statusMessage is set according to the status code.
  • The body will be the HTML of the status code message when in production
    environment, otherwise will be err.stack.
  • Any headers specified in an err.headers object.

If you call next() with an error after you have started writing the
response (for example, if you encounter an error while streaming the
response to the client) the Express default error handler closes the
connection and fails the request.

So when you add a custom error handler, you must delegate to
the default Express error handler, when the headers
have already been sent to the client:

function errorHandler (err, req, res, next) {
  if (res.headersSent) {
    return next(err)
  }
  res.status(500)
  res.render('error', { error: err })
}

Note that the default error handler can get triggered if you call next() with an error
in your code more than once, even if custom error handling middleware is in place.

Writing error handlers

Define error-handling middleware functions in the same way as other middleware functions,
except error-handling functions have four arguments instead of three:
(err, req, res, next). For example:

app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

You define error-handling middleware last, after other app.use() and routes calls; for example:

const bodyParser = require('body-parser')
const methodOverride = require('method-override')

app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use((err, req, res, next) => {
  // logic
})

Responses from within a middleware function can be in any format, such as an HTML error page, a simple message, or a JSON string.

For organizational (and higher-level framework) purposes, you can define
several error-handling middleware functions, much as you would with
regular middleware functions. For example, to define an error-handler
for requests made by using XHR and those without:

const bodyParser = require('body-parser')
const methodOverride = require('method-override')

app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)

In this example, the generic logErrors might write request and
error information to stderr, for example:

function logErrors (err, req, res, next) {
  console.error(err.stack)
  next(err)
}

Also in this example, clientErrorHandler is defined as follows; in this case, the error is explicitly passed along to the next one.

Notice that when not calling “next” in an error-handling function, you are responsible for writing (and ending) the response. Otherwise those requests will “hang” and will not be eligible for garbage collection.

function clientErrorHandler (err, req, res, next) {
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' })
  } else {
    next(err)
  }
}

Implement the “catch-all” errorHandler function as follows (for example):

function errorHandler (err, req, res, next) {
  res.status(500)
  res.render('error', { error: err })
}

If you have a route handler with multiple callback functions you can use the route parameter to skip to the next route handler. For example:

app.get('/a_route_behind_paywall',
  (req, res, next) => {
    if (!req.user.hasPaid) {
      // continue handling this request
      next('route')
    } else {
      next()
    }
  }, (req, res, next) => {
    PaidContent.find((err, doc) => {
      if (err) return next(err)
      res.json(doc)
    })
  })

In this example, the getPaidContent handler will be skipped but any remaining handlers in app for /a_route_behind_paywall would continue to be executed.

Calls to next() and next(err) indicate that the current handler is complete and in what state. next(err) will skip all remaining handlers in the chain except for those that are set up to handle errors as described above.

Handling Errors

Express.js makes it a breeze to handle errors in your routes.

Express lets you centralizes your error-handling through middleware.

Let’s look at patterns for how to get the most out of your error-handling.

First, our error-handling middleware looks like this:

app.use(function(err, req, res, next) {
  console.error(err.message); // Log error message in our server's console
  if (!err.statusCode) err.statusCode = 500; // If err has no specified error code, set error code to 'Internal Server Error (500)'
  res.status(err.statusCode).send(err.message); // All HTTP requests must have a response, so let's send back an error with its status code and message
});

Express.js interprets any route callback with four parameters as error-handling middleware. Our first parameter is err. You should put error-handling middleware at the end of your routes and middleware in your application. This let’s you catch any thrown errors.

Upstream, we can follow a simple pattern to help us understand and identify errors. Let’s look at some examples.

...

app.get('/forbidden', function(req, res, next) {
  let err = new Error(`${req.ip} tried to access /Forbidden`); // Sets error message, includes the requester's ip address!
  err.statusCode = 403;
  next(err);
});

...

Here’s how we can handle a forbidden request. To use our error-handling middleware we need two things.

First, we need to throw or create an Error. When we create our error, we can define a .message property by passing in a string as a parameter. This becomes our err.message, used later. Now we can attach a status code appropriate to the problem. We should give Forbidden requests the 403 status code.

The last and most important step is to pass a parameter into next(). If next receives an argument, Express will assume there was an error, skip all other routes, and send whatever was passed to any error-handling middleware you have defined.

Our next(err) is like saying ‘This is definitely an error. Go straight to error handling!’

Our middleware can now use .message and .statusCode to log, handle, and communicate the specifics of our error.

Generalized Error Handling

Obviously, we can’t specify routes like /forbidden for every possible broken or wrong route in our application.

How can we handle a broader set of errors?

Using the wildcard ‘*’ in our route, we can create a route to catch every request to a route we have not defined elsewhere.

...

app.get('*', function(req, res, next) {
  let err = new Error('Page Not Found');
  err.statusCode = 404;
  next(err);
});

...

This route will grab anything that isn’t routed elsewhere and throw a generic ‘404’ error.

But we can do even better.

Let’s refactor our route to make it tell Express that users shouldn’t just receive an error, but should be redirected to an error page.

...

app.get('*', function(req, res, next) {
  let err = new Error(`${req.ip} tried to reach ${req.originalUrl}`); // Tells us which IP tried to reach a particular URL
  err.statusCode = 404;
  err.shouldRedirect = true; //New property on err so that our middleware will redirect
  next(err);
});

...

Let’s spice things up a bit in our middleware…

...

app.use(function(err, req, res, next) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500; // Sets a generic server error status code if none is part of the err

  if (err.shouldRedirect) {
    res.render('myErrorPage') // Renders a myErrorPage.html for the user
  } else {
    res.status(err.statusCode).send(err.message); // If shouldRedirect is not defined in our error, sends our original err data
  }
});

...

Now our application will send any requests to nonsensical routes directly to an error page. On our server, we’ll still see error data.

That’s it. Remember:

  1. throw your Error with a useful message
  2. pass a parameter into next()
  3. centralize your errors in error-handling middleware!

Good luck!

Home  »  Express   »   Express JS Error Handling Tutorial with Best Examples

In this Express JS tutorial, we’ll briefly go over what express js is, how to install it and the most importantly how to handle errors in an Express app. Express.js allows us to catch and process both synchronous and asynchronous errors efficiently for any unexpected situation. Express offers middleware functions to deal with errors.

Table of contents

  1. Introduction Express JS
  2. Setting Up Express JS Project with Node
  3. Basic Express Error Handling Example
  4. Define Error Handling in Express App
  5. Express Wildcard 404 Error Handling
  6. Not to Be Followed Error Handling Approach in Express
  7. Best Approach For Error Handling in Express
  8. Using Error Handling with Async/Await
  9. Conclusion

Introduction Express Js

Express.js is a web application framework based on Node.js. The core philosophy of Express is based on Http Modules and connect components, which are known as middleware. Express is designed to build a robust web application and RESTful APIs. Express.js provides a high level of flexibility to web developers when it comes to building the latest web applications.

Setting Up Express JS Project with Node

Run command in terminal to create a basic Node and Express js project from scratch:

mkdir expressjs-error-handling

Enter into the project folder:

cd expressjs-error-handling

Open the project folder in the code editor.

Run the below command to create the package.json file for Express.JS error handling project.

npm init -y

Following will be the output displayed on your terminal:

{
  "name": "expressjs-error-handling",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Setting up Express Server

In this step, we set up express js server, but before that install Express js package by running the given below command:

npm install express

Now, create an app.js file in the project’s root folder. In this file, we are about to keep our app’s basic configurations.

touch app.js

Then, add the Express server configurations:

// app.js
const express = require('express'),
  PORT = process.env.PORT || 4000;
// Express Configuration
const app = express();
// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

Your node server is ready to be served, save your app.js file and run the following command.

node app
# node app
# Server is running at PORT: 4000

You can check out your basic express app on the following url: http://localhost:4000

Basic Express Error Handling Example

A web application is made of various services and databases. When a web application receives the data, that data is processed through various services in the backend. While the process is going on, any unexpected thing may occur. A database or service might get failed. A web application should be ready to handle such kind of situation, and this is where the error handlers come into the limelight.

Let us check out the basic error handling example in Express.

// Express middleware
app.get('/employees', async function(req, res) {
  let employees;
  try {
    employees = await db.collection('Employees').find().toArray();
  } catch (error) {
    // User friendly error response
    res.status(500).json({ 
      error: error.toString() 
    });
  }
  res.json({ employees });
});

To give you the demo of how does error handling work in Express. We declared the '/employees' endpoint. When users access '/employees' endpoint, then the try-catch block will be triggered. And, If any error shows up, then, it will throw the exception; otherwise, the employee’s data will be returned. This is just a brief error handling example for handling one or two endpoints in an Express middleware.

Define Error Handling in Express App

Now, we’ll learn to integrate error handling using Express functions which are known as middleware. A req, res, and next arguments are denoted by middleware in Express. In the below example, we’ll see how to use middleware to handle the errors.

app.use((err, req, res, next) => {
  res.status(500).send('Error found');
});

Express Wildcard 404 Error Handling

We can also define error handling for wildcard routes using '*' in our routes if a user tries to access the route which doesn’t exist in our Express app. Express will throw the error or in simple terms will be triggered a 404 error in this case.

app.get('*', function(req, res, next) {
  let err = new Error("Page Doesn't Exist");
  err.statusCode = 404;
  next(err);
});

However, we can make it better, by telling Express that a user should receive a 404 error and also must be redirected to the custom error page.

app.use(function(err, req, res, next) {
  console.error(err.message);
  
  // Define a common server error status code if none is part of the err.
  if (!err.statusCode) err.statusCode = 500; 
  if (err.shouldRedirect) {
    // Gets a customErrorPage.html.
    res.render('customErrorPage')
  } else {
    // Gets the original err data, If shouldRedirect is not declared in the error.
    res.status(err.statusCode).send(err.message);
  }
});

Not to Be Followed Error Handling Approach in Express

If we throw the errors asynchronously in the same function, then It’ll inevitably crash the server every time an HTTP request is made. Due to the JavaScript’s default asynchronous nature.

// app.js
const express = require('express'),
  PORT = process.env.PORT || 4000;
// Express settings
const app = express();
// Express Error Handling
app.get('*', (req, res, next) => {
  setImmediate(() => {
    throw new Error('Something is really not good!');
  });
});
app.use((error, req, res, next) => {
  // This error can't be displayed
  res.json({
    message: error.message
  });
});
// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

Best Approach For Error Handling in Express

Now, I’ll show you the best approach to handle errors in Express js. The next middleware is helpful when it comes to defining errors accurately in Express. This argument is passed in the route handler, as mentioned below.

// app.js
const express = require('express'),
  PORT = process.env.PORT || 4000;
// Express settings
const app = express();
// Express Error Handling
app.get('*', (req, res, next) => {
  // Error goes via `next()` method
  setImmediate(() => {
    next(new Error('Something went wrong'));
  });
});
app.use((error, req, res, next) => {
  // Error gets here
  res.json({
    message: error.message
  });
});
// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

The vital point to be considered here is that the Express js middlewares execute in an orderly manner, we must define the error handlers after all the other middleware functions. If you don’t follow this process, then your handlers won’t work as expected.

Using Error Handling with Async/Await

Most of the Express js was created before EcmaScript 6 in between year 2011 till 2014. I know this is not the best approach to handle errors with async/await. However, error handling can also be made possible with JavaScript’s async/await approach with the help of a custom asyncHelper middleware function.

// app.js
const express = require('express'),
  PORT = process.env.PORT || 4000;
// Express settings
const app = express();
// Error Handling Helper Function
function asyncHelper(fn) {
  return function (req, res, next) {
    fn(req, res, next).catch(next);
  };
}
// Express Error Handling async/await
app.get('*', asyncHelper(async (req, res) => {
  await new Promise(resolve => setTimeout(() => resolve(), 40));
  throw new Error('Error found');
}));
app.use((error, req, res, next) => {
  res.json({
    message: error.message
  });
});
// Set up PORT
app.listen(PORT, () => {
  console.log('Connected to port ' + PORT)
})

As you can see in the example, we bind a custom helper function asyncHelper with async/await. It is playing a vital role in Express error handling as a middleware. The async function returns a promise, we used the .catch() method to get the errors and pass it to the next() method.

Conclusion

Finally, we have completed the Express error handling tutorial with real-world examples. I hope you liked the examples I mentioned in this tutorial. I have talked about every possible situation of error-handling you might face while developing a real-world express application. However, if I have skipped anything do let me know. It will help us to enhance my knowledge as well. Thanks for reading, have a good day.

Recommended Posts:

It is always good to display custom error page on error
conditions. For example, I had written simple application, that displays
welcome message in home page.

index.js

const express = require('express')

const app = express()

const port = 8080

app.get('/', (req, res) => res.send('Welcome to home page'))

app.listen(port, () => console.log(`Application started listening on port ${port}!`));

Run index.js using the command ‘node index.js’. Open the
url ‘http://localhost:8080/’, in browser, you can see below message in browser.

Try to access the url ‘http://localhost:8080/welcome’,
you can see default message from node.js.

But in many cases, we want to display custom error page
that gives more information to the user.

How can we build
custom error message?

Add below statement to the end of all the routes and
middlewares.

app.use((req, res) => {

         return
res.send(‘Your request does not match to any of our services’)

})

If no request matches to existing routes and middlewares,
it is fallback to this.

index.js

const express = require('express')

const app = express()

const port = 8080

app.get('/', (req, res) => res.send('Welcome to home page'))

app.use((req, res) => {
 return res.send('Your request does not match to any of our services')
})

app.listen(port, () => console.log(`Application started listening on port ${port}!`));

Run index.js and open the url ‘http://localhost:8080/welcome’
in browser, you can see below message.

In real world applications, it is nicer to send the
custom error page (That contains error status code, message, stack trace if the
application is running in development mode) than the plain message.

We can do that by using some custom template engine.

Follow step-by-step procedure to build complete working
application.

Step 1: Create new
folder ‘errorPageDemo’.

Go inside errorPageDemo and execute the command npm init
-y. it creates package.json file.

C:UsersPublicerrorPageDemo>npm init -y
Wrote to C:UsersPublicerrorPageDemopackage.json:

{
  "name": "errorPageDemo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Step 2: We need to
add the express and http-errors dependency to our project.

Execute below commands from the directory where your
package.json located.

npm install —save http-errors

npm install —save express

After executing above two commands, package.json file is
updated like below.

{
  "name": "errorPageDemo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4",
    "http-errors": "^1.7.1"
  }

Step 2: Create a
folder views and define the file ‘errorPage.ejs’.

errorPage.ejs

<html>

 <head>
  <title>Express and EJS integration</title>
  <meta charset="utf-8">
  
  <style>
     h3 {
    font-family: georgia, serif; 
    font-size: x-small;
    color : red
   }

 </style>

 </head>
 
 <body>
  <h3>
   Message : <%= message %> <br /> <br />
   
   Error Code <%= errorCode %> <br /> <br />
   
   Trace: <%= stackTrace %>
  </h3>
 </body>
</html>

Step 3:

Create index.js file in the same directory where your
package.json is located.

index.js

//Load express module
const express = require('express');
const path = require('path');
const createError = require('http-errors')

//Put new Express application inside app variable
const app = express();

//Set views property and views engine
app.set("views", path.resolve(__dirname, "views"));
app.set("view engine", "ejs");

const port = 8080;

app.get('/', (req, res) => {
 res.send('Welcome to node.js programming')
})


app.use((req, res, next) => {
 return next(createError(404, 'Request not implemented (or) file not found'))
});

app.use((err, req, res, next) => {
 
 // Other than 404, we are returning internal server error (500)
 errorStatus = err.status || 500
 
 errorStackTrace = req.app.get('env') === 'development' ? err : {}
 errorMessage = err.message || "Can't process the request"

 res.locals.status = errorStatus
 
 return res.render("errorPage", {
  message: errorMessage,
  errorCode: errorStatus,
  stackTrace : stringifyError(errorStackTrace, null, 't')
 })

})

// Start the express application on port 8080 and print server start message to console.
app.listen(port, () => console.log(`Application started listening on port ${port}!`));

var stringifyError = function(err, filter, space) {
  var plainObject = {};
  Object.getOwnPropertyNames(err).forEach(function(key) {
    plainObject[key] = err[key];
  });
  return JSON.stringify(plainObject, filter, space);
};

Run index.js, and hit the url ‘http://localhost:8080/welcome’,
you can see below page.

Previous                                                

Next                                                
Home

Понравилась статья? Поделить с друзьями:
  • Express default error handler
  • Express create error
  • Express async error handler
  • Exprelia ошибка e05
  • Exposure lamp error перевод