NodeJs Express Response Handler - node.js

I am new to express (or any JS backend) so sorry if question was already answered, or kind of stupid.
I have registered endpoint.
app.get('/hello-world'), async (req, res) => {
try {
// do something
sendResponse({"Message": "Hello World"}, res);
} catch (e) {
handleError(e, res);
}
});
Where sendResponse and handleError are doing just setting status and body / additional exception metadata using res.status().json()
Is there any way to make response handling more simple by registering some response handler and write the logic of response / exception handling at one place?
What I have in mind is this:
Change example endpoint logic to:
app.get('/hello-world'), async (req, res) => {
return {"Message": "Hello World"}
// or throw new error
});
and some repsonse handler which will handle result of function
resposeHandler(payload, err, res) {
if (err) {
res.status(500).json(err) // just as an example
} else {
res.status(200).json(payload)
}
}

You can create a function wrapper to catch all the errors and send them to the error middleware:
const errorHandler = (routeHandler) =>
(req, res, next) => {
const routeHandlerReturn = routeHandler(req, res, next)
return Promise.resolve(routeHandlerReturn).catch(next)
}
Then you can reutilize it in all your controllers, making the app much cleaner:
app.get('/hello-world', errorHandler(async function(req, res, next) {
sendResponse({"Message": "Hello World"}, res);
});
If some error is thrown, it will be handled in your error handler middleware:
// index.js
app.use((err, req, res, next) => {
res.status(err.status || 500)
res.json({
message: err.message || 'Internal Error',
error: err.error
})
})

create two middlewares, one for error handle and the other one for success response
// error handling when error occured
function errorHandler(req,res,next) => {
return res.status(err.status || 500).json({
success: false,
message: err.message
});
};
// success response and return data
function successHandler(successMsg, successData) => {
return (req,res,next) => {
return res.status(200).json({
success: true,
message: successMsg,
data: successData
});
};
};
register them in express
const app = express();
app.get('/someroute', successHandler('it is endpoint of someroute!', 'your data here'))
app.use(errorHandler)
use errorHandler after you call and define the route

Related

When catching errors in Express route how do I bypass first res.render to avoid error, "Cannot set headers after they are sent to the client"?

Background: The simplified test code below uses Express and Mongoose.
Question: I set up the .then statement to throw an error for testing. When an exception is thrown my error handling middleware is triggered with next() but not before res.render('index', { doesUserExist }); is hit. This line results in the error, "Cannot set headers after they are sent to the client" because in my error handling middleware res.render('error_page', { err }); is also called. What part of my code should I change to eliminate the error?
Followup: Do I need more than a slight shift in my approach? Am I using the completely wrong pattern to perform this action efficiently/effectively?
app.get('/', function(req, res, next) {
(async function() {
let doesUserExist = await User.exists( { name: 'steve' })
.then( function(result) {
throw 'simulated error';
})
.catch( function(error) {
next(new Error(error));
});
res.render('index', { doesUserExist });
})();
});
app.use(function(err, req, res, next) {
res.render('error_page', { err });
});
This is because of an async function without a catch block
app.get('/', function (req, res, next) {
(async function () {
try {
let doesUserExist = await User.exists( { name: 'steve' });
if (doesUserExist) {
throw 'simulated error';
} else {
next(new Error(error));
}
res.render('index', { doesUserExist });
} catch (err) {
return next(err)
}
})();
});
app.use(function (err, req, res, next) {
res.render('error_page', { err });
});
Instead of next write return next(new Error(error)). In this way it wont execute any further code and go to the error middleware
You can create a function wrapper to catch all the errors and send them to the error middleware:
const asyncWrap = fn =>
function asyncUtilWrap (req, res, next, ...args) {
const fnReturn = fn(req, res, next, ...args)
return Promise.resolve(fnReturn).catch(next)
}
Then you can reutilize it in all your controllers, making the app much cleaner:
app.get('/', asyncWrap(async function(req, res, next) {
let doesUserExist = await User.exists( { name: 'steve' }) //*
.then( function(result) {
throw 'simulated error'; // This error is automatically sent to next()
})
.catch( function(error) {
next(new Error(error)); // This error works normally
});
res.render('index', { doesUserExist });
});
*You shouldnt combine await and then/catch syntax by the way.

Can I make an exception for a route in Express?

Following is the code I am referring to,
axios.get('http://localhost:1337/')
.then( response => {
var dataSet = response.data;
// URL Handle for Data page
app.get('/data', (req, res) => {
res.render('data', {
data = dataSet
});
});
})
.catch( error => {
console.log('An error occurred: ' + error);
});
// URL Handle for Contact page
app.get('/contact', (req, res) => {
res.render('contact');
});
// URL Handle for Error page
app.all('*', (req, res) => {
res.render('error');
});
Now, as I am using Axios for fetching the response from the database, I am also rendering the data in that desired page where I want the response of the API.
But there's a problem when I am using "app.all" handler for the wrong URL handle, it redirects to the error page.
I need a solution in which there can be an exception which can be added to the "app.all" handler and whenever the handle is '/data' it does not redirect to '/error'.
Looks like your route for /data is registered after the async call of axios is completed.
Try, putting axios call inside route handler
app.get('/data', (req, res) => {
axios.get('http://localhost:1337/').then( response => {
var dataSet = response.data;
res.render('data', {
data: dataSet
});
})
.catch( error => {
console.log('An error occurred: ' + error);
});
});
// URL Handle for Contact page
app.get('/contact', (req, res) => {
res.render('contact');
});
// URL Handle for Error page
app.all('*', (req, res) => {
res.render('error');
});
You should use axios in controller, so code should look like this:
app.get('/data', async (req, res, next) => {
try{
const { body } = await axios.get('http://localhost:1337/')
res.render('data', {
data : body
});
}catch(error){
next(error)
}
});
As You see, if there is error You should call next function with error instance and then create middleware as errorHandler to catch it.

Nodejs Express Forward Route Caught Errors To Middleware

I am trying to understand how to forward errors that occur in routes in a nodejs express application back to the error handler without throwing the error.
Basically I have my middleware setup like this:
const allowedMethods = ['GET', 'POST'];
app.use(function (req, res, next) {
if (!allowedMethods.includes(req.method))
return res.sendStatus(405);
});
let paymentRoutes = require('./routes/payment');
app.use('/payment', paymentRoutes);
let notificationRoute = require('./routes/notifications');
app.use('/notifications', notificationRoute);
let customerRoutes = require('./routes/customer');
app.use('/customer', customerRoutes);
let subscriptionRoutes = require('./routes/subscription');
app.use('/subscription', subscriptionRoutes);
app.use(function (err, req, res, next) {
console.log("error occured!");
console.log(err);
if (err) {
console.log("Throwing an error!");
next(err);
} else {
console.log("no error...")
}
});
app.use(function logErrors (err, req, res, next) {
winston.error(err);
next(err);
});
app.use(function sendMailToAdmin(err, req, res, next) {
//sendmailtoadmin
});
And it is working whenever I throw an error in one of my routes. Currently I am handling errors like this in my routes though.
router.get('/:id/has-premium', (req, res) => {
//throwing a test error
throw new Error;
const userId = req.params.id;
customer.hasActivePremium(userId)
.then(activePremium => {
res.status(200).send(JSON.stringify({"success": true, "premium": activePremium}));
})
.catch(err => {
res.status(err.httpCode).send(JSON.stringify({"success": false, "error": err.description}));
})
});
My question is how would I "call" my middleware error handler with the error I caught when I caught the promise?
You can use next in the route signature like
router.get('/:id/has-premium', (req, res,next) => {
//throwing a test error
throw new Error; // you can call next() here
const userId = req.params.id;
customer.hasActivePremium(userId)
.then(activePremium => {
res.status(200).send(JSON.stringify({"success": true, "premium": activePremium}));
})
.catch(err => {
res.status(err.httpCode).send(JSON.stringify({"success": false, "error": err.description}));
})
});
and then call next(err) instead of res.status(err.httpCode).send(JSON.stringify({"success": false, "error": err.description}));

NodeJS Express catch-all route firing after previous route

I have a basic express setup that includes the following:
app.all('/api/:controller/:id?', [ api, response.success, response.error ])
app.get('*', (req, res) => {
console.log('This should only fire when no route matched')
res.sendFile(path.resolve(pubRoot, 'index.html'))
})
In the code for the middleware on the first route the api module simply resolves a promise at this point (or rejects on 404) and then calls next, this makes it into the response middleware which looks like this:
const response = {
success: (req, res, next) => {
res.status(200).send(res.data)
},
error: (error, req, res, next) => {
const statusCode = error.statusCode || 500
res.status(statusCode).send(error.message || 'Internal Server Error')
}
}
This works fine on a successful request and I get the data (and status code 200) back. It also works on error (returning 404 and Not Found as expected), however, it continues on to the wildcard route. I get the console.log along with Error: Can't set headers after they are sent.
UPDATE: Below is the code for the api module:
const api = (req, res, next) => {
const controller = req.params.controller
const event = { body: req.body, id: req.params.id, query: req.query }
// Handle non-existent controller
if (!controllers[controller]) {
next(new HTTPError(404))
return
}
// Controller found, process
controllers[controller][req.method.toLowerCase()](event)
.then((result) => {
res.data = result
next()
})
.catch((err) => {
next(err)
})
}

A generic catch-version for exceptions?

I have a sequelize database that validates data and throws errors.
I know I can do something like this to catch and output my errors:
User.build()
.catch(Sequelize.ValidationError, function (err) {
// respond with validation errors
return res.status(422).send(err.errors);
})
.catch(function (err) {
// every other error
return res.status(400).send({
message: err.message
});
But I don't want to add it to every single request, is there some generic way to catch theese errors?
You can add a custom method to req (or res) that will resolve the promise and handle any errors:
app.use((req, res, next) => {
req.resolve = (promise) => {
return promise.catch(Sequelize.ValidationError, err => {
// respond with validation errors
return res.status(422).send(err.errors);
}).catch(err => {
// every other error
return res.status(400).send({ message: err.message });
});
});
next();
});
Usage (provided that the middleware above is added before your routes):
router.post('/user', (req, res) => {
req.resolve(User.build()).then(user => res.json(user));
});
ES.next version (2016):
you can use async functions that throw using this wrapper function copied from the official strongloop website:
let wrap = fn => (...args) => fn(...args).catch(args[2]);
then make the function in your router/controller like that:
router.post('/fn/email', wrap(async function(req, res) { ...throw new Error(); }
and finally have a normal catch all errors middleware:
app.use(function(err, req, res, next) { console.log(err); }
Obviously for this to work you need the babel transpiler currently

Resources