Skip to main content

MongoDB adapter

Installation

npm install @nestjs-cls/transactional-adapter-mongodb

Registration

ClsModule.forRoot({
plugins: [
new ClsPluginTransactional({
imports: [
// module in which the MongoClient instance is provided
MongoDBModule,
],
adapter: new TransactionalAdapterMongoDB({
// the injection token of the MongoClient
mongoClientToken: MONGO_CLIENT,
}),
}),
],
});

Typing & usage

To work correctly, the adapter needs to inject an instance of MongoClient

Due to how transactions work in MongoDB, the usage of the MongoDBAdapter adapter is a bit different from the others.

The tx property on the TransactionHost<TransactionalAdapterMongoDB> does not refer to any transactional instance, but rather to a ClientSession instance with an active transaction, or undefined when no transaction is active.

Queries are not executed using the ClientSession instance, but instead the ClientSession instance or undefined is passed to the query as the session option.

important

The TransactionalAdapterMongoDB does not support the "Transaction Proxy" feature, because proxying an undefined value is not supported by the JavaScript Proxy.

Example

user.service.ts
@Injectable()
class UserService {
constructor(private readonly userRepository: UserRepository) {}

@Transactional()
async runTransaction() {
// both methods are executed in the same transaction
const user = await this.userRepository.createUser('John');
const foundUser = await this.userRepository.getUserById(user._id);
assert(foundUser._id === user._id);
}
}
user.repository.ts
@Injectable()
class UserRepository {
constructor(
@Inject(MONGO_CLIENT)
private readonly mongoClient: MongoClient, // use a regular mongoClient here
private readonly txHost: TransactionHost<TransactionalAdapterMongoDB>,
) {}

async getUserById(id: ObjectId) {
// txHost.tx is typed as ClientSession
return this.mongoClient.db('default').collection('user').findOne(
{ _id: id },
{ session: this.txHost.tx }, // here, the `tx` is passed as the `session`
);
}

async createUser(name: string) {
const created = await this.mongo
.db('default')
.collection('user')
.insertOne(
{ name: name, email: `${name}@email.com` },
{ session: this.txHost.tx }, // here, the `tx` is passed as the `session`
);
const createdId = created.insertedId;
const createdUser = await this.getUserById(createdId);
return createdUser;
}
}
note

Queries don't have to be run using the "raw" MongoClient. You can as well use collection aliases and whatnot. What is important, is that they all reference the same underlying MongoClient instance, and that you pass the tx as the session option to the query.