How to switch between Mongo databases using Mongoose? - javascript

What I want to do is to use different databases for different users, for example I have 3 users that would connect to:
www.user1.myfrontend.com
www.user2.myfrontend.com
www.user3.myfrontend.com
Let's suppose that each user want to display the list of products he has, a GET request would be sent to the backend and from there I will connect to the database of the user:
mongodb://mongodatabse:secret#server/databaseOfUser1
mongodb://mongodatabse:secret#server/databaseOfUser2
mongodb://mongodatabse:secret#server/databaseOfUser3
What I did so far:
I connect to the database called config at the start of the app:
db.js
const connect = (uri, app, database="config") => {
const db= mongoose
.createConnection(uri,{ useNewUrlParser: true, useUnifiedTopology: true })
db.on('open', () => {
mongoose.connection.useDb("config")
app.emit('dbReady');
return true;
});
db.on('error', (err) => {
console.log(`Couldn't connect to database': ${err.message}`);
return false;
});
};
server.js
db.connect(process.env.MONGODB_URL, app);
app.on('dbReady', function () {
server.listen(PORT, () => {
console.info(`> Frontend is hosted #: ${process.env.BASE_URL}`);
console.info(`> Database is hosted #: ${process.env.mongodb_url}`);
console.info(`> Listening on port ${PORT}`);
});
});
Then whenever I receive a request I check in the config database for the database to use:
app.js:
const AccessConfig = require('./models/schemas/AccessConfigSchema');
const db = require('./models/db');
app.use(async (req, res, next) => {
const subdomain = req.subdomains[req.subdomains.length - 1];
try {
let database = await AccessConfig.findOne({ subdomain: subdomain});
if (!database)
database= await AccessConfig.findOne({ subdomain: "demo"});
console.log(database);
db.useDb(database);
next();
} catch (e) {
console.log(e.message)
return res.status(500).send('Error: ' + e.message);
}
});
So far It seems like the database isn't changing and I'm not even sure that this is the correct implementation or too many connections are open, etc.

I figured out, you can create connections using
//DB_URI_USER1=mongodb://mongodatabse:secret#server/databaseOfUser1
//DB_URI_USER2=mongodb://mongodatabse:secret#server/databaseOfUser2
const user1Connection = mongoose.createConnection(process.env.DB_URI_USER1, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
const user2Connection = mongoose.createConnection(process.env.DB_URI_USER2, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
Then you create the model for each
const User1 = user1Connection.model(...)
const User2 = user2Connection.model(...)
Now on the API you query the desired model.
Working for me :)

Related

Node server stucks at starting when listen function is inside the mongoDB connectToServer() function

I am trying to connect my mongodb server with express. But the server is not listening when i am giving the listen function inside connectToServer(). The following snippet is the index.js file.
const express = require("express");
const { connectToServer } = require("./utils/dbConnect");
const usersRoute=require('./routes/users.route.js');
const app = express();
const port = 5000;
connectToServer((err) => {
app.listen(port, () => {
console.log({ port });
}
)});
app.use('/users',usersRoute)
app.get("/", (req, res) => {
res.send("Hello World");
});
Here is the dbConnect.js snippet:
const { MongoClient } = require("mongodb");
const connectionString = "mongodb://localhost:27017";
const client = new MongoClient(connectionString, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
let dbConnection;
module.exports = {
connectToServer: function (callback) {
client.connect(function (err, db) {
if (err || !db) {
return callback(err);
}
dbConnection = db.db("users");
console.log("Successfully connected to MongoDB.");
return callback();
});
},
getDb: function () {
return dbConnection;
},
};
The server stucks at [nodemon] starting node index.js
I was expecting to get the server running and listening. But it doesn't.
Here is the dbConnect() snippet.
const { MongoClient } = require("mongodb-legacy");
const connectionString = "mongodb://localhost:27017";
const client = new MongoClient(connectionString, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
let dbConnection;
module.exports = {
connectToServer: function (callback) {
client.connect(function (err, db) {
if (err || !db) {
return callback(err);
}
dbConnection = db.db("users");
console.log("Successfully connected to MongoDB.");
return callback();
});
},
getDb: function () {
return dbConnection;
},
};
Here the callback function client.connect() is deprecated since mongodb v5. So i used another package to support the legacy mongodb drivers from here:
Legacy-Driver
That package on github says:
The next major release of the driver (v5) will drop support for
callbacks. This package will also have a major release at that time to
update the dependency requirement to ^5.0.0. Users can expect to be
able to upgrade to v5 adopting the changes and features shipped in
that version while using this module to maintain any callback code
they still need to work on migrating.
Lastly i changed the MongoClient location to this:
const { MongoClient } = require("mongodb-legacy");

How to connect to a local database from server

trying to access to an external database in local via my server, I executed this code server side:
console.log('MONGODB_URI_LOCAL::', config.MONGODB_URI_LOCAL)
const mongooseLocal = require('mongoose');
const connectionOptions = { useCreateIndex: true, useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false };
const conn = mongooseLocal.createConnection(config.MONGODB_URI_LOCAL, connectionOptions);
conn.on("error", console.error.bind(console, "connection error: "));
conn.once("open", async () => {
console.log("Connected successfully");
});
And I get this error:
MongoError: Authentication failed
here is the format of the URI:
config.MONGODB_URI_LOCAL = mongodb://user_name:my_password#localhost:27017/my_db_name
This is the first time I try to connect to a local database so:
Is this possible to connect to a local database from server and read or write data ?
If yes How can I fix this issue ?
Here's an approach that you can use.
This approach enables you to export your connection logic to other parts of your code.
const mongoose = require('mongoose')
const connect = async (callback) => {
try{
const connectionOptions = {
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false
};
await mongoose.connect(config.MONGODB_URI_LOCAL, connectionOptions)
// On successful connection you can call your callback function
callback()
}catch(err){
console.log(err)
process.exit(1)
}
}
connect()

Why is my callback not working correctly?

This method runs at node server
const express = require("express");
const app = express();
const fs = require("fs");
const connectDb = require("./config/db");
const __init__ = (local = false) => {
fs.writeFile(
"./config/default.json",
`{
"mongoURI": ${
local
? `"mongodb://127.0.0.1:27017/test"`
: `"mongodb+srv://admin:<password>#abc-xxghh.mongodb.net/test?retryWrites=true&w=majority"`
}
}`,
function(err) {
if (err) {
return console.log(err);
}
connectDb();
}
);
};
__init__(true);
The problem is that if originally mongoURI: 127.0.0.1:27017, and if I do __init__(false), Node will try to connect to 127.0.0.1:27017, when it should be connecting to +srv uri.
If I run __init__(false) AGAIN, then it will connect to appropriate link.
Likewise, if I then run __init__(true), it will connect to srv+ when it should be connecting to local, and if I run __init__(true) again, only then it will connect to local.
What am I doing wrong here? I'm using the callback as Im supposed to, no?
Edit:
//config/db
// for mongoDB connection
const mongoose = require("mongoose");
// require the directory
const config = require("config");
// get all contents of JSON file
const db = config.get("mongoURI");
const connectDb = async () => {
try {
console.log("connecting to mongodb", db);
await mongoose.connect(db, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true
});
console.log("Mongo DB connected");
} catch (err) {
console.log("unable to connect to mongodb");
console.log(err.message);
//exit if failure
process.exit(1);
}
};
module.exports = connectDb;
I've even tried doing the following:
.....
console.log("Developing locally:", local);
// require the directory
const config = require("config");
// get all contents of JSON file
const db = config.get("mongoURI");
connectDb(db);
.....
But it still reads the old value
The problem is on execution order since the require is sync
The order now is:
const connectDb = require("./config/db");
const config = require("config");
const db = config.get("mongoURI"); // this has the OLD VALUE
fs.writeFile(...
await mongoose.connect(db, { // this is using the OLD REFERENCE
So you need to change your connectDb function like this:
const connectDb = async () => {
const config = require("config");
// get all contents of JSON file
const db = config.get("mongoURI");
try {
console.log("connecting to mongodb", db);
await mongoose.connect(db, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true
});
console.log("Mongo DB connected");
} catch (err) {
console.log("unable to connect to mongodb");
console.log(err.message);
//exit if failure
process.exit(1);
}
};
Anyway, I think this is not a nicer way to load config based on the environment, so I would suggest improving it using factory pattern.
Your code for URL local vs srv+ is correct. Problem i could see is placement of method connectDb();
fs.writeFile("fir arg - URL", "second -content", third - error fun {});
where in your code after function, connectDb() is placed after error fun. After it should be closed.

I am having trouble connecting my app to Mongo Atlas

I am following the documentation and based off what I read I am doing it right. I am connecting to my Mongo Atlas server. The server connects and I am able to connect to the DB and the Collection. Yet the DB and the Collection are not being passed to the db object.
I have tried console logging the values and refactored my logic and yet still no solution.
// MongoDB Connection Setup
let db = {};
let MongoClient = require("mongodb").MongoClient;
let uri = process.env.MONGODB_CONNECT_URL;
let client = new MongoClient(uri, { useNewUrlParser: true });
client.connect(err => {
assert.strictEqual(null, err);
console.log('Connected Successfully to MongoDB!');
db.client = client.db("cosmosdb");
db.collection = client.db('cosmosdb').collection('cosmos');
console.log("Database Values: ", db) // This actually returns values
return db;
});
console.log('Database: ', db); // Not returning values
app.set('port', process.env.PORT || 3000);
let server = app.listen(app.get('port'), () => {
console.log(`Express server listening on port: `, server.address().port)
});
server.db = db;
When I console.log db I am expecting to see
Database: {
client: // values
collection: // values
}
yet this is what I am getting back
Database: {}
EDITED
Is your uri assigned like below? (mongodb+srv)
let uri = `mongodb+srv://${dbUser}:${dbPwd}#${dbHost}/test?retryWrites=true`;
let client = new MongoClient(uri, { useNewUrlParser: true });
There is a parameter you are missing on the connect() call, you have "err", but it should be (err, client). So for me it looks as follows:
var db = {};
var MongoClient = require('mongodb').MongoClient;
//Use connect method to connect to the Server
MongoClient.connect(process.env.MONGODB_CONNECT_URL, { useNewUrlParser: true }, function (err, client) {
assert.equal(null, err);
db.client = client;
db.collection = client.db('newswatcherdb').collection('newswatcher');
console.log("Connected to MongoDB server");
});

expressJS/mongoDB: How to add some fixture mongoDB data on start up?

This is how I've setup my expressjs server and my mongoDB (mongoDB native driver, not mongoose). Now I would like to check for some existing documents in the DB to add some fixture documents on start of the server.
I do not understand where to do that.
Something like:
const hasAdmin = db.collection('users').findOne({ username: 'admin' })
if (!hasAdmin) {
// Add some data to collection
}
app.js
const app = express()
const mongoDB = mongodb://localhost:27017/mydb
// Parse application/json
app.use(bodyParser.json())
// Parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({
extended: false
}))
// GraphQL
app.use('/graphql',
bodyParser.text({ type: 'application/graphql' }),
graphqlMiddleware,
graphqlExpress(req => ({
schema: schema
}))
)
// Connect to Mongo on start
db.connect(mongodb, (err) => {
if (err) {
console.error('Unable to connect to Mongo: ' + err)
process.exit(1)
} else {
app.listen(port, () => {
console.log('Listening on port ' + port)
})
}
})
module.exports = app
You can in theory put it anywhere there is a Mongo connection open.
I would recommend to extract your database-related functionality into a separate class (and a separate file for that; separation of concerns).
You could then call the functionality that fills your database initially from the constructor of that class.
Something like this (this is not working code, but should give you an idea):
db-helper.js
class DbHelper {
constructor() {
this.connect();
}
connect() {
// establish mongo connection
mongo.connect("...", (err, db) => {
this.db = db;
// then take care of initial filling
this.ensureDatabaseIsFilled();
});
}
ensureDatabaseIsFilled() {
const hasAdmin = this.db.collection('users').findOne({ username: 'admin' })
if (!hasAdmin) {
// Add some data to collection
}
}
/**
* Random method that retrieves data from mongo
*/
getRandomThing() {
return new Promise((resolve, reject) => {
this.db.find({...}, (err, result) => resolve(result));
});
}
}
module.exports = DbHelper;
app.js
const DbHelper = require('./db-helper');
const dbHelper = new DbHelper();
// ....
app.get('/example', (req, res) => {
dbHelper.getRandomThing().then(result => res.send(result));
});
// ....

Categories