Mongoose Adapter
The Mongoose adapter instruments MongoDB operations via Mongoose ODM. Supports all query types including find, findOne, aggregate, save, update, and delete operations.
Setup
typescript
import mongoose from 'mongoose';
import { createMonitor, expressContext, mongooseAdapter } from '@periodic/arsenic';
const app = express();
const monitor = createMonitor({
slowQueryThresholdMs: 200,
exporter: (event) => {
if (event.severity === 'critical') sendToPagerDuty(event);
else if (event.severity === 'warning') sendToSlack(event);
},
});
// 1. Attach Express context BEFORE routes
app.use(expressContext(monitor));
// 2. Connect to MongoDB
await mongoose.connect(process.env.MONGODB_URI);
// 3. Attach Mongoose adapter
mongooseAdapter(monitor, mongoose);
// Now all Mongoose queries are monitored automaticallyOrder matters
Call
mongooseAdapter() AFTER mongoose.connect(). The adapter instruments the connection that is established, not future connections.Supported operations
| Operation | Mongoose method | Signals detected |
|---|---|---|
find | Model.find() | hot_path, n_plus_one, unbounded_query, slow_query |
findOne | Model.findOne() | hot_path, unbounded_query, slow_query |
findById | Model.findById() | fast_query, indexed_lookup |
save | doc.save() | write_contention, slow_query |
updateOne | Model.updateOne() | write_contention, retry_loop |
aggregate | Model.aggregate() | slow_query, high_cpu, large_payload |
count | Model.countDocuments() | slow_query |
Event output
json
{
"type": "db.query",
"db": "mongodb",
"adapter": "mongoose",
"model": "User",
"operation": "findOne",
"durationMs": 312,
"slow": true,
"signals": ["hot_path", "unbounded_query"],
"severity": "critical",
"request": { "method": "GET", "route": "/api/users/:id" },
"callsite": { "file": "src/routes/users.ts", "line": 14 },
"metadata": { "limit": null },
"timestamp": "2025-02-11T15: 30: 45.123Z"
}N+1 detection with Mongoose
typescript
// BAD — triggers n_plus_one signal
const posts = await Post.find({ published: true });
for (const post of posts) {
const author = await User.findById(post.authorId); // N queries!
}
// GOOD — single query with populate
const posts = await Post.find({ published: true }).populate('author');Use .lean() for read-only queries
Adding
.lean() to read-only queries returns plain JavaScript objects instead of Mongoose Documents, reducing memory usage significantly. Arsenic detects this as a positive signal.