I am working with node.js in visual studio code. Still new to both.
I am trying to call a nested variable with child properties from one js file to use in another file. How can this be done?
I have already referenced the source file in my target file and I am able to call to the file and method that contains the variable which return object.object in console.log. Any calls to the variable itself or to the object assigned to its value come back undefined. I have tried using exports.var but visual studio code does not recognize the command. The object assigned to its value does have an initial global declaration in the source file.
job and its children is the value I want to transfer over. Console.log command in source file does display the expected values.
Source File: dataHandler.js
let jobArray = null;
export function getJobDetails() {
if(_jobDetails == null) {
let job, jobIndex, regIndex;
regIndex = _productTypes.indexOf("reg");
jobIndex = (regIndex > -1) ? regIndex : 0;
job = _jobsData[jobIndex].details;
jobArray = job;
}
console.log(jobArray.orderId); //returns expected value
return _jobDetails;
}
Target File: geo.js
import * as DataHandler from './dataHandler.js';
export function createGeo() {
var site = DataHandler.jobArray.orderId;
//var site = DataHandler.getJobDetails().jobArray;
//var site = DataHandler.getJobDetails(jobArray.orderId);
Do something like following.
const DataHandler = require('./dataHandler');
var test = DataHandler.getJobDetail();
console.log(test.orderId);
& DataHandler should do something like following. Here important point is I'm returning jobarray from getJobDetails.
var jobArray = null;
var getJobDetail = () => {
jobArray = {
orderId: 1
};
return jobArray;
};
module.exports = {
getJobDetail
};
Express's app and Socket.io have nothing to do with one another.
So fundamentally, you can't use socket inside app.post.
You need to identify the client. You can use Passport which has a Socket.io plugin that essentially bridges the app.post/get's req.user to socket.request.user.
Note: It doesn't have to be an authenticated client with user that's fetched from database, just a client with a temporary user stored in memory would do. Something like this:
app.use(function(req, res, next) {
if (!req.user) { // if a user doesn't exist, create one
var id = crypto.randomBytes(10).toString('hex');
var user = { id: id };
req.logIn(user);
res.redirect(req.lastpage || '/');
return;
}
next();
});
var Users = {};
passport.serialize(function(user) {
Users[user.id] = user;
done(null, user);
});
passport.deserialize(function(id) {
var user = Users[id];
done(null, user);
});
Then you can attach the client's socket ID to its user session.
io.on('connection', function (socket) {
socket.request.user.socketid = socket.id
});
And then instead of socket.emit use io.emit in app.post using the socketid
app.post('/', function (req, res) {
io.to(req.user.socketid).emit('whatever');
});
Note: io.to() is used to emit to a room, but every socket is by default joined to the same room named as its socket.id, so it'll work.
thanks : https://stackoverflow.com/questions/28310418/nodejs-accessing-nested-variable-in-global-scope
You are not returning "jobArray", Use Promise to return the variable to your child file.
Related
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 have an API developed in ExpressJs that looks like this:
router.post('/devices/data/*', function (req, res, next) {
reqBody = req.body
console.log(reqBody)
var pmsCache = reqBody.pms; //pms key exists in body
pmsCache.k1 = K1; //Adding a new key
pmsCache.k2 = K2; //Adding a new key
//
// Insert into Redis pmsCache - successful
//
// Trying to insert into PostgreSQL
rdbmsPool.connect((err, client, release) => {
if (err) {
console.error('Error acquiring client', err.stack)
throw err
} else {
var v;
var u = true;
var pms = reqBody.pms
console.log(reqBody) // This has K1 and K2 - Why ?
//
// Logic to insert values into db
//
release()
}
}
)
});
The req.body some how gets changed after I have done my Redis DB update. Why does the body change?
Javascript manage the objects by referencing
Means when you assign the reqBody.pms into pmsCache then it is not creating a new variable in memory it just assigning the address of the original variable.
Read more about javascript object referencing
https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
To solve your problem you should to use this
function deepClone(data){
return JSON.parse(JSON.stringify(data));
}
var pmsCache = deepClone(reqBody.pms);
I'm guessing that you mean that reqBody changes, which is as expected, because you're not properly declaring that variable at the start of the function, which means it gets promoted to a global variable and shared between all requests.
The fix is to use proper variable declaration:
router.post('/devices/data/*', function (req, res, next) {
const reqBody = req.body;
...
I am new to EJS and Node.js. I have been struggling to send the user object data to ejs template page. Actually, I want to display the current user's name who logged into the portal.
This is some portion of app.js
app.use(function(req,res,next){
res.locals.success_msg=req.flash('success_msg');
res.locals.error_msg=req.flash('error_msg');
res.locals.not_registered_user_msg=req.flash('not_registered_user_msg');
res.locals.authdata = firebase.auth().currentUser;
next();
});
//Get user info
app.get('*',function(req,res,next){
if(firebase.auth().currentUser!=null){
var id = firebase.auth().currentUser.uid;
var profileRef = fbRef.child('users');
var query = profileRef.orderByChild('uid').equalTo(id);
query.once('value', function(snap){
var user = JSON.stringify(snap.val());
console.log('User data is: '+user);
res.locals.user = user;
});
}
next();
});
Here is the header.ejs where I want to retrieve and display current user's name:
I am able to enter into the if condition., and able to print the phrase "Welcome,". But unable to get the actual user data, it simply showing empty space like this
please suggest me, where did i gone wrong here! Thanks in advance.
As per sugegstion i am adding logs from console here. I am getting user data from firebase including child id as marked here.
This doesn't look right to me:
var user = JSON.stringify(snap.val());
console.log('User data is: '+user);
res.locals.user = user;
You're converting the user object into a JSON string. Try this instead:
var user = snap.val();
console.log('User data is: ' + JSON.stringify(user));
res.locals.user = user;
I'm not really familiar with Firebase but I'd also guess that once is asynchronous, so you'd need to wait before calling next, a bit like this:
if (firebase.auth().currentUser != null) {
// ... This bit the same as before ...
query.once('value', function(snap) {
var user = snap.val();
console.log('User data is: ' + JSON.stringify(user));
res.locals.user = user;
next();
});
}
else {
next();
}
If that still doesn't work, could you include the output of the console logging in your question?
From reading over the express documentation, it seems that the arguments passed into the request handler are req and res.
You do not get the next function as an argument. With that said, you need to change your app.get to an app.use which is where you get the actual next function.
app.use((req, res, next) => {
if (firebase.auth().currentUser) {
const { uid } = firebase.auth().currentUser
const profileRef = fbRef.child('users')
const query = profileRef.orderByChild('uid').equalTo(uid);
query.once('value', snap => {
const user = JSON.stringify(snap.val())
res.locals.user = user
})
}
next()
})
I'm working on a project that will be a multi-tenant Saas application, and am having difficulty implementing a way to log into various databases depending on the user login info. Right now, I just want to split traffic between a Sandbox database (for demo purposes, and will be wiped on a regular basis), and an Alpha database (for current client testing and development). I have written the middleware below, config.js, that detects the user ID on login and assigns a database object using mongoose.createConnection(). This key-value pair is then added to a store using memory-cache. Here is the config.js code:
var mcache = require('memory-cache'),
Promise = require("bluebird"),
mongoose = require('mongoose');
Promise.promisifyAll(require("mongoose"));
(function () {
'use strict';
var dbSand = mongoose.createConnection(process.env.DB_SAND);
var dbAlpha = mongoose.createConnection(process.env.DB_ALPHA);
function dbPathConfigMiddlewareWrapper (){
return function setDbPath(req, res, next){
if ( req ){
if (!mcache.get(req.session.id) && req.body.email){
var login = req.body.email;
if (login === 'demo#mysite.com'){
mcache.put(req.session.id, dbSand);
} else {
mcache.put(req.session.id, dbAlpha);
}
}
req.dbPath = mcache.get(req.session.id);
next();
}
};
}
module.exports = dbPathConfigMiddlewareWrapper;
}());
So far so good. But I have been unsuccessful in calling the correct database in my routes. When I was just using a single database, I could easily use this:
var connStr = process.env.DBPATH;
if(mongoose.connection.readyState === 0){
mongoose.connect(connStr, function(err) {
if (err) throw err;
console.log('Successfully connected to MongoDB');
});
}
Now, I'm trying this to no avail:
var connStr = req.dbPath; //where req.dbPath is assigned in the config middleware above.
if(connStr.connection.readyState === 0){
mongoose.connect(req.dbPath, function(err) {
if (err) throw err;
console.log('Successfully connected to MongoDB');
});
}
Any guidance here would be greatly appreciated. This seems like it should be much more straightforward, and the documentation alludes to it but does not elaborate.
Here, I think, the problem is you are saving a database object to your key value storage. mcache.put(req.session.id, dbSand);. Which caused error in if(connStr.connection.readyState === 0).
You can stringify your object. mcache.put(req.session.id, JSON.stringify(dbSand));. And get the object's string and parse it into JSON like var connStr = JSON.parse(req.dbPath);.
You don't call mongoose.connect() if you're manually creating connections.
Instead, you have to register your models for each connection, which is a bit of a PITA but as far as I know there's no way around that. It may require some restructuring of your code.
Here's some untested code on how you could set something like that up.
Your middleware file:
// Create connections
const registerModels = require('./register-models');
let dbSand = mongoose.createConnection(process.env.DB_SAND);
let dbAlpha = mongoose.createConnection(process.env.DB_ALPHA);
// Register your models for each connection.
registerModels(dbSand);
registerModels(dbAlpha);
function dbPathConfigMiddlewareWrapper() { ... }
register-models.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let UserSchema = new mongoose.Schema(...);
module.exports = function(conn) {
conn.model('User', UserSchema);
};
This does mean that you can't use User.find(...) in your routes, because that only works when you're using a single connection (the default one that gets created with mongoose.connect(), which you're not using).
Instead, you should use something like this in your routes:
req.dbPath.model('User').find(...);
I want to create a route which can change while the program is running.
Example : app.get('/',function(req,res){/*Something here*/}; This is a normal route.
I want to replace the '/' with a variable which can be replaced with a random number. After that I'll create a qrcode with a nodejs module and the user who scans this qrcode will confirm a kind of transaction.
If you understand my idea and you have a solution, I'll take it.
As #Louy said, use parameters:
var getQRCode = require('./yourQRCodeModule');
app.param('qrcode', function(req, res, next, qrcode) {
// qrcode will be "1234" if your request path was "/1234"
console.log('checking qrcode: %s', qrcode);
// get the qrcode from some asynchronous function
getQRCode(qrcode, function callback(err, qrcode) {
// if this number was not a valid dynamic path, return an error from your module
console.log('qrcode was %s', (!err && qrcode) ? 'valid' : 'invalid');
if (err) {
next(err);
} else if (qrcode) {
req.qrcode = qrcode; // object from your module
next();
} else {
next(new Error('failed to load QR code'));
}
});
});
app.get('/:qrcode', function (req, res) {
// req.qrcode will be the object from your module
// if the number was invalid, this will never be called
});
What I'm trying to point out is that you're thinking of this scenario differently than how express approaches the problem. You want a one-time route with a specific qrcode, but these kind of routes don't exist in express. So here's what I understand your ideal solution to look like:
server creates "azjzso1291084JKioaio1" for a qrcode
you register something like app.getOnce("azjzso1291084JKioaio1", function(req, res){...})
first time the request gets called, it's removed from your express router
Here's what I'm suggesting:
server creates "azjzso1291084JKioaio1" for a qrcode
your module stores this qrcode either in a database or in memory, within your module, e.g. var qrcodes = {}; qrcodes["azjzso1291084JKioaio1"] = {some: 'object'};
your app.param asynchronous function based on the example given in step 2 could look like this:
// yourQRCodeModule.js
var qrcodes = {};
qrcodes["azjzso1291084JKioaio1"] = {some: 'object'};
module.exports = function getQRCode(qrcode, callback) {
if (qrcodes[qrcode]) {
var obj = qrcodes[qrcode]; // copy object
delete qrcodes[qrcode]; // remove from memory here
callback(null, obj);
} else {
// invalid path
callback(new Error('invalid QR code'), null);
}
};
Now notice if you request /azjzso1291084JKioaio1 twice, the second time fails. This is how you intend it to work, if I am not mistaken.