Error-handling middleware
Basic Syntax
let convertRupees = (dollar) => {
if(typeof dollar === 'number') {
return dollar * 69
} else {
throw Error ('Amount must be in number')
}
}
try {
console.log(convertRupees('one'));
} catch (error){
console.error(error)
}
console.log('If we used try...catch..This line will show even if error raised ')
An Express application can use any of the below mentioned types of middleware:
-
Application-level middleware : app.use
-
Router-level middleware : router.use
-
Error handling middleware app.use(err,req,res,next)
-
Built-in middleware : express.static,express.json,express.urlencoded
-
Third-party middleware : bodyparser,cookieparser
Error-handling middleware will have four arguments, the forth next will identify it as an error-handling middleware. the signature is (err, req, res, next). Express error handling middle-ware is very helpful in catching globally occurring errors without worrying about the Server to crash.
Example Without Error-handling Middleware
var express = require('express');
var app = express();
app.use(function(req,res,next) {
console.log("In second route");
next(); // go to next route.
});
app.get('/', function(req,res){
res.send('Welcome NodeJs !');
})
server = app.listen(3000, function() {
console.log('Listenint Port : 3000 ');
});
Example Error-handling Middleware
To generate the run-time error, we will throw random error and see whether it catches it or not.
var express = require('express');
var app = express();
app.use(function(req,res,next) {
console.log("In second route");
next(); // go to next route.
});
app.get('/', function(req,res){
throw new Error(); // Express will catch this on its own.
res.send('Welcome NodeJs !');
})
app.use(function(err,req,res,next) {
console.log("Error happens",err.stack);
res.status(500).send({"Error Happen in the Request : " : err.stack});
});
server = app.listen(3000, function() {
console.log('Listenint Port : 3000 ');
});
Third-party Middlewares
In some cases we will be adding some extra features to our backend
Install the Node.js module for the required functionality, then load it in your app at the application level or at the router level
Example : body-parser
This third party middleware will Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
To install run the below command
npm install body-parser
Example : app.js
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())
app.post('/save',(req,res)=>{
res.json({
"status":true,
"payload":req.body
})
})
var server = app.listen(3000, function() {
console.log('Listenint Port : 3000 ');
});
Run the command in node terminal
node app.js
And browse the path in postman : “http://localhost:3000/save”
Output
{
"status": true,
"payload": {}
}
Handling 404 errors with Router middleware
when a route does not match with any handler then it will throw 404 Error. To do this we will put a middle-ware at the end of all routes, it will be executed when none of the routes match.
var express = require("express");
var app = express();
//Creating Router() object
var router = express.Router();
// Router middleware, mentioned it before defining routes.
router.use(function (req, res, next) {
console.log("/" + req.method);
next();
});
router.use("/user/:id", function (req, res, next) {
console.log('Passed Value : ' + req.params.id)
if (req.params.id == 0) {
res.json({ "message": "Pass ID other than 0" });
}
else next();
});
// Provide all routes here, this is for Home page.
router.get("/", function (req, res) {
res.json({ "message": "Welcome Express Router" });
});
router.get("/user/:id", function (req, res) {
res.json({ "message": "Express Router with Param " + req.params.id });
});
// Handle 404 error.
// The last middleware.
app.use("*", function (req, res) {
res.status(404).send('404');
});
// Tell express to use this router with /api before ex - http://localhost:3000/api .
app.use("/api", router);
// Listen to this Port
app.listen(3000, function () {
console.log("Listening Port : 3000");
});
Error Handling
We’re distinguishing between errors in development and in production. When we’re in production, we send the error using this function here, which will then send as many details as possible to the client, so that we really get all the information in order to get rid of the bug. If it’s a programming error, or if it’s an operational error, then we still really want to see anything that’s going on.
When we’re in production, which is arguably the most important part of our application, then we distinguish between operational errors, so errors that we know and trust, and to other errors, that might be kind of unexpected.
If the error is operational, so for example
1.the user tried to access a route that doesn’t exist,
2.or tried to input invalid data
all of these are operational errors.Then we can send appropriate error messages, for the client to know what went wrong.
On the other hand, we have these unknown errors/unexpected errors
For example : Coming from MongoDB, which we do not mark as operational. In this case, they would right now simply be handled using this generic error message here. For example, a validation error. Right now, that’s an error that’s coming from MongoDB and not from our own app error class. We do not create these errors by ourselves. Again, they are right now not marked as operational, but we of course need to mark them as operational so that we can then send the appropriate error message back to the client. In the example that I was just mentioning, that the input data is invalid. There are two or three other errors that we need to mark as operational ourselves. So we will do that down here. So in this else block, we will do that over the next couple of lectures.
Three type of Errors in MongoDB : 400 – Bad Request
1. Invalid ID (We have to show it as Operational Errors) – (error.name === ‘CastError’)
2. Duplicate Name In DB (error.code == 1100)
3. ValidationError [Max Value] – (error.name == ‘ValidationError’)
500 – Internal Server Error
Steps:
=======
We can directly create the middleware in “index.js”
app.all('*', (req, res, next) => {
res.status(404).json({
status: 'fail',
message: `Can't Find URL${req.originalUrl} on the server`
})
// const err = new Error(`Can't find ${req.originalUrl} on this server !`)
// err.status = 'fail';
// err.statusCode = 404;
// next(err);
})
app.use((err, req, res, next)=> {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
})
2. we’re gonna build a couple of different functions for handling with different types of errors,and so I want all of these functions to be all in the same file, all right ? Controlling errors (appController.js)
# move the middleware from index.js to appController.js
module.exports = (err, req, res, next)=> {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
}
4. import “appController” into the “index.js” file
const globalErrorHandler = './appController.js'
app.all('*', (req, res, next) => {
next(new AppError(`Can't Find URL${req.originalUrl} on the server`, 404));
})
app.use(globalErrorHandler)
3. Add the AppError Class
class AppError extends Error {
constructor(message, statusCode){
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail':'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = AppError;
4. import “AppError” into the “index.js” file
app.all('*', (req, res, next) => {
next(new AppError(`Can't Find URL${req.originalUrl} on the server`, 404));
})
5.Modify the async await functions
# async function return promises so receive the error in catch part.
const catchAsync = fn => {
return (req,res,next) => {
fn(req, res, next).catch(err=> next(err))
}
}
export.createTour = catchAsync(async(req,res,next) => {
const newTour = await Tour.create(req.body)
res.statu(201).json({
status: 'Success',
data: {tour: newTour}
})
})
6. Move the catchAsync to new file
module.exports = catchAsync = fn => {
return (req,res,next) => {
fn(req, res, next).catch(err=> next(err))
}
}
7. Import catchAsync into controller.js and use it