MongoDB
Category: Infrastructure
Integration type: Platform-level (sidecar service or Atlas)
External SDK: mongoose or mongodb (native driver)
Purpose
MongoDB is the document store for all data that doesn’t fit cleanly in relational tables — large blobs, free-form agent outputs, versioned content, and high-cardinality log entries:
| Collection | What it stores |
|---|---|
agent_outputs | Full text content from agent runs (blog posts, briefs, strategies) |
skills | Per-tenant Markdown skill files for Claude skill injection |
client_context_files | The assembled client context file per tenant |
contract_pdfs | Contract PDF binary metadata (actual PDF in S3) |
activity_logs | Verbose activity execution logs (not the audit trail — that’s in PostgreSQL) |
llm_responses | Raw NDJSON streams from Claude (for debugging and replay) |
PostgreSQL holds the structured references (IDs, status, foreign keys). MongoDB holds the blobs those references point to.
Config Structure
Platform config (env vars)
MONGODB_URI=mongodb://mongo:27017/leadmetrics # Docker service
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/leadmetrics # AtlasIntegration Pattern
Connection (packages/db-mongo/src/client.ts)
import { MongoClient } from 'mongodb';
let client: MongoClient;
export async function getMongoClient(): Promise<MongoClient> {
if (!client) {
client = new MongoClient(config.MONGODB_URI, {
serverSelectionTimeoutMS: 5000,
maxPoolSize: 20,
});
await client.connect();
}
return client;
}
export async function getDb(): Promise<Db> {
const c = await getMongoClient();
return c.db(); // Uses DB name from URI
}Key collection patterns
Storing agent output:
// After Blog Writer agent completes
const db = await getDb();
await db.collection('agent_outputs').insertOne({
_id: new ObjectId(),
tenantId: activity.tenantId,
activityId: activity.id,
agentType: 'blog_writer',
content: {
title: output.title,
html: output.html,
markdown: output.markdown,
wordCount: output.wordCount,
},
version: 1,
createdAt: new Date(),
});Storing and retrieving skills:
// Skills for a tenant (per agent type)
await db.collection('skills').updateOne(
{ tenantId, agentType: 'blog_writer' },
{
$set: {
files: [
{ name: 'client-context.md', content: clientContext },
{ name: 'blog-sop.md', content: blogSop },
{ name: 'brand-voice.md', content: brandVoice },
],
updatedAt: new Date(),
},
$setOnInsert: { createdAt: new Date() },
},
{ upsert: true },
);Multi-tenant isolation
Every document includes tenantId as a string field. Indexes on tenantId ensure queries remain fast even at scale:
// Indexes created on startup
await db.collection('agent_outputs').createIndex({ tenantId: 1, activityId: 1 });
await db.collection('skills').createIndex({ tenantId: 1, agentType: 1 }, { unique: true });
await db.collection('activity_logs').createIndex({ tenantId: 1, createdAt: -1 });
// TTL index for activity logs — auto-delete after 90 days
await db.collection('activity_logs').createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 60 * 60 * 24 * 90 },
);Docker Compose Setup
# docker-compose.yml
services:
mongo:
image: mongo:7.0
volumes:
- mongo_data:/data/db
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password
volumes:
mongo_data:Test Cases
Integration tests (real MongoDB)
MongoDB tests use mongodb-memory-server for in-process testing (unlike PostgreSQL which uses a real instance):
import { MongoMemoryServer } from 'mongodb-memory-server';
let mongod: MongoMemoryServer;
beforeAll(async () => {
mongod = await MongoMemoryServer.create();
process.env.MONGODB_URI = mongod.getUri();
await connect();
});
afterAll(async () => {
await mongod.stop();
});| Test | Approach |
|---|---|
| Upsert skill for tenant | Insert twice with same tenantId + agentType; assert single doc |
| Agent output stored and retrieved by activityId | Insert; query by activityId; assert content matches |
| TTL index on activity_logs | Verify index exists with expireAfterSeconds: 7776000 |
| Tenant isolation | Insert docs for two tenants; query with tenantId = A; assert B’s docs not returned |
Related
- Database Overview — when to use MongoDB vs PostgreSQL
- MongoDB Collections — full collection schemas
- NoSQL Entity Relations — cross-references to PostgreSQL
- Skills System — how skills are stored and injected