I'm stuck trying to post data from client to mongodb - node.js

Iā€™m trying to pass data from my server app.js to my database file so that I can store it into MongoDB atlas.
I can see where the problem is I'm honestly just not sure how to go about fixing it. The problem seems to be two parts.
1.) I'm passing a function into .insertOne and not an object, this is resulting in a promise error. When I try to change things in the userDataFinal function I start running into scope errors and things not being defined. I'm not really sure how to fix this.
2.) my code is trying to create a new document as soon as it starts up because
db.collection('User').insertOne(userDataFinal);
is located in the .connect callback function.
I need this code to run only when a put request has been made on the client side.
relevant server code app.js
const base = require('./base.js');
app.post('/',(req, res)=>{
var userName = req.body;
base.userDataFinal(userName);
res.render('index');
});
Relevant database code base.js
var userDataFinal = function getUserName(user){
console.log(user);
}
module.exports = {userDataFinal};
////////////////////////////////////////////////////////////////////////
// connects to the database
MongoClient.connect(URL, {useNewUrlParser: true}, (error, client)=>{
if(error){
return console.log('Could not connect to the database');
}
// creates a new collection
const db = client.db(database);
// adds a document to the designated collection
db.collection('User').insertOne(userDataFinal);
console.log('Database is connected...')
});

First, you are passing a callback into your MongoClient.connect() function.
This callback is useful when you want to make sure that you are connected.
As you said it, you want your code to run only when the request has been made. You can remove your insertion from the connect, but you can still keep the error handling part as it is always useful to know why there was a db connection error.
Also, you are calling the mongo insertOne method, that expects an object. You are passing it a function.
EDIT: Create a db variable outside all your functions, then assign it from the Mongo connect callback once you have access to the client.
You will be able to use this db later in the routes.
var MongoClient = require('mongodb').MongoClient;
var db; // Will be set once connected
MongoClient.connect(URL, {useNewUrlParser: true}, (error, client)=>{
if(error){
return console.log('Could not connect to the database');
}
db = client.db(database);
console.log('Database is connected...')
});
app.post('/',(req, res)=>{
var userName = req.body.username;
db.collection('User').insertOne({ username: userName });
res.render('index');
});
Please note that you are probably passing the userName through the body, in this case you need to retrieve it that way: req.body.username (if you named the related body parameter username).

Related

How to update express session outside of the request with connect mongo?

I am building an integration with Express Session and I am trying to authenticate the user in a separate webhook. So I need a way to update the user session outside of the request since it would be a different request.
What I did is I passed the session ID to the new request, and use MongoClient to update the session in the database. I remember Express Session only stores the ID on the client-side and the data is store on the database, so I assume updating the database would do it. But that doesn't seem to be the case. I can clearly see that the session on MongoDB is all updated but I kept getting the outdated data in req.session.data.
Here's what I've done
So the first thing I tried is to use the req.session.reload() method like these:
Updating express-session sessions
change and refresh session data in Node.js and express-session
But it is still giving me outdated data as if the function did nothing (it did print out logs so I assume it did run).
I tried using this that uses store.get() method from Express Session but it is giving me undefined session.
Express load Session from Mongo with session_id
So as a last resort I use MongoClient to get the session data directly and update the session with the data obtained. I use async for the route handler and await the MongoClient to get the data. It doesn't wait for the await and just kept throwing undefined. I thought it's my code that's wrong but I am able to get user data with the code and it did wait for the MongoClient to get the data, but somehow it is not working for session data.
Here's part of my code:
app.router.get('/login', async function (req, res, next) {
req.session.auth = await mongo.getCookie(req.session.id).auth;
req.session.user = await mongo.getCookie(req.session.id).user;
console.log('Login Action', req.session.id, req.session.auth, req.session.user);
}
module.exports.getCookie = async function getCookie(identifier) {
try {
let database = client.db('database');
let collection = await database.collection('sessions');
let result = await collection.findOne({ _id: identifier }, function(err, res) {
if (err) throw err;
return res;
});
return result;
}
catch (err) {
console.error(err);
return null;
}
}
Here's other answers that I've check
This one only update the expiration so I assume its not going to work for me, I did set the resave to false so that it doesn't try to save every single request, since its saving to the database I assume it has nothing to do with updating and I have tried setting it to true and nothing happened.
Renewing/Refreshing Express Session
And in this it uses socket.io and store.get() so it's not going to work as well.
How to access session in express, outside of the req?
Can someone guide me on how do I get Express Session to update from the database or is there a way to get the data from the database and update the session that way?
So I did eventually figured it out, turns out the session was not updated from the database. Looks like it has a local copy in the cache, but I was under the impression that the express session only uses the database.
I know for a fact that my data is indeed in the database but not in the cache or wherever the local copy of the express session is stored. So the easiest way to get pass this problem is to update the local copy with the data on the database.
And I created this function to update the session data
async function sessionUpdate(req) {
try {
// get the data on the database
let tempSession = await mongo.getCookie(req.session.id);
// if the data exist, we can copy that to our current session
if (tempSession) {
req.session.auth = tempSession.auth;
req.session.user = tempSession.user;
// you can do other stuff that you need to do with the data too
if (req.session.health == null || req.session.health == undefined || typeof req.session.health === 'undefined') {
req.session.health = 0;
}
// This update and save the data to the session
req.session.save( function(err) {
req.session.reload( function (err) {
//console.log('Session Updated');
});
});
}
else if (!tempSession) {
req.session.auth = false;
req.session.user = null;
req.session.ip = request.connection.remoteAddress;
}
}
catch (err) {
console.error(err);
}
}
Now that we have a function that can update the session data, we need to implement it everywhere. I use middleware to do that, you can use other methods too but this must be called before the route or the session logic.
// middleware with no mounted path so it is used for all requests
receiver.router.use(async function (req, res, next) {
try {
await sessionUpdate(req);
}
catch (err) {
console.error(err);
}
next();
});
It's not exactly a good/efficient way of doing it... that's a lot of things to run before even getting to the route so I would assume it would bring the performance down a bit... But this is how I get it to work and if anyone can come up with some better idea I would very much appreciate it šŸ™Œ

TypeError: Cannot read property 'db' of undefined. when using mongodb atlas but no error when using mongodb locally

i get the error when i use the mongodb atlas but when using the mongodb locally(mongo installed on my computer) the code runs very well with no errors.
http.listen(3000, function(){
console.log("Server started");
mongoClient.connect("mongodb+srv://<username>:<password>#myname.anjzd.mongodb.net/<dbname>?retryWrites=true&w=majority", function(error, client){
var database = client.db("mydatabase");
console.log("Database connected.");
});
That's because you've got an error from this callback and therefore your client will be set to null.
I guess the error comes from your URL that you put inside connect.
<username>, <password> and <dbname> are placeholders for your real username, password and database name respectively.
say your real username, password and database are ABC, 123 and myDB respectively, then your connection URL would be something like this:
let url = "mongodb+srv://ABC:123#myname.anjzd.mongodb.net/myDB?retryWrites=true&w=majority"
If the error is caused by something else you can handle it properly to detect the reason.
mongoClient.connect(url, function(error, client){
if (error) return console.log(error);
var database = client.db("mydatabase");
console.log("Database connected.");
});

TypeError: Cannot read property 'find' of undefined when deployed but not locally

I have created a MongoDB Atlas Cluster, put some data in there, then created a node/express app to retrieve data from that cluster. Locally I am able to retrieve the correct data but when my node server is deployed to AWS Elastic Beanstalk, I get this error:
TypeError: Cannot read property 'find' of undefined
I'm unsure of how to even trace this problem I am having.
const MongoClient = require('mongodb').MongoClient;
let database;
MongoClient.connect(
'url-to-cluster',
{useNewUrlParser: true, useUnifiedTopology: true},
(err, client) => {
const users = client.db('entity');
database = users.collection('entityinfo');
});
// Used in app.use method
database.find().toArray()
.then(results => {
res.json(results[0].UID);
});
The call to MongoClient.connect is asynchronous, it will return immediately and call the provided callback only after the connection resolves.
To demonstrate this, add log messages before the call to connect, inside the callback, and before the call to find. I believe you will discover that find is being called before the callback that would give database a value.
This likely works locally because the connection completes fast enough that is it done before find is called, but takes several milliseconds (perhaps tens of milliseconds) when connecting to the server in the cloud. i.e. this is a race condition.
You might try including the find call inside the callback, like
MongoClient.connect(
'url-to-cluster',
{useNewUrlParser: true, useUnifiedTopology: true},
(err, client) => {
const users = client.db('entity');
database = users.collection('entityinfo');
// Used in app.use method
database.find().toArray()
.then(results => {
res.json(results[0].UID);
});
});

How do I get a MongoDB import script to close the db after inserting results?

I am running a quick little nodejs script to find documents in one collection and insert them into another collection but on the same DB. I came up with this guy, but it has no way to close because I think its running open or async?
I have tried placing the db.close() in various places and tried mongoClient.close(). No luck which had me thinking about trying to force a timeout for the async call. Added a connection Time out but it did not have the desired behaviour.
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
const async = require("async");
// Connection URL
var url = 'mongodb://localhost:27017/sourceDB';
// Use connect method to connect to the Server
MongoClient.connect(url,{connectTimeoutMS: "5"}, (err, db) => {
db.collection('source.collection', function(err, col) {
assert.equal(null, err);
col.find().forEach(function (data) {
console.log(data);
db.collection('destination.collection').insertOne(data, function(err, res) {
assert.equal(null, err);
});
console.log("Moved");
});
});
});
The script does well and picks up the collection and inserts, but the connection remains open.
It is not recommended to explicitly close the connection as shown by this SO thread.
Rather, allow the client library to manage the connection for you.

What's the best practice for MongoDB connections on Node.js?

This is something that is a bit unclear to me (I'm just getting started with Node and Mongo), and it really concerns me because of server performance and strain (which I guess is another question, but I'll get to that at the end of the post).
So, assuming I'm writing an API with Node.js and Restify, where each API endpoint corresponds to a function, should I:
a) open the db connection and store it in a global var, and then just use that in every function?
Example:
// requires and so on leave me with a db var, assume {auto_reconnect: true}
function openDB() {
db.open(function(err, db) {
// skip err handling and so on
return db;
}
}
var myOpenDB = openDB(); // use myOpenDB in every other function I have
b) open the db connection and then just put everything in one giant closure?
Example:
// same as above
db.open(function(err, db) {
// do everything else here, for example:
server.get('/api/dosomething', function doSomething(req, res, next) { // (server is an instance of a Restify server)
// use the db object here and so on
});
}
c) open and close the db each time it is needed?
Example:
// again, same as above
server.get('/api/something', function doSomething(req, res, next) {
db.open(function(err, db) {
// do something
db.close();
});
});
server.post('/api/somethingelse', function doSomethingElse(req, res, next) {
db.open(function(err, db) {
// do something else
db.close();
});
});
This last one is what I would do out of intuition, but at the same time I don't feel entirely comfortable doing this. Doesn't it put too much strain on the Mongo server? Especially when (and I hope I do get to that) it gets hundreds ā€” if not thousands ā€” of calls like this?
Thank you in advance.
I like MongoJS a lot. It lets you use Mongo in a very similar way to the default command line and it's just a wrapper over the official Mongo driver. You only open the DB once and specify which collections you'll be using. You can even omit the collections if you run Node with --harmony-proxies.
var db = require('mongojs').connect('mydb', ['posts']);
server.get('/posts', function (req, res) {
db.posts.find(function (err, posts) {
res.send(JSON.stringify(posts));
});
});
Option A is not a great idea since there is no guarantee that the DB will be finished opening before an HTTP request is handled (granted this is very unlikely)
Option C is also not ideal since it needlessly opens and closes the DB connection
The way that I like to handle this is using deferreds/promises. There are a bunch of different promise libraries available for Node but the basic idea is to do something like this:
var promise = new Promise();
db.open(function(err, db) {
// handle err
promise.resolve(db);
});
server.get('/api/something', function doSomething(req, res, next) {
promise.then(function(db)
// do something
});
});
I believe Mongoose handles connections in a way that vaguely resembles this.

Resources