nest-profiler-mongoose
Profile Mongoose queries and aggregations in the MongoDB panel.
@eleven-labs/nest-profiler-mongoose
@eleven-labs/nest-profiler-mongoose captures every Mongoose query and aggregation executed during a profiled execution and displays them in a dedicated MongoDB panel.

Installation
pnpm add @eleven-labs/nest-profiler-mongoose@alphaThere is no stable release yet — install every
@eleven-labs/nest-profiler*package with the@alphadist-tag (@latestresolves to nothing).
Peer dependencies: mongoose ^9.0.0, @nestjs/mongoose ^11.0.0
Setup
import { ConditionalModule } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { MongooseCollectorModule } from '@eleven-labs/nest-profiler-mongoose';
const isProfilerEnabled = (env: NodeJS.ProcessEnv) => env['PROFILER_ENABLED'] === 'true';
@Module({
imports: [
MongooseModule.forFeature([{ name: Review.name, schema: ReviewSchema }]),
ConditionalModule.registerWhen(
MongooseCollectorModule.forRoot({
slowThreshold: 100,
nPlusOneThreshold: 2,
slowSeverity: 'warning',
}), // slow/N+1 tagging + severity
isProfilerEnabled,
),
],
})
export class AppModule {}MongooseModule.forRoot() (or forRootAsync) must be registered in AppModule before using MongooseCollectorModule.
Enabling / disabling — gate the collector with
ConditionalModule.registerWhen(..., isProfilerEnabled)as shown, so it loads only whenPROFILER_ENABLEDis on. Wire the coreProfilerModuleonce at the root — the recommended setup bundles the root-level profiler modules into a singleProfilingModulebehind aConditionalModulegate (see Enabling and disabling the profiler and the example app). A top-levelenabledoption is also supported as an alternative.
What it collects
For each Mongoose query or aggregation executed during a request:
| Field | Description |
|---|---|
collection | MongoDB collection name (e.g. reviews) |
operation | Mongoose operation (e.g. find, aggregate) |
filter | Query filter object (if applicable) |
duration | Execution time in ms |
startedAt | Unix timestamp |
count | Documents returned (reads) or affected (writes) |
error | Error message if the query failed |
streaming | true for streaming reads (Query.cursor() / Aggregate.cursor()) |
connection | Connection endpoint host:port (no credentials) |
database | Target database name |
fingerprint | collection + operation + filter shape, for N+1 grouping |
tags | Performance tags applied by the core rule engine |
Slow queries, N+1 patterns and silent zero-count delete/updates (the zero-rows tag) are flagged by the core rule engine and shown as coloured pills (and filterable on the list page). See Performance tags.
Toolbar badge
The toolbar badge shows: {n}q (e.g., 4q). When slow queries are present: 4q (1 slow).
How it works
At module initialization, the collector patches mongoose.Query.prototype.exec and mongoose.Aggregate.prototype.exec on the Mongoose instance retrieved from connection.base. This captures all queries regardless of when schemas were registered, and is fully transparent — Mongoose behavior is unchanged.
Streaming reads — Query.cursor() and Aggregate.cursor() bypass exec(), so they are patched too. The read is recorded (with streaming: true) at cursor creation, so it is captured whatever the consumption pattern. Its duration is finalized from the cursor's terminal close/end/error events when they fire — which they do for flowing / pipe() / explicit close() consumption, but not for for await or eachAsync() on a Mongoose cursor (they emit no terminal event); those keep duration: 0 and are labelled not timed (stream) in the panel's Duration column. Measuring their duration would require wrapping the row iterator, a per-document cost we avoid. Streamed row counts are not captured.
Schema panel
MongooseSchemaCollectorModule adds a global Schema · Mongoose panel to the profiler home page, listing every registered model with its fields (type, required, _id, default), references (ref → target model) and indexes (name, columns, unique). Unlike the per-request MongoDB panel, this is static process-level data introspected once at startup — so it renders on the list page next to the Config panel, not inside a profile.

import { MongooseSchemaCollectorModule } from '@eleven-labs/nest-profiler-mongoose';
ConditionalModule.registerWhen(MongooseSchemaCollectorModule.forRoot(), isProfilerEnabled),Pass connectionName to introspect a named connection (omit it for the default), and enabled: false to disable per environment. The panel reads each model's schema.paths and schema.indexes() and never touches data; path defaults are passed through the profiler's redactString, so a default embedding a secret is masked. The panel no-ops (does not appear) when no Mongoose connection is wired.