Register for the free MongoDB.live developer conference July 13 and 14. Click to register now.
HomeLearnHow-toUpgrade Fearlessly with the MongoDB Versioned API

Upgrade Fearlessly with the MongoDB Versioned API

Updated: Jun 02, 2021 |

Published: Jun 01, 2021

  • Atlas
  • MongoDB
  • API

By A. Jesse Jiryu Davis

Rate this article

Do you hesitate to upgrade MongoDB, for fear the new database will be incompatible with your existing code?

Once you've written and deployed your MongoDB application, you want to be able to upgrade your MongoDB database at will, without worrying that a behavior change will break your application. In the past, we've tried our best to ensure each database release is backward-compatible, while also adding new features. But sometimes we've had to break compatibility, because there was no other way to fix an issue or improve behavior. Besides, we didn't have a single definition of backward compatibility.

Solving this problem is more important now: We're releasing new versions four times a year instead of one, and we plan to go faster in the future. We want to help you upgrade frequently and take advantage of new features, but first you must feel confident you can upgrade safely. Ideally, you could immediately upgrade all your applications to the latest MongoDB whenever we release.

The MongoDB Versioned API is how we will make this possible. The Versioned API encompasses the subset of MongoDB commands that applications commonly use to read and write data, create collections and indexes, and so on. We commit to keeping these commands backward-compatible in new MongoDB versions. We can add new features (such as new command parameters, new aggregation operators, new commands, etc.) to the Versioned API, but only in backward-compatible ways.

We follow this principle:

For any API version V, if an application declares API version V and uses only behaviors in V, and it is deployed along with a specific version of an official driver, then it will experience no semantically significant behavior changes resulting from database upgrades so long as the new database supports V.

(What's a semantically insignificant behavior change? Examples include the text of some error message, the order of a query result if you don't explicitly sort it, or the performance of a particular query. Behaviors like these, which are not documented and don't affect correctness, may change from version to version.)

To use the Versioned API, upgrade to the latest driver and create your application's MongoClient like this:

1client = MongoClient(
2 "mongodb://host/",
3 api={"version": "1", "strict": True})

For now, "1" is the only API version. Passing "strict": True means the database will reject all commands that aren't in the Versioned API. For example, if you call replSetGetStatus, which isn't in the Versioned API, you'll receive an error:

1{
2 "ok" : 0,
3 "errmsg" : "Provided apiStrict:true, but replSetGetStatus is not in API Version 1",
4 "code" : 323,
5 "codeName" : "APIStrictError"
6}

Run your application's test suite with the new MongoClient options, see what commands and features you're using that are outside the Versioned API, and migrate to versioned alternatives. For example, "mapreduce" is not in the Versioned API but "aggregate" is. Once your application uses only the Versioned API, you can redeploy it with the new MongoClient options, and be confident that future database upgrades won't affect your application.

The mongosh shell now supports the Versioned API too:

1mongosh --apiVersion 1 --apiStrict

You may need to use unversioned features in some part of your application, perhaps temporarily while you are migrating to the Versioned API, perhaps permanently. The escape hatch is to create a non-strict MongoClient and use it just for using unversioned features:

1# Non-strict client.
2client = MongoClient(
3 "mongodb://host/",
4 api={"version": "1", "strict": False})
5
6client.admin.command({"replSetGetStatus": 1})

The "strict" option is false by default, I'm just being explicit here. Use this non-strict client for the few unversioned commands your application needs. Be aware that we occasionally make backwards-incompatible changes in these commands.

The only API version that exists today is "1", but in the future we'll release new API versions. This is exciting for us: MongoDB has a few warts that we had to keep for compatibility's sake, but the Versioned API gives us a safe way to remove them. Consider the following:

1client = MongoClient("mongodb://host")
2client.test.collection.insertOne({"a": [1]})
3
4# Strangely, this matches the document above.
5result = client.test.collection.findOne(
6 {"a.b": {"$ne": null}})

It's clearly wrong that {"a": [1]} matches the query {"a.b": {"$ne": null}}, but we can't fix this behavior, for fear that user's applications rely on it. The Versioned API gives us a way to safely fix this. We can provide cleaner query semantics in Version 2:

1# Explicitly opt in to new behavior.
2client = MongoClient(
3 "mongodb://host/",
4 api={"version": "2", "strict": True})
5
6client.test.collection.insertOne({"a": [1]})
7
8# New behavior: doesn't match document above.
9result = client.test.collection.findOne(
10 {"a.b": {"$ne": null}})

Future versions of MongoDB will support both Version 1 and 2, and we'll maintain Version 1 for many years. Applications requesting the old or new versions can run concurrently against the same database. The default behavior will be Version 1 (for compatibility with old applications that don't request a specific version), but new applications can be written for Version 2 and get the new, obviously more sensible behavior.

Over time we'll deprecate some Version 1 features. That's a signal that when we introduce Version 2, those features won't be included. (Future MongoDB releases will support both Version 1 with deprecated features, and Version 2 without them.) When the time comes for you to migrate an existing application from Version 1 to 2, your first step will be to find all the deprecated features it uses:

1# Catch uses of features deprecated in Version 1.
2client = MongoClient(
3 "mongodb://host/",
4 api={"version": "1",
5 "strict": True,
6 "deprecationErrors": True})

The database will return an APIDeprecationError whenever your code tries to use a deprecated feature. Once you've run your tests and fixed all the errors, you'll be ready to test your application with Version 2.

Version 2 might be a long way off, though. Until then, we're continuing to add features and make improvements in Version 1. We'll introduce new commands, new options, new aggregation operators, and so on. Each change to Version 1 will be an extension of the existing API, and it will never affect existing application code. With quarterly releases, we can improve MongoDB faster than ever before. Once you've upgraded to 5.0 and migrated your app to the Versioned API, you can always use the latest release fearlessly.

You can try out the Versioned API with the MongoDB 5.0 Release Candidate, which is available now from our Download Center.

#Appendix

Here's a list of commands included in API Version 1 in MongoDB 5.0. You can call these commands with version "1" and strict: true. (But of course, you can also call them without configuring your MongoClient's API version at all, just like before.) We won't make backwards-incompatible changes to any of these commands. In future releases, we may add features to these commands, and we may add new commands to Version 1.

#Safe Harbor

The development, release, and timing of any features or functionality described for our products remains at our sole discretion. This information is merely intended to outline our general product direction and it should not be relied on in making a purchasing decision nor is this a commitment, promise or legal obligation to deliver any material, code, or functionality.

Rate this article
MongoDB Icon
  • Developer Hub
  • Documentation
  • University
  • Community Forums

© MongoDB, Inc.