Async Error handling Wrapper does not work - javascript

I am trying to write a wrapper for express callback function displayUsers() in this case, it would append error handling logic to avoid using try catch everywhere.
The main issue is that fn() actually executes before being invoked by router.get
I am not sure why as I am returning it within a function, not executing it.
///Userroute.js
var router = require('express').Router();
var userModel = require('../models/user');
var asyncErrorHandler = require('../helpers/asyncErrorHandler');
var viewsDir = '../views/';
// get users from model
var displayUsers = async function(req, res, next) {
var users = await userModel.getUsers();
console.log(users);
res.render(`${viewsDir}/users.hbs`, {users:users})
};
var safeDisplayUsersfn = asyncErrorHandler(displayUsers);
router.get('/', safeDisplayUsersfn);
//asyncErrorHandler.js
module.exports = function (fn) {
return async function(req, res) {
try{
await fn();
}catch(error){
console.log('Error happened' + error);
res.status(500).send('Unexpected Error');
}finally{
}
}
}

fn() was executing correctly, what needed to be done is to pass parameters to the executing function: fn(req,res,next); Like:
module.exports = function (fn) {
return async function(req, res, next) {
try{
await fn(req, res, next);
}catch(error){
console.log('Error happened' + error);
res.status(500).send('Unexpected Error');
}finally{
}
}
}

Related

How do I create a catchall async error handler in express?

I am using express.js and I am trying to create a catchall async error handler.
Say I have three routes which all may throw error,
const app = express()
app.get('/user', async function(req,res) {
const res = await getUsers()
})
app.get('/questions', async function(req,res) {
const res = await getQuestions()
})
app.get('/answers', async function(req,res) {
const res = await getAnswers()
})
in these three routes, all getXX function might throw error.
I want to have just one async handler for all routes. Something like this
app.use(asyncHandler)
so I don't have to try/catch every place that may throw error. Is there any solution for this?
Thanks in advance!
It's best practice to write async/await in try/catch block. because await only takes resolve output from promise if error throw from await function it handles by the catch block.
In catch block next(error) called which go throw all following middleware until it is not found error handling minddler.
const app = require("../../Reference/node-post-demo/app")
app.get('/user', async function (req, res, next) {
try {
const res = await getUsers();
return res.json(res);
} catch (error) {
next(error)
}
})
app.get('/questions', async function (req, res, next) {
try {
const res = await getQuestions()
return res.json(res);
} catch (error) {
next(error)
}
})
app.get('/answers', async function (req, res, next) {
try {
const res = await getAnswers()
return res.json(res);
} catch (error) {
next(error)
}
})
app.use(async (error, req, res) => {
//here, your error hendling code
})

My variable is always undefined, function returning before awaiting the query

const express = require('express')
const usersJs = require('./endpoints/users')
var cors = require('cors')
const app = express()
app.use(cors())
app.use(express.json());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
app.post('/addUser', (req, res) => {
result = usersJs.doAddUser().then(result => console.log(result))
res.json(result)
})
This is my server which is doing the call of doAdduser() from another file as you see bellow...
let stuff_i_want
module.exports.doAddUser = () => {
return addUser(async function (result) {
stuff_i_want = result;
return stuff_i_want
});
}
addUser = async (callback) => {
const mysql = require('mysql')
const connection = mysql.createConnection({
host: "localhost",
port: "",
user: "",
password: "",
database: ""
})
connection.connect(async function (err) {
if (err) throw err;
connection.query("SELECT * FROM USERS", async function (err, result) {
if (err) throw err;
stuff_i_want = result
});
});
return callback(result)
}
but the problem that result is always undefined in when im trying to res.json() it to the client, or when im trying console.log(result), its happening before addUser() which will make it undefined, tried with delay, with async await, but still result undefined in client and server but in doAdduser i can see the result from the database
I wanna be honest with you, this code looks super messi. However i have an solution for you.
You try to get the result outside the promise chain, that dont work. I would use the modern way async / await
const express = require('express')
const { addUser } = require('./endpoints/users')
var cors = require('cors')
const app = express()
app.use(cors())
app.use(express.json());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
app.post('/addUser', async (req, res) => {
try {
let result = await addUser();
return res.status(200).json(result);
} catch (err) {
console.log(err);
return res.status(500).json({ message: "Something went wrong" })
}
})
Also wrap it with an try / catch
Next to your mysql connection:
const mysql = rquire("mysql");
module.exports.addUser = () => {
return new Promise((resolve, reject) => {
const connection = mysql.createConnection({
host: "localhost",
port: "",
user: "",
password: "",
database: ""
});
connection.connect(err => {
if (err) reject(err);
connection.query("SELECT * FROM USERS", (err, result) => {
if (err) return reject(err);
return resolve(result);
});
});
});
};
import it on the top not somehwere in the middle of the code this is just confusing.
export a named module called addUser witch is an function.
That function returns an promise.
Then reject on errors and resolve if you got an result from your DB.
The try / catch block will catch the error.
Also add an status code to your response. I am not sure here i might be wrong but no status code means your request was successful even if something went wrong on the serverside
Its also good to have an router folder where you add all your routes and import the route handlers in there that looks something like this then:
const { addUserHandler } = require("./user");
router.post("/addUser", addUserHandler)
So routes and the logic are seperated and it doesnt look messi

How do I return status 500 and stop running the script for that route

If an error occurs in a custom function, I would like to stop the script from running and return the response with a 500 error.
However my script is running the error, and later continuing the script.
./helpers.js
const requiredEnv = (vars, callback) => {
const unsetEnv = vars.filter((varName) => !(typeof process.env[varName] !== 'undefined'));
let error;
if (unsetEnv.length > 0) {
error = "Required ENV variables are not set: [" + unsetEnv.join(', ') + "]";
}
callback(error);
}
module.exports = { requiredEnv };
./route.js
const express = require('express');
const router = express.Router();
const requiredEnv = require('./requiredEnv');
router.post('/', (req, res) => {
requiredEnv(['MY_VAR'], (error) => {
console.log('this is an error shown in the console');
res.status(500).send({ error });
// should stop this script here...
});
// somehow it continues here, even though the previous callback function is returning an error
console.log('this is continued');
res.status(200).send({ message: 'Message sent' });
});
module.exports = router;
I've tried next() but that did not work.
update
I almost have it working. (thanks #oooyaya & #ayush)
const middlewareFoo = function(_, res, next) {
requiredEnv(['MY_VAR'], function (error) {
if (error) {
// ✓ it stops when MY_VAR is not present
return res.status(500).send({ error });
} else {
// ✗ when MY_VAR is defined, it does not continue...
console.log('continue if no error...');
next()
}
});
}
router.use(middlewareFoo);
router.post('/', (req, res) => {
// ... left out for brevity
But if there's no error it's not working. Similar issue I guess.
Untested, but this will likely get you closer. The problem is that you're running asyncy code and so the res.send(200) happens before the callback to the requiredEnv() callback. They need to live within the same callback and you can check if there was an error or not. If so, res.status(500). If not, res.status(200).
const express = require('express');
const router = express.Router();
router.post('/', (req, res, next) => {
requiredEnv(['MY_VAR'], (error) => {
if(error) {
console.log('this is an error shown in the console');
res.status(500).send({ error });
// should stop this script here...
return;
}
console.log('this is continued');
res.status(200).send({ message: 'Message sent' });
});
});
module.exports = router;

How to create a reusable code for passport.authenticate?

I have multiple controllers and each controller has multiple methods. In each method I authenticate the user and use the user id returned from the authentication to get the data from database. I am trying to create reusable code for authentication since the code is repeated.
In the controller:
const authenticate = require('../utils/user-authenticate');
exports.getData = async (req, res, next) => {
const userId = await authenticate.user(req, res, next);
console.log(userId);
};
And in the authentication I have:
exports.user = (req, res, next) => passport.authenticate('jwt', async (error, result) => {
if (error) {
// Send response using res.status(401);
} else {
return result;
}
})(req, res, next);
The console.log(userId); prints undefined always. This is print before passport finishes. Looks like async/await does not work the way I want here.
It works if I use await authenticate.user(req, res, next).then() but isn't it possible to assign the result directly to userId variable?
If I use return next('1'): first time undefined but second time it prints 1.
wrapped into a promise:
exports.user = (req, res, next) => new Promise((resolve, reject) => {
passport.authenticate('jwt', async (error, result) => {
if (error) {
// reject(error)
// Send response using res.status(401);
} else {
resolve(result);
}
})(req, res, next);
})
but think about:
//app.use or something similar
addMiddleware(authJWT);
// later in the chain
useMiddleware((req, res, next)=>{
// test auth or end chain
if(!req.JWT_user) return;
req.customField = 'one for the chain'
// process next middleware
next()
});
Thanks #Estradiaz for the suggestion:
exports.user returns undefined ... Return is scoped within inner
callback - if you want to pass it outside wrap it into a promise
Reusable passport.authenticate:
exports.user = (req, res) => {
return new Promise(resolve => {
passport.authenticate('jwt', null, async (error, result) => {
if (error) {
email.sendError(res, error, null);
} else if (result) {
resolve(result);
} else {
return res.status(401).json({errors: responses['1']});
}
})(req, res);
});
};
And this is how I use it in my controller, for instance in a function:
exports.getData = async (req, res, next) => {
const userId = await authenticate.user(req, res);
};

[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I'm working with PostgreSQL and NodeJS with its "PG Module".
CRUD works but sometimes doesn't update automatically the views when i save or delete some item. this is my code and I think that the error is here but i cannot find it, i tried everything :'(
Error Message:
const controller = {};
const { Pool } = require('pg');
var connectionString = 'postgres://me:system#localhost/recipebookdb';
const pool = new Pool({
connectionString: connectionString,
})
controller.list = (request, response) => {
pool.query('SELECT * FROM recipes', (err, result) => {
if (err) {
return next(err);
}
return response.render('recipes', { data: result.rows });
});
};
controller.save = (req, res) => {
pool.query('INSERT INTO recipes(name, ingredients, directions) VALUES ($1, $2, $3)',
[req.body.name, req.body.ingredients, req.body.directions]);
return res.redirect('/');
};
controller.delete = (req, res) => {
pool.query('DELETE FROM RECIPES WHERE ID = $1', [req.params.id]);
return res.redirect('/');
}
module.exports = controller;
PD: CRUD works but sometimes appears that error.
This error occurs when you sent a response before and then you try to send response again. For this you have to check if there is any piece of code that is sending your response twice. Sometimes it happens due to asynchronous behavior of nodejs. Sometimes a process will be in event loop and we send response and when it finishes execution response will be sent again. So You can use callbacks or async await to wait for execution.
Callback
const controller = {};
const { Pool } = require('pg');
var connectionString = 'postgres://me:system#localhost/recipebookdb';
const pool = new Pool({
connectionString: connectionString,
})
controller.list = (request, response) => {
pool.query('SELECT * FROM recipes', (err, result) => {
if (err) {
return next(err);
}
return response.render('recipes', { data: result.rows });
});
};
controller.save = (req, res) => {
pool.query('INSERT INTO recipes(name, ingredients, directions) VALUES ($1, $2,$3)',
[req.body.name, req.body.ingredients, req.body.directions],function(err,resp)
{
if(err){
console.log(err)
}else{
return res.redirect('/');
}
});
};
controller.delete = (req, res) => {
pool.query('DELETE FROM RECIPES WHERE ID = $1', [req.params.id],function(err,resp){
if(err){
console.log(err)
}else{
return res.redirect('/');
}
});
}
module.exports = controller;
Or You can also use async await to wait for execution and then send response.
Async/Await
const controller = {};
const { Pool } = require('pg');
var connectionString = 'postgres://me:system#localhost/recipebookdb';
const pool = new Pool({
connectionString: connectionString,
})
controller.list = async(request, response) => {
try{
const result = await pool.query('SELECT * FROM recipes');
return response.render('recipes', { data: result.rows });
}
catch(err){
return next(err);
}
};
controller.save = async(req, res) => {
try{
await pool.query('INSERT INTO recipes(name, ingredients, directions) VALUES ($1, $2,$3)',[req.body.name, req.body.ingredients, req.body.directions]);
return res.redirect('/');
}
catch(err){
return next(err);
}
};
controller.delete = async(req, res) => {
try{
await pool.query('DELETE FROM RECIPES WHERE ID = $1', [req.params.id]);
return res.redirect('/');
}catch(err){
console.log(err);
}
}
module.exports = controller;
Check res.send() should not call two times.
In Controller
const getAll = function(req, res){
res.send(service.getAll(req,res));
}
In Service
const Type = require("../models/type.model.js");
exports.getAll = (req, res) => {
Type.getAll((err, data) => {
res.send(data);
});
};
Above res.send(data); two-time calling will create a problem. better to use
const getAll = function(req, res){
service.getAll(req,res);
}
You need to embed your response in the callback to the query. Since the call is asynchronous, sending the response earlier will terminate the call stack never waiting for the webapi(Behaviour may vary).
controller.delete = (req, res) => {
pool.query('DELETE FROM RECIPES WHERE ID = $1', [req.params.id],(err, result)
=> {
// error handling can be done accordingly
return res.redirect('/');
})
}

Categories

Resources