What's the most efficient way to send data fetched from within a GET function to a PUG template? I've been pushing the data into an array and then sending that to the template, but this seems inefficient.
app.get('/tweet/:id', function(req, res) {
const id = req.params.id;
T.get('statuses/show/' + id, function(err, data, response) {
let stweet = [];
stweet.push(data.text);
});
res.render('tweet', {stweet: stweet});
});
This gets a Tweet ID from the url and uses it to retrieve the Tweet object. Is there some way I can pull "data" from T.get('statuses/show'...) and display it directly on the PUG template?
First of all, your code cannot possible work, since T.get is asyncrhonous & stweet is defined in a scope which res.render('tweet', {stweet: stweet}); does not have access. So that code will throw:
Uncaught ReferenceError: stweet is not defined
T supports promises, you should use them so your code would be a lot cleaner. After T.get is done just send the data directly to the PUG template, there is no need to use: .push
app.get('/tweet/:id', async(req, res) => {
try {
// We wait until `T.get` is done
const { data } = await T.get(`statuses/show/${req.params.id}`, { /** params **/ });
res.render('tweet', { stweet: [data.text] });
} catch(e) {
// Or whatever error message you want to display
res.render('error');
}
});
You should check: How do I return the response from an asynchronous call?
Related
in my case, the scenario is when i click a button the value of sort, cost range and the type of Cuisine is got through the JS code then i send a post axios request from that and then passing the data from client side to server side ,where i use a controller to get the values of those data, then i send an axios GET request to the Route which is defined in my code to get the information from the database ,the problem which i face is HOW TO SEND the data from my server to frontend pug template because when i use .render() it shows internal server error in my browser but when i use .json() , i can see the output in my console ,so the problem is with my controller function i guess ? so can anyone look into my problem and provide a solution
the controller code is
exports.filter = catchAsync(async (req, res, next) => {
try {
let cost = req.query.cost;
const cuisine = req.body.params.Cuisine;
const Sort = req.body.params.sort;
let paramobj = {};
if (cuisine) {
paramobj.Cuisine = cuisine;
}
if (Sort) {
paramobj.sort = Sort.toString();
}
if (!cost) {
cost = "";
} else {
cost = `?${cost}`;
}
const data = await axios.get(
`http://localhost:3000/api/ver1/restaurant${cost}`,
{
params: paramobj,
}
);
const restaurantinloc=data.data.data
res
.status(200)
.render("restaurantcard", {
restaurantinloc,
});
// .json(restaurantinloc);
} catch (err) {
console.log(err);
}
});
everything works fine when i use res.json(restaurantinloc) but when i use res.render() i shows internal server error and not rendering the page
I have a problem with a routing function in express.
I call from firebase realtime database , the JSON has a lot of nested data and I would like to retrieve it with a for loop.
router.get('/admin_tavoli', async (req, res) => {
try {
var lista_tavoli = await firebase().ref('tavoli').once('value');
var lista_tavoli_val = lista_tavoli.val();
for(var i in lista_tavoli_val){
console.log(lista_tavoli_val[i].comanda.tavolo);
}
res.send('ok');
} catch (err) {
res.json({ message: err })
}
});
If I keep to the first level to JSON for example
for(var i in lista_tavoli_val){
console.log(lista_tavoli_val[i].comanda);
}
there are no problems.
But if I go deeper to JSON
for(var i in lista_tavoli_val){
console.log(lista_tavoli_val[i].comanda.tavolo);
}
the execution of the program goes in error, but the strange thing is that in the terminal I see the correct data.
Why does this happen?
thanks to all for the help
I'm working on an e-commerce project. I'm trying to create a shopping cart within the app so that people don't accidentally access another user's data in the Mongo database. To do this, I tried setting up a variable as res.locals.cart. This didn't work because I found out from the docs that res.locals expires in each new page.
My next idea was to create an anonymous shopping cart each time app.js started and store it in the global app.locals object. This does work, and in the following code, you can see it returns the model of the shopping cart. But after that, it's undefined as soon as I refresh or go to a new page as seen by console.log. Why is it doing that? How can I make it so that my data stays across the whole app? And I need it to be a variable, so that it changes for each new user. If there are also any NPM packages that solve this problem, that would be helpful to know.
app.locals.cart = Cart.create({}, function (err, newCart) {
if (!err) {
console.log(newCart);
return newCart
}
});
app.get('/cart', function (req, res) {
console.log(app.locals.cart);
res.render('cart')
});
💡 This is not the best practive, but, if you still want to do it, than this is an example code you can see:
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.locals.cart = [];
const item = {
id: 1,
name: 'Testing'
}
const addToCart = function(req, res, next) {
const { username } = req.body;
// req.body.username just for identifier
// you can change it with user id from token or token or whatever you want
if(typeof app.locals.cart[username] === 'undefined') {
app.locals.cart[username] = [];
}
// add item to cart by username / identifier
app.locals.cart[username].push(item);
next();
}
// or if you want to use this add to global, than you can use this middleware
// and now, you can see
// app.use(addToCart);
app.post('/', addToCart, (req, res, next) => {
// console.log
const { username } = req.body;
console.log(app.locals.cart[username])
res.send(app.locals.cart[username]);
})
app.get('/:username', (req, res, next) => {
const { username } = req.params;
console.log(app.locals.cart[username]);
res.send(app.locals.cart[username]);
})
app.listen(3000, () => {
console.log('Server is up');
})
I hope it's can help you 🙏.
I think the way you are trying is not a best practice.
Instead of using the locals try a different approach.
Creating a cart for each user in the database will be better.
You can link the card with the user. And whenever a user makes a request you fetch the cart from DB and do whatever you want.
To do that, you can add a user property to the Cart Schema. Whenever a user signs up, create a cart for it in the DB. When the user checkouts the cart, save the products in the card as another Document, let say an Order in the Orders Collection and clear the cart for future usage.
QUICK DIGEST:
Store any data from Mongoose onto a variable on your middleware and then have that variable read by app.locals or res.locals. The reason for this is because app.locals is changing and your middleware variable isn't, which lets it be read the same way every time. Example:
res.locals.data = middleware.var;
//or
app.locals.data = middleware.var;
Model.findById("model-id", function (err, noErr) {
if (!err) {
middleware.var = noErr //data retrieved from callback being passed into middleware.var
}
});
Here's what I discovered. My code didn't work because Express refuses to store data directly from a Mongoose function. I discovered this by console.logging the data inside the function and again outside it. I'm not sure why Express refuses to take data this way, but that's what was happening.
Cart.create({}, function (err, newCart) {
if (!err) {
app.locals.cart = newCart;
console.log(app.locals.cart);
//Returns Mongoose model
}
});
console.log(app.locals.cart);
//Now returns undefined
I solved this by storing the value to a variable in my middleware object called mids.
Here's the code on my app.js:
mids.anonymousCart();
app.use(function (req, res, next) {
res.locals.userTrue = req.user;
res.locals.cart = mids.cart;
next();
});
And here's the code on my middleware file:
var mids = {
anonymous_id: undefined,
cart: []
};
mids.anonymousCart = function () {
if (typeof mids.anonymous_id === 'undefined') {
Cart.create({}, function (err, newCart) {
if (!err) {
mids.anonymous_id = newCart._id.toString();
Cart.findById(mids.anonymous_id).populate('items').exec(function (err, cartReady) {
if (!err) {
mids.cart = cartReady;
}
});
}
});
}
}
What's happening here is that when app.js first starts, my middleware mids.anonymousCart is run. In my middleware, it checks that the id of Cart model stored as anonymous_id exists or not. It doesn't, so it creates the cart and stores the id. Since this is stored in the middleware and not Express, it can be accessed by any file linked to it.
I then turned the id I got back into a string, so that it could be read in the next function's findById. And if there's any items in the cart, Mongoose will then fill them in using the populate() method. But THE THING TO PAY ATTENTION TO is the way the data received in cartReady is stored in the middleware variable mids.cart, which is then read by res.locals.cart on app.js.
I need to fetch two different MongoDB collections (db.stats and db.tables ) for the same request req.
Now, in the code below, I am nesting the queries within the callback function.
router.post('/', (req, res) => {
let season = String(req.body.year);
let resultData, resultTable;
db.stats.findOne({Year: season}, function (err, data) {
if (data) {
resultData = getResult(data);
db.tables.findOne({Year: season}, function (err, data) {
if (data) {
resultTable = getTable(data);
res.render('index.html', {
data:{
result : resultData,
message: "Working"}
});
} else {
console.log("Error in Tables");
}
});
} else {
console.log("Error in Stats");
}
});
});
This code works, but there a few things that don't seem right. So my question is:
How do I avoid this nested structure? Because it not only looks ugly but also, while I am processing these requests the client side is unresponsive and that is bad.
What you have right now is known as the callback hell in JavaScript. This is where Promises comes in handy.
Here's what you can do:
router.post('/', (req, res) => {
let season = String(req.body.year);
var queries = [
db.stats.findOne({ Year: season }),
db.tables.findOne({ Year: season })
];
Promise.all(queries)
.then(results => {
if (!results[0]) {
console.log("Error in Stats");
return; // bad response. a better way is to return status 500 here
} else if (!results[1]) {
console.log("Error in Tables");
return; // bad response. a better way is to return status 500 here
}
let resultData = getResult(results[0]);
let resultTable = getTable(results[1]);
res.render('index.html', { data: {
result : resultData,
message: "Working"
} });
})
.catch(err => {
console.log("Error in getting queries", err);
// bad response. a better way is to return status 500 here
});
});
It looks like you are using Mongoose as your ODM to access your mongo database. When you don't pass in a function as the second parameter, the value returned by the function call (e.g. db.stats.findOne({ Year: season })) will be a Promise. We will put all of these unresolved Promises in an array and call Promise.all to resolve them. By using Promise.all, you are waiting until all of your database queries get executed before moving on to render your index.html view. In this case, the results of your database function calls will be stored in the results array in the order of your queries array.
Also, I would recommend doing something like res.status(500).send("A descriptive error message here") whenever there is an error on the server side in addition to the console.log calls.
The above will solve your nested structure problem, but latter problem will still be there (i.e. client side is unresponsive when processing these requests). In order to solve this, you need to first identify your bottleneck. What function calls are taking up most of the time? Since you are using findOne, I do not think that will be the bottleneck unless the connection between your server and the database has latency issues.
I am going to assume that the POST request is not done through AJAX since you have res.render in it, so this problem shouldn't be caused by any client-sided code. I suspect that either one of getResult or getTable (or both) is taking up quite a significant amount of time, considering the fact that it causes the client side to be unresponsive. What's the size of the data when you query your database? If the size of it is so huge that it takes a significant amount of time to process, I would recommend changing the way how the request is made. You can use AJAX on the front-end to make a POST request to the back-end, which will then return the response as a JSON object. That way, the page on the browser would not need to reload, and you'll get a better user experience.
mongodb driver return a promise if you dont send a callback so you can use async await
router.post('/', async(req, res) => {
let season = String(req.body.year);
let resultData, resultTable;
try {
const [data1,data2] = await Promise.all([
db.stats.findOne({Year: season}),
db.tables.findOne({Year: season})
]);
if (data1 && data2) {
resultData = getResult(data1);
resultTable = getTable(data2);
return res.render('index.html', {
data: {
result: resultData,
message: "Working"
}
});
}
res.send('error');
console.log("Error");
} catch (err) {
res.send('error');
console.log("Error");
}
});
Is it possible to reuse / call the blueprint function (find/create/update/destory) and just add some items needed for the controllers. I'm sorry if I'm having hard time expressing my question but hopefully my example will help.
Example:
modue.exports = function(){
index: ....,
create: function(req, res){
try{
// i want to call the blueprint here to save some things
create(req, res);
// then do more after creating the record
....
}catch(err){
// want to catch some error here like validation err
// instead of sending it to res.serverErr();
}
}
....
}
//File api/controller/UserController.js
// suppose the model is User under api/models
modue.exports = {
create: function(req,res){
// pass req.query to User Model's Create function, so you dont need to rewrite all
//optional paramters for this overwrite version
User.create(req.query).exec(function(e, r){
if(e){
// !!!try to create in a different way!
}
})
}
}
You need to first copy blueprint folder from sails which is present in node_modules folder
Paste the blueprint folder in you api folder
Then in your controller for e.g UserController include actionUtil for e.g
var actionUtil = require('sails/lib/hooks/blueprints/actionUtil');
module.exports = {
create: function (req, res) {
// paste code from blueprint create.js file
var Model = actionUtil.parseModel(req);
// Create data object (monolithic combination of all parameters)
// Omit the blacklisted params (like JSONP callback param, etc.)
var data = actionUtil.parseValues(req);
// Create new instance of model using data from params
Model.create(data).exec(function created(err, newInstance) {
// Differentiate between waterline-originated validation errors
// and serious underlying issues. Respond with badRequest if a
// validation error is encountered, w/ validation info.
if (err)
return res.serverError({status:500, message:'error', err: err});
// If we have the pubsub hook, use the model class's publish method
// to notify all subscribers about the created item
if (req._sails.hooks.pubsub) {
if (req.isSocket) {
Model.subscribe(req, newInstance);
Model.introduce(newInstance);
}
// Make sure data is JSON-serializable before publishing
var publishData = _.isArray(newInstance) ?
_.map(newInstance, function (instance) {
return instance.toJSON();
}) :
newInstance.toJSON();
Model.publishCreate(publishData, !req.options.mirror && req);
}
// do your after create stuff here
// Send JSONP-friendly response if it's supported
res.ok({status: 200, message: 'ok', results: newInstance});
});
}
}