One of the major reasons for the mongoose 5 release was the MongoDB driver removing support for the authenticate()
function. Up until Mongoose 4.11, that function was the only way mongoose supported doing authentication. In Mongoose 4.11.0, we added the annoying but necessary useMongoClient
deprecation warning, and in mongoose 5.0.0-rc0 we deleted 686 lines of legacy connection logic that removed the need for the useMongoClient
option. In addition, we made a couple related improvements to the connection API that will make mongoose much cleaner to work with: we made mongoose.connect()
always return a promise, and we added a global and connection-level bufferCommands
option.
Consistently Return a Promise for mongoose.connect()
Here's a thought exercise for you: what happens when JavaScript promise resolves to itself?
const promise = new Promise(resolve => {
setImmediate(() => resolve(promise));
});
If you said "infinite recursion", you're right. Newer versions of Node.js actually catch chaining cycles like this and print a handy error:
TypeError: Chaining cycle detected for promise #<Promise>
We previously made connect()
return a promise in mongoose 4.4.0, but quickly realized this was a backwards breaking change because people relied on the fact that mongoose.connect()
returned the mongoose singleton and fixed it in 4.4.1. But then, we also found that people also created promise wrappers around mongoose.connect()
that relied on mongoose.connect()
returning the mongoose singleton. So, in order to support mongoose.connect()
returning something that was promise-like but also looked like mongoose
we created the MongooseThenable
class, which wrapped the mongoose singleton but also had a .then()
and .catch()
and resolved to the original mongoose singleton.
In mongoose 5, we cleaned up this spaghetti and made mongoose.connect()
return a promise that resolves to the mongoose singleton.
const mongoose = require('mongoose');
// Prints "Mongoose { ... }"
mongoose.connect('mongodb://localhost:27017/test').then(res => console.log(res));
This means you can no longer chain .on()
or .model()
onto mongoose.connect()
:
// No longer works in mongoose 5
mongoose.connect('mongodb://localhost:27017/test').connection.
on('error', handleErr).
model('Test', new Schema({ name: String }));
// Do this instead
mongoose.connect('mongodb://localhost:27017/test').catch(handleErr);
// Or this
mongoose.connect('mongodb://localhost:27017/test');
mongoose.connection.on('error', handleErr);
mongoose.model('Test', new Schema({ name: String }));
This also means mongoose.connect()
now works nicely with async/await:
const ret = await mongoose.connect('mongodb://localhost:27017/test');
ret === mongoose; // true
As for mongoose.createConnection()
, mongoose still supports using the return value of mongoose.createConnection()
as either a promise or as a connection.
// Works
mongoose.createConnection().then(conn => conn.model('Test', schema));
// Also works
const conn = mongoose.createConnection();
conn.model('Test', schema);
Mongoose does this by deleting conn.then()
before resolving the promise, so if you try to call .then()
after the connection has successfully connected, you will get an error:
const conn = mongoose.createConnection();
conn.then(() => {
console.log(conn.then); // undefined
});
We don't believe this to be a major limitation, because await
doesn't throw an error if .then()
is not present. Please open up an issue on GitHub if this causes problems for you.
const conn = mongoose.createConnection();
console.log(conn.then); // [Function]
console.log(await conn); // The connection object
console.log(conn.then); // undefined
console.log(await conn); // The connection object
Global and Connection-Level bufferCommands
Mongoose schemas have a bufferCommands
option that disables mongoose buffering, which is the mechanism that queues up operations until connection is established.
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
const schema = new mongoose.Schema({ name: String });
const User = mongoose.model('User', schema);
// Will still work, even though connection hasn't been established when
// `create()` is called
User.create({ name: 'Val' }).then(doc => console.log(doc));
In mongoose 4, you had to disable bufferCommands
at the schema level, as well as disable the MongoDB driver's buffering mechanism by setting bufferMaxEntries
at the connection level:
const mongoose = require('mongoose');
// `bufferMaxEntries: 0` disables the MongoDB driver's buffering
// See http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html
mongoose.connect('mongodb://localhost:27017/test', { bufferMaxEntries: 0 });
const schema = new mongoose.Schema({ name: String }, { bufferCommands: false });
const User = mongoose.model('User', schema);
// Will throw an error because mongoose is not connected yet
User.create({ name: 'Val' }).then(doc => console.log(doc));
Disabling buffering is important if you want to report an error immediately if you try to execute an operation when mongoose is disconnected because the MongoDB server is down. However, setting it on the schema level is inconvenient. In mongoose 5, you can set it on the individual connection:
mongoose.connect('mongodb://localhost:27017/test', {
bufferMaxEntries: 0, // MongoDB driver buffering
bufferCommands: false // Mongoose-specific buffering
});
You can also globally disable buffering.
mongoose.set('bufferCommands', false);
mongoose.connect('mongodb://localhost:27017/test', {
bufferMaxEntries: 0 // MongoDB driver buffering
});
Keep in mind that the connection-level bufferCommands
option overrides the global bufferCommands
option, and schema-level bufferCommands
overrides both.
mongoose.set('bufferCommands', false); // Disable buffering globally
mongoose.connect('mongodb://localhost:27017/test', {
bufferCommands: true // But enable it for this connection
});
mongoose.model('Test', new Schema({}, {
bufferCommands: false // But disable it for this model
}));
Moving On
Mongoose 5.0.0 was formally released this week! These are just 2 of the 38 changes and improvements we've made for mongoose 5. You can find more details on the backwards breaking changes in mongoose 5 on GitHub. Download mongoose 5 with npm install mongoose
and let us know what you think on GitHub.