The following is a detailed collection of the changes in the major v5 release of the mongodb package for Node.js.
- Changes
- Build and Dependency Changes
- API Changes
- Custom Promise library support removed
Collection.insert,Collection.update, andCollection.removeremovedCollection.mapReduce()helper removedBulkWriteResultno longer contains a publicly enumerableresultpropertyBulkWriteResultnow contains individual result properties- Bulk results no longer contain
lastOp()andopTime BulkWriteOptions.keepGoingoption removedWriteConcernError.err()removedAddUserOptions.digestPasswordremoved- Kerberos option
gssapiCanonicalizeHostNameremoved ObjectIDtype removed in favor ofObjectIdslaveOkoptions removed- Cursors now implement
AsyncGeneratorinterface instead ofAsyncIterator
- Runtime Changes
- Dead Code Cleanup
Node.js driver v5 drops support for callbacks in favor of a Promise-only API. Below are some strategies for callback users to adopt v5 of the driver in order of most recommended to least recommended.
The Node.js driver team understands that a callback to Promise migration can be a non-trivial refactor. To help inform your migration strategy, we've outlined three different approaches below.
The Node team strongly encourages anyone who is able to migrate from callbacks to Promises to do so. Migrating to the driver's Promise-based API will streamline the adoption of future driver updates, as well as provide better TypeScript support than the other options outlined in this document.
The Promise-based API is identical to the callback API except:
- no callback is accepted as the last argument
- a Promise is always returned
For example, with a findOne query:
// callback-based API
collection.findOne({ name: 'john snow' }, (error, result) => {
if (error) {
/* do something with error */
return;
}
/* do something with result */
});
// Promise-based API
collection
.findOne({ name: 'john snow' })
.then(() => {
/* do something with result */
})
.catch(error => {
/* do something with error */
});
// Promise-based API with async/await
try {
const result = await collection.findOne({ name: 'john snow' });
/* do something with result */
} catch (error) {
/* do something with error */
}If you only have a few callback instances where you are currently unable to adopt the Promise API, we recommend using the Promise API and Node.js' callbackify
utility to adapt the Promise-based API to use callbacks.
Note Manually converting a Promise-based API to a callback-based API is error prone. We strongly encourage the use of callbackify.
We recommend using callbackify with an anonymous function that has the same signature as the collection method.
const callbackFindOne = callbackify((query, options) => collection.findOne(query, options));
callbackFindOne({ name: 'john snow' }, {}, (error, result) => {
// handle error or result
});If your application uses callbacks and you are not ready to use Promises, support for your workflow has not been removed. We have migrated callback support to a new package:
The package wraps all of the driver's asynchronous operations that previously supported both Promises and callbacks. All the wrapped APIs offer callback support via an optional callback argument alongside a Promise return value so projects with mixed usage will continue to work.
mongodb-legacy is intended to preserve driver v4 behavior to enable a smoother transition between
driver v4 and v5. However, new features will only support a Promise-based API in both the driver
and the legacy driver.
After installing the package and modifying imports, the following example demonstrates equivalent usages of either async/await syntax, .then/.catch chaining, or callbacks:
// Just add '-legacy' to my mongodb import
import { MongoClient } from 'mongodb-legacy';
const client = new MongoClient();
const db = client.db();
const collection = db.collection('pets');
// Legacy projects may have intermixed API usage:
app.get('/endpoint_async_await', async (req, res) => {
try {
const result = await collection.findOne({});
res.end(JSON.stringify(result));
} catch (error) {
res.errorHandling(error);
}
});
app.get('/endpoint_promises', (req, res) => {
collection
.findOne({})
.then(result => res.end(JSON.stringify(result)))
.catch(error => res.errorHandling(error));
});
app.get('/endpoint_callbacks', (req, res) => {
collection.findOne({}, (error, result) => {
if (error) return res.errorHandling(error);
res.end(JSON.stringify(result));
});
});NOTE: This is a TypeScript compile-time only change. Dot notation in filters sent to MongoDB will still work the same.
MongoDB Node.js Driver v4.3.0 introduced TypeScript support for dot notation in filter predicates. For example:
interface Schema {
user: {
name: string;
};
}
declare const collection: Collection<Schema>;
// compiles pre-v4.3.0, fails in v4.3.0+
collection.find({ 'user.name': 4 });This change caused a number of problems for users, including slow compilation times and compile errors for valid dot notation queries. While we have tried to mitigate this issue as much as possible in v4, ultimately we do not believe that this feature is fully production ready for all use cases.
Driver 5.0 removes type checking for dot notation in filter predicates. The preceding example will compile with driver v5.
Although we removed support for type checking on dot notation filters by default, we have preserved the corresponding types in an experimental capacity.
These helper types can be used for type checking. We export the StrictUpdateFilter and the StrictFilter
types for type safety in updates and finds.
To use one of the new types, simply create a predicate that uses dot notation and assign it the type of StrictFilter<your schema>.
interface Schema {
user: {
name: string;
};
}
declare const collection: Collection<Schema>;
// fails to compile, 4 is not assignable to type "string"
const filterPredicate: StrictFilter<Schema> = { 'user.name': 4 };
collection.find(filterPredicate);NOTE As an experimental feature, these types can change at any time and are not recommended for production settings.
The new minimum supported Node.js version is now 14.20.1.
The bson-ext package will no longer automatically import and supplant the bson dependency.
@aws-sdk/credential-providers has been added to the package.json as a peerDependency that is optional.
This means npm will let you know if the version of the SDK you have installed is incompatible with the driver.
npm install --save @aws-sdk/credential-providers@3.186.0snappy compression has been added to the package.json as a peerDependency that is optional.
This means npm will let you know if the version of snappy you have installed is incompatible with the driver.
npm install --save "snappy@^7.2.2"The MongoClient option promiseLibrary along with the Promise.set export that allows specifying a custom Promise library has been removed.
This allows the driver to adopt async/await syntax which has performance benefits over manual Promise construction.
Three legacy operation helpers on the collection class have been removed:
| Removed API | API to migrate to |
|---|---|
insert(document) |
insertOne(document) |
insert(arrayOfDocuments) |
insertMany(arrayOfDocuments) |
update(filter) |
updateMany(filter) |
remove(filter) |
deleteMany(filter) |
The insert method accepted an array of documents for multi-document inserts and a single document for single-document inserts. insertOne should now be used for single-document inserts and insertMany should be used for multi-document inserts.
// Single document insert:
await collection.insert({ name: 'spot' });
// Migration:
await collection.insertOne({ name: 'spot' });
// Multi-document insert:
await collection.insert([{ name: 'fido' }, { name: 'luna' }]);
// Migration:
await collection.insertMany([{ name: 'fido' }, { name: 'luna' }]);The mapReduce helper has been removed from the Collection class. The mapReduce operation has been
deprecated in favor of the aggregation pipeline since MongoDB server version 5.0. It is recommended
to migrate code that uses Collection.mapReduce to use the aggregation pipeline (see Map-Reduce to Aggregation Pipeline).
If the mapReduce command must be used, the Db.command() helper can be used to run the raw mapReduce command.
// using the Collection.mapReduce helper in < 4.x drivers
const collection = db.collection('my-collection');
await collection.mapReduce(
function () {
emit(this.user_id, 1);
},
function (k, vals) {
return 1;
},
{
out: 'inline',
readConcern: 'majority'
}
);
// manually running the command using `db.command()`
const command = {
mapReduce: 'my-collection',
map: 'function() { emit(this.user_id, 1); }',
reduce: 'function(k,vals) { return 1; }',
out: 'inline',
readConcern: 'majority'
};
await db.command(command);Note When using the Db.command() helper, all mapReduce options should be specified
on the raw command object and should not be passed through the options object.
To access the raw result, please use bulkWriteResult.getRawResponse().
These can be accessed via:
bulkWriteResult.insertedCount;
bulkWriteResult.matchedCount;
bulkWriteResult.modifiedCount;
bulkWriteResult.deletedCount;
bulkWriteResult.upsertedCount;
bulkWriteResult.upsertedIds;
bulkWriteResult.insertedIds;The lastOp() method and opTime property on the BulkResult have been removed. Merging of bulk results
no longer normalizes the values. There is no new method or property to replace them.
The keepGoing option on the BulkWriteOptions has been removed. Please use the ordered option instead.
The err() getter on the WriteConcernError class has been removed. The toJSON() method can be used in place of err().
The digestPassword option has been removed from the add user helper.
gssapiCanonicalizeHostName has been removed in favor of the CANONICALIZE_HOST_NAME value.
For clarity, the deprecated and duplicate export ObjectID has been removed. ObjectId matches the class name and is equal in every way to the capital "D" export.
The deprecated slaveOk option and slaveOk() method on the Collection class have been removed. Please
now use secondaryOk as the replacement for the option and the method.
All cursor types have been changed to implement AsyncGenerator instead of AsyncIterator.
This was done to make our typing more accurate.
Cursors will now automatically close when exiting a for await ... of loop on the cursor itself.
const cursor = collection.find({});
for await (const doc of cursor) {
console.log(doc);
break;
}
cursor.closed; // trueEverywhere the driver sends a hello command (initial handshake and monitoring), it will now pass the command value as 1 instead of the
previous true. This change was made for specification compliance reasons.
Both the logger and the logLevel options had no effect and have been removed.
Options can no longer be provided to Cursor.close(). This removes support for the skipKillCursors option that was unused.
The .unref() method was a no-op and has now been removed from the Db class.
The fullResponse option on the CommandOperationOptions was unused in the driver and has been removed.
Both of these types were unused but exported. These types have been removed. Please use Document instead.
The following types are used internally by the driver but were accidentally exported. They have now been marked internal and are no longer exported.
ServerSelectorPipeOptionsServerOptions