Stable API For Drivers
- Status: Accepted
- Minimum Server Version: N/A
Abstract
As MongoDB moves toward more frequent releases (a.k.a. continuous delivery), we want to enable users to take advantage of our rapidly released features, without exposing applications to incompatible server changes due to automatic server upgrades. A stable API will help accomplish that goal.
META
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
This document tends to use "SHOULD" more frequently than other specifications, but mainly in the context of providing guidance on writing test files. This is discussed in more detail in Design Rationale.
Specification
Background
When applications interact with MongoDB, both the driver and the server participate in executing operations. Therefore, when determining application compatibility with MongoDB, both the driver and the server behavior must be taken into account.
An application can specify the server API version when creating MongoClient. When this is done:
- The client sends the specified API version to the server, causing the server to behave in a manner compatible with that API version.
- The driver will behave in a manner compatible with a server configured with that API version, regardless of the server's actual release version.
Presently there is no specification for how a driver must behave when a particular server API version is requested, or what driver operations are subject to API compatibility guarantees. Such requirements may be stipulated in subsequent specifications.
This specification requires MongoClient to validate that it supports the specified server API version, if any, but does not define what such support means.
MongoClient
changes
MongoClient
instances accept a new serverApi
option to allow the user to declare an API version:
class MongoClient {
MongoClient(... serverApi: ServerApi);
}
enum ServerApiVersion {
v1 = "1",
}
class ServerApi {
version: string|ServerApiVersion;
strict: Optional<Boolean>; // Default false
deprecationErrors: Optional<Boolean>; // Default false
}
Drivers SHOULD group the serverApi
option with other similar client options like autoEncryptionOpts
. Drivers MUST
NOT allow specification of any stable API options via the connection string. See the
Design Rationale for more details.
ServerApiVersion enumeration
This specification and subsequent specifications will define the known API versions. Drivers SHOULD define an
enumeration containing the known API versions, using the version identifiers given. Drivers MAY deviate from the version
identifiers used in this and subsequent specifications if doing so is necessary given the driver's programming
language's constraints. Drivers MUST ensure that adding new API versions to this enumeration does not result in backward
compatibility breaks in non-major releases. This can be the case in languages that allow exhaustive switch
statements
(e.g. Swift).
Drivers for languages that don't have enums (e.g. PHP) MUST expose the version as a string, but SHOULD offer constants
to allow for IDE features such as code completion. In these cases, the driver MUST validate (e.g. when the application
provides a version string to the ServerApi
class) that the version string is valid and trigger a client-side error if
an unknown API version was used.
ServerApi class
The ServerApi
class stores an API version, along with flags that decide whether or not unknown or deprecated commands
in the specified API version trigger a server-side error. A version
MUST be specified when declaring an API version,
while the strict
and deprecationErrors
options are both optional. The ServerApi
class is considered immutable;
changes to the declared API version MUST be prohibited.
Declared Version Inheritance
Drivers MUST ensure that users cannot override the API version declared in the MongoClient
instance. This includes the
MongoDatabase
and MongoCollection
classes, as well as any operations in these classes. See the rationale for more
details.
Sending Declared API Version to the Server
The declared API version MUST be sent to the server as part of every command request, with the exception of the cases
listed below. Drivers MUST NOT use a server's reported maxWireVersion
to decide whether it supports the stable API.
The server will reply with an error if the declared API version is not supported, or if the command does not support API
versioning options. If the user does not declare an API version, the driver MUST NOT send any API versioning options to
the server.
If an API version is declared then the driver MUST use OP_MSG
for all messages, including the initial handshake.
Command Syntax
The options from the declared API version are mapped to the following command options:
ServerApi field | Command option |
---|---|
version | apiVersion |
strict | apiStrict |
deprecationErrors | apiDeprecationErrors |
If an API version was declared, drivers MUST add the apiVersion
option to every command that is sent to a server.
Drivers MUST add the apiStrict
and apiDeprecationErrors
options if they were specified by the user, even when the
specified value is equal to the server default. Drivers MUST NOT add any API versioning options if the user did not
specify them. This includes the getMore
command as well as all commands that are part of a transaction. A previous
version of this specification excluded those commands, but that has since changed in the server.
Handshake behavior
If an API version was declared, drivers MUST NOT use the legacy hello command during the initial handshake or
afterwards. Instead, drivers MUST use the hello
command exclusively and use the OP_MSG
protocol. If the server does
not support hello
, the server description MUST reflect this with an Unknown
server type.
Cursors
For getMore
, drivers MUST submit API parameters. If the values given do not match the API parameters given in the
cursor-initiating command, the server will reply with an error.
Transactions
When running commands as part of a transaction, drivers MUST send API parameters with all commands that are part of a
transaction, including commitTransaction
and abortTransaction
. If the API parameters for a command in a transaction
do not match those of the transaction-starting command, the server will reply with an error.
Generic command helper
Drivers that offer a generic command helper (e.g. command()
or runCommand()
) MUST NOT inspect the command document
to detect API versioning options. As with all other commands, drivers MUST inherit the API version from the client.
Specifying API versioning options in the command document and declaring an API version on the client is not supported.
Drivers MUST document that the behaviour of the command helper is undefined in this case.
Design Rationale
No URI Options
Since changing the API version can cause the application to behave differently, drivers MUST NOT allow users to change the declared API version without deploying code changes. This ensures that users don't copy a connection string with a declared API version that may be different from what their application expects. A URI option can be added later if we realise our users need it, while the opposite is not easily accomplished.
Don't Allow Overriding the Declared API Version
While users are used to overriding options like read preference, read concern, and write concern in MongoDatabase
and
MongoCollection
objects, or on an operation level, we explicitly decided against this for the declared API version.
With a single API version available to start, we can't anticipate what use cases users may have to override the API
version. Not including this feature at the beginning allows us to gather feedback on use cases and add the features
users are looking for. On the other hand, adding the ability to override the declared API version can't be undone until
a future major release, which is almost impossible to accomplish across all drivers.
Generic Command Helper Behaviour
The runCommand helper is a way for the user to run a native command with the driver doing little to no inspection in the
command. This allows users to run arbitrary commands that may not have helpers in the driver, or to pass options that
are not supported by the driver version they are currently using. Commands run using this helper do not inherit any
readConcern
or writeConcern
options that may have been set on the MongoClient
or MongoDatabase
objects.
However, the declared API version is a different case. We are introducing this feature to give users a certain peace of mind when upgrading driver or server versions, by ensuring that their code will continue to show the same behaviour they've gotten used to. This includes all commands run using the generic command helper. Thus, the helper will inherit the API version declared on the client.
Hardcode supported versions in drivers
Since a new API version might require driver changes (e.g. to account for removed commands), we don't yet know what changes drivers must make for a future version. Until we do, we must prevent users from choosing any unknown API version.
Backward Compatibility
Driver changes are fully backward compatible. Not declaring an API version when creating a client may cause an error if
the server was started with the requireApiVersion
option enabled, but this is outside of driver control.
Future Work
Overriding the Declared API Version
In the future, we may want to allow users to override the declared API version on a MongoDatabase
, MongoCollection
,
or individual operation level. However, this is not necessary until there is a different API version and we have data on
why and how users would want to override the declared API version.
Stable CRUD API
Drivers may also want to provide specialized MongoClient
, MongoDatabase
, and MongoCollection
classes to only
include features that are part of the stable API. This is not covered in this specification.
Changelog
-
2024-09-10: Migrated from reStructuredText to Markdown.
-
2022-10-05: Remove spec front matter and reformat changelog.
-
2022-02-24: Rename Versioned API to Stable API
-
2022-01-14: Require
OP_MSG
for all messages including the initial step of
the handshake when using stable API. -
2021-05-05: Require sending stable API parameters with
getMore
and
transaction-continuing commands. -
2021-04-20: Require using
hello
when using the stable API. -
2021-04-10: Replaced usages of
acceptAPIVersion2
withacceptApiVersion2
.