@fluojs/mongoose
v1.1.0
Published
Mongoose integration for Fluo with session-aware transaction context and lifecycle management.
Downloads
596
Maintainers
Readme
@fluojs/mongoose
Mongoose integration for fluo with session-aware transaction handling and lifecycle-friendly connection management.
Table of Contents
- Installation
- When to Use
- Quick Start
- Lifecycle and Shutdown
- Common Patterns
- Public API
- Related Packages
- Example Sources
Installation
pnpm add @fluojs/mongoose
pnpm add mongooseWhen to Use
- when Mongoose should plug into the same DI and application lifecycle as the rest of the app
- when MongoDB sessions and transactions need one shared wrapper instead of ad hoc session plumbing in every service
- when request-scoped transactions need explicit
requestTransaction(...)boundaries - when an application already creates and configures its concrete Mongoose connection and wants fluo to observe, not replace, that ownership
Quick Start
import { Module } from '@fluojs/core';
import { MongooseModule } from '@fluojs/mongoose';
import mongoose from 'mongoose';
const connection = mongoose.createConnection('mongodb://localhost:27017/test');
@Module({
imports: [
MongooseModule.forRoot({
connection,
dispose: async (conn) => conn.close(),
}),
],
})
class AppModule {}MongooseModule.forRootAsync(...) accepts injected dependencies and a useFactory that may return options synchronously or asynchronously. Use the exported MongooseAsyncModuleOptions<TConnection> type when sharing async registration helpers across modules. Pass global on the top-level async registration when the providers should be visible globally. The resolved options are reused within one application container, so connection setup and disposal hooks stay consistent across that container's providers. Reusing the same async module definition across tests or multi-app processes resolves fresh options per application container instead of sharing a memoized connection.
Lifecycle and Shutdown
MongooseModule registers MongooseConnection with the fluo application lifecycle. The package does not create or own the raw Mongoose connection for you; pass a concrete Mongoose connection object/function as connection, keep connection-string, pool, plugin, and model compilation ownership in the application, and provide a dispose hook when the application should close that external connection during shutdown.
Shutdown preserves transaction cleanup order and rejects new manual or request-scoped transaction boundaries once shutdown begins:
- Open request-scoped transactions are aborted with
Application shutdown interrupted an open request transaction. - Active ambient sessions are tracked until their transaction callback and session cleanup settle.
- Their Mongoose sessions finish
abortTransaction()andendSession()cleanup. - The configured
dispose(connection)hook runs only after active request transactions and ambient session scopes have settled.
MongooseConnection.createPlatformStatusSnapshot() and the exported low-level createMongoosePlatformStatusSnapshot(...) helper report ready while serving traffic, shutting-down while request transactions are draining, and stopped after the dispose hook completes. The status details include sessionStrategy, transactionContext: 'als', active request/session counts, resource ownership, and strict/session support diagnostics. Manual transaction() calls and service @Transaction() methods expose the same ambient session to conn.model(...); supported facade methods (create, find, findOne, aggregate, and bulkWrite) automatically attach that session. Automatic session injection is scoped to the MongooseConnection.model(...) wrapper method and does not replace or mutate the raw connection.model(...) cache/compile path returned by conn.current(). Use conn.currentSession() for unsupported model methods, doc.save(), or external utilities that need explicit session plumbing. If the wrapped Mongoose connection exposes connection.transaction(...), fluo delegates the transaction boundary to that API so Mongoose's own ambient-session scope is preserved while still exposing the same session through currentSession(). Request-scoped transactions observe the request AbortSignal while acquiring sessions and while starting delegated connection.transaction(...) work, so request cancellation can interrupt those startup phases before user callbacks run.
Nested requestTransaction(...) calls opened inside an existing manual transaction(...) boundary reuse the ambient session, stay visible in details.activeRequestTransactions, and are aborted during shutdown so the outer manual transaction can roll back before dispose(connection) runs.
Common Patterns
Service Transaction Boundary (@Transaction)
The @Transaction() decorator is the recommended way to define transaction boundaries in your service layer. It ensures that all repository calls made within the decorated method share the same MongoDB session.
import { Transaction } from '@fluojs/mongoose';
import { UserRepository } from './user.repository';
export class UserService {
constructor(private readonly repo: UserRepository) {}
@Transaction()
async onboardUser(dto: CreateUserDto) {
const user = await this.repo.create(dto);
await this.repo.initProfile(user._id);
return user;
}
}
export class UserRepository {
constructor(private readonly conn: MongooseConnection) {}
async create(data: any) {
// model() returns a session-aware facade inside @Transaction().
// Operations like create, find, findOne, aggregate, and bulkWrite
// automatically participate in the ambient transaction.
return this.conn.model('User').create(data);
}
async initProfile(userId: any) {
return this.conn.model('Profile').create({ userId });
}
}Calls to @Transaction() methods are reentrant. If a decorated method calls another decorated method, they share the same underlying MongoDB session. Note that doc.save() is not automatically session-aware in v1; use the supported facade operations (model.create(), model.find(), model.findOne(), model.aggregate(), or model.bulkWrite()) for automatic transaction participation.
Manual Transactions and currentSession()
The MongooseConnection provides currentSession() to access the ambient MongoDB session and current() to access the root connection handle. Use these as escape hatches when you need to pass sessions to external utilities or perform advanced manual plumbing.
import { MongooseConnection } from '@fluojs/mongoose';
export class AdvancedRepository {
constructor(private readonly conn: MongooseConnection) {}
async customOperation() {
const session = this.conn.currentSession();
const User = this.conn.current().model('User');
// Explicitly passing the session
return User.find({ status: 'active' }).session(session || null);
}
}Use conn.transaction() for manual transaction blocks:
await this.conn.transaction(async () => {
const User = this.conn.model('User');
await User.create([{ name: 'Ada' }]);
});If the wrapped connection implements connection.transaction(...), fluo treats that as the strict transaction boundary. Otherwise, when the connection does not implement startSession(), transactions use fail-open direct callback execution by default (strictTransactions: false), which is useful for local fakes and staged migrations but provides no rollback atomicity. Set strictTransactions: true for production flows that require MongoDB transaction guarantees; missing transaction support then makes readiness not-ready and causes transaction helpers to throw.
For supported facade methods, fluo preserves existing Mongoose operation options and only merges the ambient { session } into the correct options argument. If a model call passes an explicit { session: null } or a different session object inside an ambient transaction, fluo throws a session conflict error to prevent accidental transaction escapes.
Public API
MongooseModule.forRoot(options)/MongooseModule.forRootAsync(options)MongooseConnectionMongooseConnection.createPlatformStatusSnapshot()— reports health/readiness, resource ownership, active request/session drain counts, and strict transaction support diagnostics for platform observability surfaces.MongooseConnection.model(name, ...args)— returns the raw model outside transactions or a session-aware facade forcreate,find,findOne,aggregate, andbulkWriteinside an active transaction without mutating the underlying Mongoose connection.TransactionMONGOOSE_CONNECTION,MONGOOSE_DISPOSE,MONGOOSE_OPTIONScreateMongooseProviders(options)— compatibility/manual composition helper; preferMongooseModule.forRoot(...)orMongooseModule.forRootAsync(...)for application-facing registration so module exports and provider visibility stay aligned.createMongoosePlatformStatusSnapshot(...)connectionmust be a concrete object/function handle for both sync and async registration; missing handles are rejected during module registration or async bootstrap.Transactionis a standard TC39 method decorator for service-layer session transaction boundaries. It resolvesthis.conn, the decorated instance itself, or one unique nestedthis.*.conncollaborator by default; pass an accessor when theMongooseConnectionlives under a different field or resolution would be ambiguous.
Related exported types
MongooseModuleOptions<TConnection>MongooseAsyncModuleOptions<TConnection>MongooseConnectionLikeMongooseSessionLikeMongooseHandleProviderMongoosePlatformStatusSnapshotInput
Related Packages
@fluojs/runtime: manages startup and shutdown hooks@fluojs/http: provides request lifecycle primitives that can be paired with explicitrequestTransaction(...)boundaries@fluojs/prismaand@fluojs/drizzle: alternate database integrations with different transaction models
Example Sources
packages/mongoose/src/vertical-slice.test.tspackages/mongoose/src/module.test.tspackages/mongoose/src/public-api.test.ts
