Throw await syntax in Nodejs async express handler is not working - javascript

const express = require("express");
const expressAsyncHandler = require("express-async-handler");
const app = express();
const f = async () => {
return false;
};
app.get(
"/",
expressAsyncHandler(async () => {
throw await f();
}),
() => {
console.log("the bug!");
}
);
app.use((err, req, res, next) => {
console.log("caught!", err);
});
app.listen(4000, () => console.log("listening on port 4000..."));
Expected output on the console:
"caught!".
output:
the bug!.
question: Why? Is it a bug in async-express-handler package or is it a normal JavaScript behaviour? what if I want to throw await something inside? how ?

Your problem is that you're throwing the value false. This doesn't fit into nodejs' callback conventions (and by extension, express error handling), which requires the err parameter to get a truthy value to be considered an error. A much simpler way to reproduce the issue:
app.get(
"/",
(req, res, next) => {
Promise.reject(false).catch(next);
// or even just:
next(false);
},
() => {
console.log("the bug!");
}
);
So just don't do that! Always throw an Error, not a string, not something else, and this principle also holds for promise rejections.

Related

My Node.js backend hangs when I perform database updates

I'm running a React/Node/MariaDB setup, where my Node acts as a getter and setter to my database depending on front end events. So I have React polling Node every 2 seconds to get a value called selectorPos or selPos in some cases. This value needs to be kept up to date with the database value, but also needs to be able to be changed by the GUI components. So I have a button press set to update the value in the database, then it would be read and updated upon the 2 second polling.
My solution works perfectly for a few button clicks. But seemingly randomly it will hang after a database write and nothing else works. Once I force stop the node server and restart, it rapid prints out a bunch of console.log statements I had set up for polling and writing, as if it had a backlog to catch up on, then it works again until another hang and reset.
I feel like there is something quite minor I am overlooking here. The code works, but it seems to get overloaded quite easily.
Here is my code:
React stuff:
componentDidMount() {
this.interval = setInterval(()=> this.getSelPos(), 2000);
}
componentWillUnmount() {
clearInterval(this.interval)
}
getSelPos() {
axios.get("http://localhost:5000/selector", { crossdomain: true }).then(response => {
console.log(response.data);
this.setState({SelectorPos: parseInt(response.data)});
console.log("Selpos is" + selPos);
});
}
setSelPos(i) {
axios.post("http://localhost:5000/selector", { position: i }).then((response) => {
console.log(response);
}, (error) => {
console.log(error);
});
}
handleClick(i){
this.setSelPos(i);
Node:
const express = require('express')
const db = require('./db')
const app = express()
const port = 5000
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
// GET
app.get('/selector', async (req, res) => {
try {
const result = await db.pool.query("select value from db.tags where tag='1006'");
res.send(result[0].value.toString());
console.log(result[0].value.toString());
} catch (err) {
throw err;
}
});
// POST
app.post('/selector', async (req, res) => {
try {
const result = await db.pool.query("update db.tags set value = " + req.body.position.toString() + " where tag='1006'");
console.log(result);
} catch (err) {
throw err;
}
});
app.listen(port, () => console.log(`Listening on port ${port}`));

How to return JSON in Express

I'm new to node/express. I have a simple express app with one post route ('/'). Given a list of GitHub users names, it should return information about those developers.
For example, if I post
{ "developers": ["JohnSmith", "JaneDoe"] }
It should return
[
{
"bio": "Software Engineer at Google",
"name": "John Smith"
},
{
"bio": "Product Manager, Microsoft",
"name": "Jane Doe"
}
]
This is what I have so far
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.post('/', function(req, res, next) {
try {
let results = req.body.developers.map(async d => {
return await axios.get(`https://api.github.com/users/${d}`);
});
let out = results.map(r => ({ name: r.data.name, bio: r.data.bio }));
return res.send(out);
} catch {
next(err);
}
});
app.use((err, req, res, next) => {
res.send("There was an error");
})
app.listen(3000, function(){
console.log("Server started on port 3000!");
});
When I try this in a tool like Insomnia, I keep getting There was an error.
You can use res.formate() as per this docs...
Or set type for res as per this Docs...
res.type('json')
From middleware, you are always saying res.json("there is some error"), why are you keeping it there if not needed. if you remove app.use(). you will see the request is returning you names of the developer
read this for error handling.
https://expressjs.com/en/guide/error-handling.html
The way you return JSON in an Express API is as follows:
res.status(200).json(out)
See https://expressjs.com/it/api.html for the full documentation.
You're getting There was an error. Because, there is an error in your / route. But, the thing is you haven't wrote the err parameter with catch. If add that one in your catch (of your try/catch block) and console.log it, you'll see the error
TypeError: Cannot read property 'name' of undefined
You're getting this error because, results variable is actually an array of pending Promise. You need to first resolve these pending promises. One way to do is,
app.post("/", function (req, res, next) {
try {
const developers = req.body.developers;
let promises = [];
developers.map((d) => {
promises.push(axios.get(`https://api.github.com/users/${d}`));
});
Promise.all(promises).then((responses) => {
let out = responses.map((r) => ({
name: r.data.name,
bio: r.data.bio,
}));
return res.send(out);
});
} catch (err) {
next(err);
}
});

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;

Async wrap function for express 4 doesn't catch error

I am trying out this trick from strongloop https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/ for having a wrap function for async routes
but the error function is never called. I also tried to put the error function inside the authRouter file.
in authRouter.js:
let wrap = fn => (...args) => fn(...args).catch(args[2]);
router.post('/login', wrap(async (req,res) => {
if (!req.body.email || !req.body.password) throw new Errors.BadRequestError();
}));
export default router;
and in app.js
app.use('/auth', authRouter);
app.use(function(err, req, res) {
console.log('in here');
const status = err.status || 500;
if (status === 500) console.log(err);
res.status(status);
res.send({
message: err.message,
error: err
});
});
You need to have 4 parameters in the error handler to make express recognise it as one:
(from http://expressjs.com/en/guide/error-handling.html): "Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next)."
this is the last error handler I don't wanna call next()
That doesn't really matter, you still have to declare it even if you don't use it.

Categories

Resources