@abstraks-dev/mongodb-connection
v1.0.1
Published
MongoDB connection manager with caching and middleware for Lambda functions
Downloads
224
Maintainers
Readme
@abstraks-dev/mongodb-connection
MongoDB connection management for AWS Lambda functions with automatic caching, Secrets Manager integration, and middleware support.
Features
- 🔄 Automatic Connection Caching - Reuses connections across Lambda invocations
- 🔐 AWS Secrets Manager Integration - Securely fetch MongoDB URI from Secrets Manager
- 🛡️ Environment-aware - Supports dev/prod environments
- 🎯 Lambda Middleware - Easy-to-use middleware pattern for Lambda handlers
- 📊 Connection Statistics - Monitor connection health and status
- ⚡ Performance Optimized - Minimizes cold starts with efficient caching
Installation
npm install @abstraks-dev/mongodb-connection mongooseRequired Peer Dependencies:
mongoose- MongoDB ODM
Quick Start
Basic Usage
import { createMongoDBConnection } from '@abstraks-dev/mongodb-connection';
import { getSecret } from './secrets.js'; // Your Secrets Manager helper
// Create connection instance
const { connection, connectDB, withDBConnection } = createMongoDBConnection(
'auth', // service name
getSecret // secret retrieval function
);
// Connect manually
await connectDB();
// Use in Lambda handler with middleware
const myHandler = async (event, context, callback) => {
// MongoDB is already connected by middleware
const users = await User.find();
return { statusCode: 200, body: JSON.stringify(users) };
};
export const handler = withDBConnection(myHandler);Direct URI Usage
import { createSimpleConnection } from '@abstraks-dev/mongodb-connection';
const { connection, connectDB } = createSimpleConnection(
'mongodb://localhost:27017/mydb'
);
await connectDB();API Reference
createMongoDBConnection(serviceName, getSecret, options)
Factory function that creates a MongoDB connection with helpers.
Parameters:
serviceName(string, required) - Service name for Secrets Manager lookupgetSecret(function, required) - Async function to retrieve secrets:(secretName, key) => Promise<string>options(object, optional):mongoURI- Direct MongoDB URI (bypasses Secrets Manager)environment- Environment name (default:process.env.ENVIRONMENT || 'dev')
Returns:
{
connection: MongoDBConnection,
connectDB: () => Promise<MongooseClient>,
withDBConnection: (handler) => WrappedHandler
}Example:
// auth/service/helpers/connectDB.js
import { createMongoDBConnection } from '@abstraks-dev/mongodb-connection';
import { getSecret } from './secrets.js';
export const { connection, connectDB, withDBConnection } =
createMongoDBConnection('auth', getSecret);createSimpleConnection(mongoURI)
Factory function for simple connections with direct URI.
Parameters:
mongoURI(string, required) - MongoDB connection string
Returns:
{
connection: MongoDBConnection,
connectDB: () => Promise<MongooseClient>
}Example:
import { createSimpleConnection } from '@abstraks-dev/mongodb-connection';
const { connectDB } = createSimpleConnection(
process.env.MONGO_URI || 'mongodb://localhost:27017/dev'
);
await connectDB();MongoDBConnection Class
Core connection manager class.
Constructor
new MongoDBConnection(options);Options:
serviceName(string, required) - Service namegetSecret(function, optional) - Secret retrieval functionmongoURI(string, optional) - Direct MongoDB URIenvironment(string, optional) - Environment name (default: 'dev')
Methods
connect(): Promise<MongooseClient>
Connects to MongoDB. Automatically caches connection across Lambda invocations.
const client = await connection.connect();Connection Priority:
- Direct
mongoURIoption process.env.MONGO_URI- Secrets Manager lookup using
serviceNameandenvironment
Throws:
- Error if no MongoDB URI found
- Error if connection fails
disconnect(): Promise<void>
Disconnects from MongoDB and clears cache.
await connection.disconnect();isConnected(): boolean
Checks if connection is active.
if (connection.isConnected()) {
console.log('Connected!');
}getConnection(): MongooseClient | null
Returns cached Mongoose client or null.
const client = connection.getConnection();getConnectionState(): number
Returns Mongoose connection state:
0= disconnected1= connected2= connecting3= disconnecting
const state = connection.getConnectionState();getStats(): object
Returns connection statistics.
const stats = connection.getStats();
// {
// serviceName: 'auth',
// environment: 'dev',
// isConnected: true,
// readyState: 1,
// hasCache: true
// }Usage Patterns
Pattern 1: Lambda Middleware (Recommended)
The easiest way to use with Lambda functions:
// helpers/connectDB.js
import { createMongoDBConnection } from '@abstraks-dev/mongodb-connection';
import { getSecret } from './secrets.js';
export const { withDBConnection } = createMongoDBConnection('auth', getSecret);
// lambdas/getUser.js
import { withDBConnection } from '../helpers/connectDB.js';
import User from '../models/User.js';
const getUserHandler = async (event, context, callback) => {
// MongoDB connected automatically
const user = await User.findById(event.pathParameters.id);
callback(null, {
statusCode: 200,
body: JSON.stringify(user),
});
};
export const handler = withDBConnection(getUserHandler);Benefits:
- Automatic connection before handler runs
- Proper error handling with Lambda responses
- Works with both callback and return patterns
Pattern 2: Manual Connection
For more control over connection timing:
import { createMongoDBConnection } from '@abstraks-dev/mongodb-connection';
import { getSecret } from './secrets.js';
const { connectDB } = createMongoDBConnection('social', getSecret);
export const handler = async (event, context) => {
try {
// Connect when needed
await connectDB();
// Your logic here
const posts = await Post.find();
return {
statusCode: 200,
body: JSON.stringify(posts),
};
} catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: error.message }),
};
}
};Pattern 3: Shared Instance
Share connection across multiple files:
// helpers/db.js
import { createMongoDBConnection } from '@abstraks-dev/mongodb-connection';
import { getSecret } from './secrets.js';
export const { connection, connectDB, withDBConnection } =
createMongoDBConnection('media', getSecret);
// controllers/media.controllers.js
import { connection } from '../helpers/db.js';
export async function getMediaStats() {
// Check connection state
if (!connection.isConnected()) {
await connection.connect();
}
return await Media.countDocuments();
}
// lambdas/getMedia.js
import { withDBConnection } from '../helpers/db.js';
const handler = async (event) => {
// Connection managed by middleware
const media = await Media.find();
return { statusCode: 200, body: JSON.stringify(media) };
};
export default withDBConnection(handler);Environment Configuration
Development
// Use local MongoDB or environment variable
process.env.MONGO_URI = 'mongodb://localhost:27017/auth-dev';
const { connectDB } = createMongoDBConnection('auth', getSecret, {
environment: 'dev',
});Production
// Use AWS Secrets Manager
// Creates connection that will fetch from:
// Secret: "auth-prod"
// Key: "MONGO_URI"
const { connectDB } = createMongoDBConnection('auth', getSecret, {
environment: 'prod',
});Direct URI
// Bypass Secrets Manager entirely
const { connectDB } = createMongoDBConnection('auth', getSecret, {
mongoURI: 'mongodb://your-cluster.mongodb.net/production',
});Error Handling
The library throws descriptive errors for common issues:
try {
await connectDB();
} catch (error) {
if (error.message.includes('MONGO_URI not found')) {
console.error('MongoDB URI not configured');
} else if (error.message.includes('Error connecting')) {
console.error('Connection failed:', error);
}
}Migration from Service-Specific Code
Before (Duplicated in each service)
// auth/service/helpers/connectDB.js
import mongoose from 'mongoose';
import { getSecret } from './secrets.js';
mongoose.set('strictQuery', false);
let cachedClient = null;
export const connectDB = async () => {
if (cachedClient && mongoose.connection.readyState === 1) {
return cachedClient;
}
const environment = process.env.ENVIRONMENT || 'dev';
const mongoURI =
process.env.MONGO_URI ||
(await getSecret(`auth-${environment}`, 'MONGO_URI'));
if (!mongoURI) {
throw new Error(`MONGO_URI not found`);
}
const client = await mongoose.connect(mongoURI);
cachedClient = client;
return client;
};After (Using shared module)
// auth/service/helpers/connectDB.js
import { createMongoDBConnection } from '@abstraks-dev/mongodb-connection';
import { getSecret } from './secrets.js';
export const { connection, connectDB, withDBConnection } =
createMongoDBConnection('auth', getSecret);Savings: ~30 lines of code → 3 lines
Best Practices
1. Reuse Connection Instances
// ✅ Good - Create once, reuse everywhere
// helpers/db.js
export const { connectDB, withDBConnection } = createMongoDBConnection(...);
// ❌ Bad - Creates new instance each time
export function getConnection() {
return createMongoDBConnection(...);
}2. Use Middleware for Lambda Handlers
// ✅ Good - Automatic connection management
export const handler = withDBConnection(async (event) => {
// Connection guaranteed
});
// ❌ Okay but more verbose
export const handler = async (event) => {
await connectDB();
// Connection manual
};3. Check Connection State for Background Jobs
// ✅ Good - Verify before long-running operations
async function processQueue() {
if (!connection.isConnected()) {
await connection.connect();
}
// Process items
}4. Handle Secrets Manager Errors
// ✅ Good - Graceful error handling
try {
await connectDB();
} catch (error) {
console.error('Failed to connect:', error);
// Fallback or alert
}Testing
Mocking in Tests
import { jest } from '@jest/globals';
// Mock mongoose
const mockMongoose = {
set: jest.fn(),
connect: jest.fn(),
disconnect: jest.fn(),
connection: { readyState: 0 },
};
jest.unstable_mockModule('mongoose', () => ({ default: mockMongoose }));
// Mock secret retrieval
const mockGetSecret = jest
.fn()
.mockResolvedValue('mongodb://localhost:27017/test');
// Import and test
const { connectDB } = await import('@abstraks-dev/mongodb-connection');
const connection = createMongoDBConnection('test', mockGetSecret);Troubleshooting
"MONGO_URI not found" Error
Cause: No MongoDB URI found in any source
Solutions:
- Set
process.env.MONGO_URI - Ensure Secrets Manager has correct secret name format:
{serviceName}-{environment} - Pass direct URI via options:
{ mongoURI: '...' }
Connection Cached from Previous Lambda
Cause: Connection reuse across invocations (this is intentional for performance)
Solution: This is expected behavior. Connection is automatically reused. If you need to force reconnect:
await connection.disconnect();
await connection.connect();Mongoose StrictQuery Warnings
Cause: Mongoose version differences
Solution: The library automatically sets mongoose.set('strictQuery', false). No action needed.
Performance Considerations
- Cold Start: First connection takes ~500ms
- Warm Start: Cached connection is instant (<1ms)
- Memory: ~10MB per connection (Mongoose overhead)
- Invocations: Connection persists across Lambda invocations in same container
License
MIT
Related Packages
@abstraks/lambda-responses- Standardized Lambda response helpers@abstraks/api-key-auth- API key authentication with SSM@abstraks/jwt-auth- JWT authentication utilities
Support
For issues and questions:
