Mongoose with ReplicaSet on Atlas - node.js

I have a replica set on MongoDB Atlas and this is my mongo shell connection string which connects perfectly:
$ mongo "mongodb://MY_SERVER-shard-00-00-clv3h.mongodb.net:27017,MY_SERVER-shard-00-01-clv3h.mongodb.net:27017,MY_SERVER-shard-00-02-clv3h.mongodb.net:27017/MY_DATABASE?replicaSet=MY_REPLICASET-NAME-shard-0" --ssl --username MY_USERNAME --password MY_PASSWORD --authenticationDatabase MY_ADMIN_DATABASE
How Can I convert it to use in mongoose? How Can I build my uri and options variable?
I tried the following without success:
// connection string using mongoose:
var uri = 'mongodb://MY_USER:MY_PASSWORD#' +
'MY_SERVER-shard-00-00-clv3h.mongodb.net:27017,' +
'MY_SERVER-shard-00-01-clv3h.mongodb.net:27017,' +
'MY_SERVER-shard-00-02-clv3h.mongodb.net:27017/MY_DATABASE';
var options = {
replset: {
ssl: true,
authSource: 'MY_ADMIN_DATABASE',
rs_name: 'MY_REPLICASET_NAME-shard-0'
}
};
mongoose.connect(uri, options);
var db = mongoose.connection;
I've tried including user: and pass: on options, removing MY_USER:MY_PASSWORD# from uri, change rs_name to replicaSet, every unsuccessful attempt. It seems that mongoose is not considering the authSource option.
Using the mongojs, it works fine with the following code:
// connection string using mongojs:
var uri = 'mongodb://MY_USER:MY_PASSWORD#' +
'MY_SERVER-shard-00-00-clv3h.mongodb.net:27017,' +
'MY_SERVER-shard-00-01-clv3h.mongodb.net:27017,' +
'MY_SERVER-shard-00-02-clv3h.mongodb.net:27017/MY_DATABASE';
var options = {
ssl: true,
authSource: 'MY_ADMIN_DATABASE',
replicaSet: 'MY_REPLICASET_NAME-shard-0'
};
var db = mongojs(uri,'', options);
But, I need to use mongoose because the ODM in my project.
How can I build my uri and options variable using mongoose?

ON MONGODB 3.4.x
I resolved this issue putting the 'options' value directly in 'uri' string, according to documentation (http://mongoosejs.com/docs/connections.html) on 'Replica Set Connections' section.
// connection string using mongoose:
var uri = 'mongodb://MY_USER:MY_PASSWORD#' +
'MY_SERVER-shard-00-00-clv3h.mongodb.net:27017,' +
'MY_SERVER-shard-00-01-clv3h.mongodb.net:27017,' +
'MY_SERVER-shard-00-02-clv3h.mongodb.net:27017/MY_DATABASE' +
'ssl=true&replicaSet=MY_REPLICASET_NAME-shard-0&authSource=MY_ADMIN_DATABASE';
mongoose.connect(uri);
var db = mongoose.connection;
Now, it is working fine!
NOTICE WITH MONGODB 3.6
On MongoDB Atlas using the version 3.6.x, the connection string changed to use a DNS server making the link shorter.
mongodb+srv://MY_USER:MY_PASSWORD#MY_SERVER.mongodb.net/MY_DATABASE
...if you use this connection string in your application, this will connect with success but it will be able to read and write only with atlas users with higher privilegies access (atlasAdmin, readWriteAnyDatabase...).
To you work with an specific user with privilege only to readWrite your database, you will need to keep the same connection string used in MongoDB 3.4 because the mongoose not recognized the DNS option (mongodb+srv).
P.S. all the new resources from MongoDB 3.6.x will continue working normally!

Add username and password to database connection
mongodb://[username:password#]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
Standard Connection String Format

Related

Using Connection URIs with Read Replication in Sequelize

If I have a connection URI, I can use that normally with Sequelize as such:
const sequelize = new Sequelize('postgres://user:pass#example.com:5432/dbname');
However, if I want to use Read and Write replication (https://sequelize.org/master/manual/read-replication.html), then there doesn't seem an option to use connection URI. Can I pass connection URI strings to read and write in the replication option as in:
const sequelize = new Sequelize(null, null, null, {
dialect: 'postgres',
replication: {
read: [
'postgres://user:pass#reader.example.com:5432/dbname',
'postgres://user:pass#anotherreader.example.com:5432/dbname'
],
write: 'postgres://user:pass#writer.example.com:5432/dbname'
}
})
EDIT:
I have already found a solution to the issue. and that is using an npm library like connection string to parse the connection string as shown below:
const write_uri = new ConnectionString(uri);
const sequelize = new Sequelize(null, null, null, {
dialect: 'postgres',
replication: {
read: [
'postgres://user:pass#reader.example.com:5432/dbname',
'postgres://user:pass#anotherreader.example.com:5432/dbname'
],
write: {
host: write_uri.hosts[0].name,
username: write_uri.user,
password: write_uri.password,
database: write_uri.path[0],
port: write_uri.hosts[0].port
}
}
});
But, that is not what I'm looking for.
As per sequelize source at master, you can't.
According to the sequelize source docs at sequelize.js, The Sequelize Constructor accepts options agrument, like this
constructor(database, username, password, options){
}
Where options.replication should be an object with two properties, read and write. Write should be an object (a single server for handling writes), and read an array of object (several servers to handle reads). Each read/write server can have the following properties: host, port, username, password, database.
you need to pass an array of objects to read:[] with connection values as props instead of passing strings.
You can pass config object to sequelize constructor even if you use uri connection.
Look the example in from the docs:
https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor
// with uri
const sequelize = new Sequelize('mysql://localhost:3306/database', {})
Look at the constructor overloading definition:
Sequelize(uri: string, options?: Sequelize.Options): Sequelize.Sequelize
Just pass the options you need.

MongoError - Authentication error (password with #)

Here is my code in my NodeJS application, to connect to my MongoDB engine :
const collection = 'mynewcollection';
const password = 'passwordwithan#';
const mongoUrl = `mongodb://admin:${encodeURIComponent(password)}#mymongobase.net/${collection}`;
// Connect using the connection string
MongoClient.connect(mongoUrl, {useNewUrlParser: true}, function(err, db) {
console.log(err.toString())
});
I get an authentication error. I tried several things to handle the '#' character and reading the documentation I thought that it was the good one...
But it is still failing even if the user and password are the good one.
Is the API correctly used ? Do you understand what is wrong ?
Thanks in advance.
OK I found the solution.
If you use :
const mongoUrl = `mongodb://admin:${encodeURIComponent(password)}#mymongobase.net/${collection}`;
Mongo will try to connect with admin/password defined on the collection.
If you don't put the collection in the mongo url :
const mongoUrl = `mongodb://admin:${encodeURIComponent(password)}#mymongobase.net`;
Then you can use the super user defined on all the mongo engine.

What changes are to be made in code to move from Feathers nedb to Postgres?

I have used feathers-nedb library and wrote a server code in Node. Now I need to shift to Postgres DB. I have written models and data insert queries in feathers-nedb so is there way I don't mess up with the structure, but connect to Postgres instead and run the code.
There is a way. You can use the feathers-knex library. Just change the nedb model to feathers-knex and create a Model using knex Postgres connection string.
const dbPath = app.get('nedb');
const Model = new NeDB({
filename: path.join(dbPath, 'test.db'),
autoload: true
});
const Model = new knex({
client: 'pg',
connection: "postgres://postgres:password#localhost:5432/test",
searchPath: ['knex', 'public'],
});
This is the only code change that is required on the Model side. On the service side, instead of feathers-nedb use feathers-knex.

Trying to create a connection in Node.js using the npm light-orm and PostgreSQL (pg)

I am trying to create a postgreSQL connection using the light-orm.
I have it working in MySQL using:
var mysql = require('mysql'),
lightOrm = require('light-orm');
lightOrm.driver = mysql.createConnection({
host: "localhost",
user: "me",
password: "secret",
database: "test"
});
lightOrm.driver.connect();
However I cannot seem to get it to work with PostgreSQL.
What I have is:
var pg = require('pg'),
lightOrm = require('light-orm');
lightOrm.driver = new pg.Client('postgres://me:secret#localhost/test');
lightOrm.driver.connect();
I know that the connection is happening, because if I change the password to something incorrect, I get an error. But when trying to use the same code that works with MySQL, I either get an error, or nada.
My guess is this problem stems from my lack of knowledge of the pg module and not a light-orm issue.

Simplest way to create a connection and execute a query on replicaset with mongoose?

I'm trying to execute a specific query on replica set... but without success.
What is the simplest way to create a connection and execute a query on replicaset with mongoose?
My code-1:
var mongoose = require('mongoose');
mongoose.connect('mongodb://user:pass#host:port/db,user:pass#host2:port/db');
var someSchema = new mongoose.Schema({...}, { read: 'secondary' });
var some = mongoose.model('Some', someSchema);
some.find(...) -> err [Error: not authorized for query on bd.collection]
My code-2:
mongoose.connect('mongodb://user:pass#host:port/db,user:pass#host2:port/db?slaveOk=true');
some.find(...) -> err [Error: not authorized for query on bd.collection]
My code-3:
mongoose.connect('mongodb://user:pass#host:port/db,mongodb://user:pass#host2:port/db?slaveOk=true');
some.find(...) -> err [Error: not authorized for query on bd.collection]
My code-4:
mongoose.connect('mongodb://user:pass#end:port/db,mongodb://user:pass#end2:port/db);
some.find(...) -> err [Error: not authorized for query on bd.collection]
My code-5:
mongoose.connect('mongodb://user:pass#end:port/db,mongodb://user:pass#end2:port/db?slaveOk=true);
var someSchema = new mongoose.Schema({...});
var some = mongoose.model('Some', someSchema);
some.find(...).slaveOk() -> query executed on master (apparently)
some.find(...).read('secondary') -> query executed on master (apparently - no change on mongostat of replicaset. I'm mongoHQ user and I don't trust in mongostat from dashboard.)
SO, if I connect with mongo hostReplica:port/db -u user -p pass, and exec db.getMongo().setSlaveOk(), I can exec db.collection.find without problem.
What I'm doing wrong?
Thanks
Connecting to mongo client with readPreference option set to secondary:
mongodb://username:password#host:27017/database?readPreference=secondary
Not sure about slaveOk being deprecated, but readPreference always works.
I followed this doc and it works fine for me. My connection string for Mongoose looks like this:
mongodb://user:pass#host1,user:pass#host2/db?replicaSet=myReplica&readPreference=secondaryPreferred
I think that slaveOk is deprecated, and we should now use the readPreference option.
As I use Nodejs/Mongoose for a read-only api, I want to read from the slave(s), and keep the master for insertions mainly (which are done outside of Nodejs). That's why I use secondaryPreferred.

Resources