This is how I am doing now. but trying to see if there is a right or another way to do it.
async function getmanual_vars(req, res,ajax){
var return_data = {};
let db = await mongo_client.connect(mongo_url);
let db_data = await db.collection('data').find({ created_by_user_id: req.cookies.user_id}).toArray();
// more codes inside //
res.json({ success: true});
}
router.post('/manual_vars', check_auth,function(req, res, next) {
getmanual_vars(req, res,0);
});
You can do it this way:
async function getmanual_vars(req, res) {
try {
var return_data = {};
let db = await mongo_client.connect(mongo_url);
let db_data = await db.collection('data').find({
created_by_user_id: req.cookies.user_id
}).toArray();
// more codes inside //
res.json({ success: true});
} catch (error) {
res.status(500).json({ success: false, error: error });
}
}
router.post('/manual_vars', check_auth, getmanual_vars);
This should have a catch for the possible errors and exceptions from your async function with status 500.
As this is already a function that takes in req and res, we can just pass in the function itself to the router instead of having to create a wrapper around it.
Related
I'm trying to store Redis key value to a variable in nodejs, something like
let gPost;
redis.get("posts", async function (err, post) {
if (err) console.log(err);
if (post) gPost = post;
}
but this approach is giving me undefined. Is there any way by which I can store value to Redis? I've already searched for it and a few posts suggested using callbacks. But what I basically want is something like this:
router.post("/:id/likes", async (req, res) => {
try {
redis.get(`posts.${req.params.id}.likes`, function (err, likeCount) {
if (err) console.error(err.message);
redis.get(`posts.${req.params.id}`, async function (err, post) {
if (err) console.log(err);
if (post) {
await customCallback(likeCount, post, req, res);
const retPost = JSON.parse(post);
return res.send({ retPost, redis: true });
} else {
try {
const reqPost = await Post.findById(req.params.id).lean().exec();
redis.set(`posts.${req.params.id}`, JSON.stringify(reqPost));
await customCallback(likeCount, reqPost, req, res);
const retPost = JSON.parse(post);
return res.send({ retPost, redis: false });
} catch (err) {
console.log(err);
}
}
});
console.log(upPost);
});
} catch (err) {
return res.status(500).send({ message: err.message });
}
});
So, here I want to increase my likes count on a post. But I don't want to hit any unnecessary requests to the database. Here first I'm getting posts.id.likes and inside it, I'm trying to fetch that post. If a post is found I'll increase my likes there only. Else, I'll make an API call to the database to fetch that post. Can you where I'm getting it wrong, or any other efficient approach I can use? Thanks.
If you're using a recent version of node-redis, you can just use promises.
Your code seems to simplify to something like
/**
* Get a post from Redis or the database.
*
* If found in the database, caches it in Redis.
*
* Returns a pair: post object and whether it was from Redis.
* #param id Post ID.
*/
async function getPost(id) {
const redisKey = `posts.${id}`;
const postData = await redis.get(redisKey);
if (postData) {
return [JSON.parse(postData), true];
}
const postObj = await Post.findById(id).lean().exec();
await redis.set(redisKey, JSON.stringify(post));
return [postObj, false];
}
router.post("/:id/likes", async (req, res) => {
try {
const { id } = req.params;
const [postObj, fromRedis] = await getPost(id);
const likeCount = await redis.get(`posts.${id}.likes`);
await customCallback(likeCount, postObj, req, res);
return res.send({ postObj, redis: fromRedis });
} catch (err) {
return res.status(500).send({ message: err.message });
}
});
The async function below is supposed to check if a url is a legit url
let CheckUrl = function (url, done) {
dns.lookup(url, function(err, address) {
if (err) return done(err);
done(null, true); //return true because I don't care what the address is, only that it works
});
}
The express.js code below gets the url but I'm having trouble understanding how to write the if statement so that it returns true or false.
// Gets URL
app.post("/api/shorturl/new", function(req, res) {
if (CheckUrl(req.body.url)) {
// do something
}
});
I'm not sure what to pass as the second argument in CheckUrl() in this if statement. Or maybe I wrote the first async function incorrectly to begin with?
Please use the async await
I have written a test code for you as below:
const express = require('express');
const app = express();
const dns = require('dns');
let CheckUrl = function (url, done) {
return new Promise((resolve, reject) => {
dns.lookup(url, function(err, address) {
console.log("err " , err)
if (err) {
resolve(false)
} else {
resolve(true)
}
});
});
}
app.post("/api/shorturl/new", async function(req, res) {
try {
let result = await CheckUrl(req.body.url);
console.log("result " , result)
res.send(result)
}
catch (error) {
console.log("in catch error " , error)
res.send(error)
}
});
app.listen(3000)
you can get the knowledge to know about the Promise here. The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
As mentioned by DeepKakkar, this was what I was looking for:
app.post("/api/shorturl/new", async (req, res) => {
try {
let result = await CheckUrl(req.body.url);
res.send(result)
}
catch (error) {
return new Error('Could not receive post');
}
});
I'm using mongoCleint to connect to the database using node server. Able to post the data to the database and able to read back the data in JSON.stringify(items) but not able to return it. Problem is that res.json(getRequest) in not giving back any json despite 'JSON.stringify(items)` is giving me a list of items. Am I not doing it right?
app.get('/getItems', function (req, res) {
var getRequest;
MongoClient.connect(url, function(err, client) {
if(err){
console.log("there is an error")
} else {
console.log("running get request")
const db = client.db(dbName);
db.collection('documents').find({}).toArray(function(err, items) {
if(err) {
console.error(err)
} else {
console.log(JSON.stringify(items))
getRequest = JSON.stringify(items)
}
})
client.close();
}
});
console.log(res.json(getRequest))
return res.json({ success: true, data: getRequest });
})
The callbacks run asynchronously but you don't wait for them. Consider running the following code:
app.get('/getItems', async function (req, res) {
const client = await MongoClient.connect(url)
const db = client.db(dbName)
const getRequest = await db.collection('documents').find({}).toArray()
client.close()
return res.json({ success: true, data: getRequest })
})
I tried to comment #Cider 's response but I don't have enough points.. I think it's ok but is lacking the res.json so It won't return anything.
I'd do it this way:
app.get('/getItems', async function (req, res) {
const client = await MongoClient.connect(url)
const db = client.db(dbName)
const getRequest = await db.collection('documents').find({}).toArray()
client.close()
return res.json({ success: true, data: getRequest })
})
I'm trying to learn Asynchronous programming with NodeJS and I'm having trouble understanding how to create usable functions.
I'm trying to compare the results of a HTTP get request and a file read all inside an "express" callback. What is the best way to split out two different async operations into their own functions so that they can be used again together in a different callback?
I Have it working when I write everything inside the express callback
app.get('/', (req, res) => {
axios.get('http://127.0.0.1:8080')
.then(function(response) {
var http_data = response.data
// Do more stuff with data
fs.readFile('fwversion_current', 'utf8', function(err, contents) {
var file_data = contents.trim()
// Do more stuff with data
if (http_data == file_data) {
res.send("Match")
}
else {
res.send("No Match")
}
});
});
But I'm hoping for something more like this so I can use these same operations in other places. I'm not sure the right node way to get there.
function getHttpData() {
axios.get('http://127.0.0.1:8080')
.then(function(response) {
var http_data = response.data
// Do more stuff with data
return http_data
});
}
function getFileData() {
fs.readFile('fwversion_current', 'utf8', function(err, contents) {
var file_data = contents.trim()
// Do more stuff with data
return file_data
});
}
app.get('/', (req, res) => {
let http_data = await getHttpData()
let file_data = await getFileData()
if (http_data == file_data) {
res.send("Match")
}
else {
res.send("No Match")
}
});
You will need to wrap those functions inside a function that returns a Promise, this will let you the ability to await for them to complete before continuing.
function getHttpData(url) {
// axios.get already returns a Promise so no need to wrap it
return axios.get(url)
.then(function(response) {
let http_data = response.data;
// Do more stuff with data
return http_data;
});
}
function getFileData(path) {
return new Promise(function(resolve, reject) {
fs.readFile(path, function(err, contents) {
if (err) {
reject(err);
return;
}
let file_data = contents.trim();
// Do more stuff with data
resolve(file_data);
});
});
}
Now when both functions returns a Promise we can await for them to complete.
Make the handler an async function because it's needed to use the await keyword, I'm using Promise.all to fire both requests simultaneously and not wait for one to complete before we fire the other.
Wrap it in a try catch to handle errors and send status 500
app.get('/', async (req, res) => {
try {
const [http_data, file_data] = await Promise.all([
getHttpData(url),
getFileData(path),
]);
http_data == file_data
? res.send('Match')
: res.send('No Match');
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong');
}
});
Right now i have this code
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
Posts.list().then(data=> {
var jsonOutput=JSON.stringify(data)
postData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Events.list().then(data=> {
var jsonOutput=JSON.stringify(data)
eventData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Messages.list().then(data=> {
var jsonOutput=JSON.stringify(data)
messageData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Users.list().then(data=> {
var jsonOutput=JSON.stringify(data)
userData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
//Then when all data from colections is retrieve i want to use the 4 variables that i created in the beggining
});
So basicly im trying to retrieve the data from my mongo database and then assign the results to that 4 variables that i create, but im not getting success.
For what i´ve been seeing i have to use async but im having some trouble doing it.
I don't like too much mrlanlee solution. This is a typical situation where using async / await can really make sense. Anyway, the Hugo's solution (the second one, with async await), even if it just works, will make the four queries in sequence, one after another to. If you want a clean, working and parallel solution, check this:
router.get('/export', async function(req, res, next) {
let data
try {
data = await Promise.all([
Posts.list(),
Events.list(),
Messages.list(),
Users.list()
]);
// at this point, data is an array. data[0] = Posts.list result, data[1] = Events.list result etc..
res.status(200).json(data)
} catch (e) {
res.status(500).send('error');
}
});
The other answer from Sashi is on the right track but you will probably run into errors. Since your catch statement on each promise returns 500, if multiple errors are caught during the query, Express will not send an error or 500 each time, instead it will throw an error trying to.
See below.
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
try {
postData = Posts.list().then(data=> {
return JSON.stringify(data);
});
eventData = Events.list().then(data=> {
return JSON.stringify(data)
});
messageData = Messages.list().then(data=> {
return JSON.stringify(data);
})
userData = Users.list().then(data=> {
return JSON.stringify(data)
});
} catch (err) {
// this should catch your errors on all 4 promises above
return res.status(500).send('error')
}
// this part is optional, i wasn't sure if you were planning
// on returning all the data back in an object
const response = {
postData,
eventData,
messageData,
userData,
};
return res.status(200).send({ response })
});
For explanation of why you weren't able to mutate the variables, see Sashi's answer as he explains it.
The variables defined outside the async code is out of scope of the async functions. Hence you cannot store the returned value from the async functions in those variables.
This should work.
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
postData = Posts.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
eventData = Events.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
messageData = Messages.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
userData = Users.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
});
Using Async/Await is a much neater solution.
router.get('/export', async function(req, res, next) {
var postData, eventData, messageData, userData;
try{
postData = await Posts.list();
eventData = await Events.list();
messageData = await Messages.list()
userData = await Users.list();
catch (e){
res.status(500).send('error');
}
});