I have the following code snippet in my routes:
router.get("/home", (req,res) =>{
var message = req.session.user1;
res.render("home", {data: message});
})
In my taskController.js, I have a function that I would query data using Sequelize that should also be passed to home. How can I insert the function in the snippet above?
My initial idea was
router.get("/home", (req,res) =>{
Some query here stored in var task
var message = req.session.user1;
res.render("home", {data: message, dataTwo: task});
})
But this only applies if I do the usual MySql query within the route. But I'm currently using Sequelize (which is in my taskController.js) to do the querying for me. So my option is to call that function in that controller and use it in the router.get above but I'm not quite sure on how to incorporate it.
You can use async/await to asynchronously fetch the task from your controller. Require your controller, then make the function for the route async (req, res), and call the function on your controller that returns the task using await. The function in your controller should also be async and either return the sequelize Promise directly, or after modifying the data as needed.
// import your controller
const controller = require('./controllers/controller');
// make the function async
router.get("/home", async (req, res) => {
// await the result of a function that is async (returns a promise)
const task = await controller.getTask(maybeSomeId);
const message = req.session.user1;
res.render("home", {data: message, dataTwo: task});
});
// you can add error handling like this
router.get("/home2", async (req, res, next) => {
try {
// await the result of a function that is async (returns a promise)
const task = await controller.getTask(maybeSomeId);
const message = req.session.user1;
return res.render("home", {data: message, dataTwo: task});
} catch (err) {
return next(err);
}
});
Related
I have an express-router route as follows:
.post(
authController.protect,
itemController.createItem, //create Item
itemController.sendNotification('new-item'), //if user exists send notification
);
the createItem controller has some logic that can run sendNotifications with type('new-match') and if skipped will continue to the next controller using next() as follows:
if(foo) next()
else sendNotifications('new-match')
my sendNotifications middleware is defined as follows:
const sendNotification = type =>
catchAsync(async (req, res, next) => {
if (req.userFound) {
await Notification.create({ user: req.userFound, notType: type });
const user = await User.findByIdAndUpdate(req.userFound, {
$inc: { notifications: 1 }
});
req.userFoundEmail = user.email;
}
next();
});
exports.sendNotification = sendNotification;
My problem is that when sendNotifications middleware is induced from the route stack it will have access to req,res,next but when it is called from the controller itself req,res,next are undefined
I am calling the sendNotification from inside the controller as explained above:
sendNotification('new-match');
How i can provide access to req,res,next from both ways?
I am new to this. There are a couple of solutions posted for a similar problem but none of them helped.
I have posted below a very simple toy example that will help you to debug.
index.js
const express = require('express')
port=3001
var controller = require('./controller');
app.use('/api', controller);
controller.js
var model = require('./model')
var router = express.Router();
router.get('/bl', function(req, res) {
model.getData( function (err, objects) {
if(err) return res.send(err);
return res.status(200).json(objects);
});
});
module.exports = router;
model.js
const bl = {"hello":"world"}
const getData= (request, response) => {
return(bl);
}
module.exports = {
getData
}
Issue:
invoking : http://localhost:3001/api/bl => no response
, console : no error
Note:
in my model.js, I am querying in the Postgres database, and I can see the results in console.log.
but I don't see any such result when I try to see data using console.log in controller.js. Similar behavior I observed in the above toy example
I can see an error and something worrying me.
The error is in following two lines
// In controller.js
model.getData( function (err, objects) {
// In models.js
const getData= (request, response) => {
You define a function accepting two arguments (request, response) (both of them are objects) and than you call it passing only one argument of type function.
The thing worrying me is the getData function itself.
const bl = {"hello":"world"}
const getData= (request, response) => {
return(bl);
}
Apart from parameters, it is a sync function, but in your question you pointed that in models.js you query in the Postgres database, and you can see the results in console.log; now you give us no details about how you query Postgress, but I suppose you are doing it with something asynchronous (probably pg). The other relevant detail you didn't revealed is if you query Postgress inside or outside the body of getData function. I hope I'm wrong, but since in your example you are returning something define outside the body of the function, I have the doubt.
Let's consider only the good option, you query Postgress inside the body of the function, probably your complete getData function looks like follows.
const getData = (request, response) => {
client.query('SELECT $1::text as message', ['Hello world!'], (err, res) => {
// Here you get your message in console
console.log(err ? err.stack : res.rows[0].message);
client.end();
})
return something;
// I don't know what, but fore sure something wrong
}
To solve both the problems you just need to fix getData (by chance or by cut&paste you are calling it in the right way in controller.js).
Let's start fixing its signature makeing it accept only one argument: the callback function:
const getData = (done) => {
then let's asynchronously "return" the data we got from Postgress through the done callback function.
const getData = (done) => {
client.query('SELECT $1::text as message', ['Hello world!'], (err, res) => {
// We can leave this for debugging purposes
console.log(err ? err.stack : res.rows[0].message);
// Here we are not handling errors from client.end()...
// but we can neglect about this right now
client.end();
// Let's calle the callback function passing it the result
done(err, res);
})
// There's nothing to return
}
It should be enough.
Hope this helps.
You need a third argument, you will mostly see callback or just cb and execute it in the function and pass some data to it. The first parameter false will be the error argument later and bl will be the objects argument that is passed
const getData= (request, response, callback) => {
callback(false, bl);
}
and then pass req and res arguments to it:
model.getData(req, res, function (err, objects) {
if(err) return res.send(err);
return res.status(200).json(objects);
});
Or the modern way to do it with async / await you could return an promise
const getData= (request, response) => {
return new Promise((resolve, reject) => {
if(someErrorAppear){
reject("some error occured");
}
resolve(bl);
})
}
Now you can go with async / await
router.get('/bl', async function(req, res) {
try {
let objects = await model.getData(req, res);
res.status(200).json(objects);
}catch(err){
res.send(err);
}
});
});
There is a simple solution to this.
Actually, the error is in model.js. Try below code:
const bl = {"hello":"world"}
const getData= (callback) => {
callback (bl)
}
module.exports = {
getData
}
I am quite new to express and I created a database in Postgres to extract the data about blog posts to place the information in an ejs file.
I get the error:
Cannot read property 'send' of undefined
I've tried to call db.getPosts() with res and req, but it's not possible to set a header again, returns an error.
The problematic chunk of code in my query.js file:
const getPosts = (_req, res) => {
pool.query('SELECT * FROM blog_posts', (error, results) => {
console.log(error);
// console.log(results.rows);
if (error) {
throw error
}
return res.send(results.rows );
})
}
send(results.rows) or render('blog', {posts: results.rows}) called on res give the exact same error.
Function in server.js that is supposed to use this data is as follows:
app.get("/blog", function (req, res) {
const posts = db.getPosts();
res.render("blog", { posts: posts });
});
What do I do wrong? I lack some knowledge, that is for sure, so please, if you can help, explain this briefly to me if possible.
Also, is send() function a correct function to get the data to operate on in server.js? Many tutorials suggest json() but then I don't really get the proper data format, it is just displayed in the browser.
Thank you very much.
Make getPosts receive a callback:
const getPosts = (callback) => {
pool.query('SELECT * FROM blog_posts', (error, results) => {
console.log(error);
// console.log(results.rows);
if (error) {
throw error
}
callback(results.rows);
})
}
Usage would be something like:
app.get("/blog", function (req, res) {
db.getPosts(function(rows) {
res.render("blog", {posts: rows})
});
});
in your getPosts method do not use send. just return results.rows. upate your code like below.
const getPosts = () => {
pool.query('SELECT * FROM blog_posts', (error, results) => {
console.log(error);
// console.log(results.rows);
if (error) {
throw error
}
return results.rows;
})
}
also you need to use async await while calling getposts as it is a async function. update the code like below.
app.get("/blog", async function (req, res) {
const posts = await db.getPosts();
res.render("blog", { posts: posts });
});
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');
}
});
I want to use "await"
According to the sails documentation I act as follows:
https://sailsjs.com/documentation/reference/waterline-orm/models/create
create: function (req, res, next) {
var new_place = await Place.create({...}, function place_created(err, XX){
if(err && err.invalidAttributes) {
return res.json({'status':false, 'errors':err.Errors});
}
}).fetch();
if(new_place){
console.log(new_place);
res.json({'status':true,'result':new_place});
}
},
But I get the following Error:
var new_place = await Place.create({...}, function place_created(err, XX){
^^^^^
SyntaxError: await is only valid in async function
what should I do to fix this.
SyntaxError: await is only valid in async function
This is because you are using await in a function that is not async
Remember, the await keyword is only valid inside async functions. If you use it outside of an async function's body, you will get a SyntaxError.
Source MDN async function
You need to make the function async for it to work. Making those changes in your code,
'use strict';
create: async function(req, res, next) {
var new_place = await Place.create({ ... }, function place_created(err, XX) {
if (err && err.invalidAttributes) {
return res.json({ 'status': false, 'errors': err.Errors });
}
}).fetch();
if (new_place) {
console.log(new_place);
res.json({ 'status': true, 'result': new_place });
}
},
I think you should make your function async.
async(function(){
var new_place = await Place.create({...})
})();
And if you are using await you should not use callbacks. You should manage the response as explained here
Also you can check this guide of how to manage async in sail.js