Can I make an exception for a route in Express? - node.js

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.

Related

Handle exceptions on every Express route

I would like some basic error handling on every route, so if there is ever an exception, the API at least responds with 500.
According to this pattern, you still need to include a try/catch block in every route:
app.post('/post', async (req, res, next) => {
const { title, author } = req.body;
try {
if (!title || !author) {
throw new BadRequest('Missing required fields: title or author');
}
const post = await db.post.insert({ title, author });
res.json(post);
} catch (err) {
next(err) // passed to the error-handling middleware
}
});
That seems a little repetitive. Is there a higher-level way where exceptions are automatically caught everywhere and passed to the middleware?
I mean, it would obviously be possible for me to just define my own appGet():
function appGet(route, cb) {
app.get(route, async (req, res, next) => {
try {
await cb(req, res, next);
} catch (e) {
next(e);
}
});
}
Is there some built in version of this?
You can use express-promise-router package.
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.
E.g.
app.ts:
import express from 'express';
import Router from 'express-promise-router';
import bodyParser from 'body-parser';
const router = Router();
const app = express();
const port = 3000;
app.use(bodyParser.json());
app.use(router);
router.post('/post', async (req, res) => {
const { title, author } = req.body;
if (!title || !author) {
throw new Error('Missing required fields: title or author');
}
const post = { title, author };
res.json(post);
});
router.use((err, req, res, next) => {
res.status(500).send(err.message);
});
app.listen(port, () => console.log(`Server started at http://localhost:${port}`));
You don't need try/catch statement block anymore.
Test result:
I think the better approach would be to divide the services and the controllers which is demonstrated below.
Add post service:
async function addPostService (title, author) => {
if (!title || !author)
throw new BadRequest('Missing required fields: title or author');
return await db.post.insert({ title, author });
};
Add post controller:
function addPost(req, res, next){
const { title, author }= req.body;
addPostService
.then((post) => {
res.json(post);
})
.catch(next) // will go through global error handler middleware
}
Now, we can make a global error handler middleware which will catch the error thrown by any controller throughout the app.
function globalErrorHandler(err, req, res, next){
switch(true){
case typeof err === 'string':
// works for any errors thrown directly
// eg: throw 'Some error occured!';
return res.status(404).json({ message: 'Error: Not found!'});
// our custom error
case err.name = 'BadRequest':
return res.status(400).json({ message: 'Missing required fields: title or author!'})
default:
return res.status(500).json({ message: err.message });
}
}
And, don't forget to use the error handler middleware right before starting the server.
// ....
app.use(globalErrorHandler);
app.listen(port, () => { console.log('Server started...')});

NodeJs Express Response Handler

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

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.

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)
})
}

Resources