Error Handling in 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.
- Synchronous error handling
- Asynchronous error handling
Handling synchronous errors
If you want to handle a synchronous error, you can throw the error in an Express controller.
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('/users/:id', (req, res) => {
const userId = req.params.id
if (!userId) {
return res.sendStatus(400).json({
error: 'Missing id'
})
}
Users.get(userId, (err, user) => {
if (err) {
return res.sendStatus(500).json(err)
}
res.send(users)
})
})
These errors can be caught with an Express error handler. If you did not write a custom error handler , Express will handle the error for you with a default error handler, the default error handler will be set the http status to 500
Handling asynchronous errors
If you want to handle an asynchronous error, you need to send the error into an express error handler through the next argument.
app.get('/users/:id', (req, res,next) => {
const userId = req.params.id
if (!userId) {
const error = new Error(“Missing Id”)
error.httpStatusCode = 400
return next(error)
}
Users.get(userId, (err, user) => {
if (err) {
err.httpStatusCode = 500
return next(err)
}
res.send(users)
})
})
Writing a custom error handler
Express error handlers take in four arguments:
- error
- req
- res
- next
They must be placed after all your middlewares and routes.
app.use(/*...*/)
app.get(/*...*/)
app.post(/*...*/)
app.put(/*...*/)
app.delete(/*...*/)
// Place your error handler after all other middlewares
app.use((error, req, res, next) => { /* ... */ })
Express will stop using its default error handler once you create a custom error handler. To handle an error, you need to communicate with the frontend that’s requesting the endpoint. This means you need to:
- Send over a valid HTTP status code
- Send over a valid response
A valid HTTP status code depends on what happened.
Here’s a list of common errors you should prepare for:
400 Bad Request Error:
Used when user fails to include a field (like no credit card information in a payment form)
Also used when a user enters incorrect information (Example: Entering different passwords in a password field and password confirmation field).
401 Unauthorized Error:
Used when a user enters incorrect login information (like username, email or password).
403 Forbidden Error:
Used when the user is not allowed access to the endpoint.
404 Not Found Error:
Used when the endpoint cannot be found.
500 Internal Server Error
The request sent by the frontend is correct, but there was an error from the backend.
Once you determined the correct HTTP status code, you want to set the status with res.status
app.use((error, req, res, next)=>{
//Bad request error
res.status(400)
res.json(/*….codes goes here …/*) })
})
The HTTP status code should match the error message. For the status code to match the error message, you must send the status code together with the error.
The easiest way is to use the http-errors package. It lets you send three things in your errors:
- A status code
- A message to go with the error
- Any properties you’d like to send. This is optional.
Installing http-errors:
npm install http-errors --save
Using http-errors:
const createError = require('http-errors')
// Creating an error
throw createError(status, message, properties)
Let’s work through an example together to make it clearer. Let’s say you tried to find a user by their email address. The user cannot be found. You want to throw an error that says “User not found”.
When you create the error, you want to:
- Send a 400 Bad Request Error (because the user filled in incorrect information). You send this as the first parameter.
- Send a message that says “User not found”. You send this as the second parameter.
app.put('/testing', asyncHandler(async (req, res) => {
const { email } = req.body
const user = await User.findOne({ email })
// Throws error if user not found
if (!user) throw createError(400, `User '${email}' not found`)
}))
You can get the status code with error.status and the error message with error.message.
//Logging the error
app.use((error, req, res, next) => {
console.log('Error status: ', error.status)
console.log('Message: ', error.message)
})
Then, you set the error status with res.status. You send the message with res.json.
app.use((error, req, res, next) => {
// Sets HTTP status code
res.status(error.status)
// Sends response
res.json({ message: error.message })
})
Personally I like to send the status, the message, and the stack trace for me to debug easily.
app.use((error, req, res, next) => {
// Sets HTTP status code
res.status(error.status)
// Sends response
res.json({
status: error.status,
message: error.message,
stack: error.stack
})
})
This is all about the error handling for for details please go through the link mentioned below :
- https://nemethgergely.com/error-handling-express-async-await/
- https://thecodebarbarian.com/80-20-guide-to-express-error-handling
- http://expressjs.com/en/guide/error-handling.html#:~:text=Error%20Handling%201%20Catching%20Errors.%20It%E2%80%99s%20important%20to,in%20the%20app.%203%20Writing%20error%20handlers.