ConnectionHandler
The ConnectionHandler class manages MongoDB database connections for Esix. It handles connection pooling, supports multiple database adapters (including a mock adapter for testing), and provides a singleton instance for consistent connection management throughout your application.
Overview
ConnectionHandler uses environment variables to configure database connections and automatically manages connection pooling behind the scenes. You typically don’t need to interact with this class directly, as Esix models use it internally.
Singleton Instance
Esix exports a singleton instance that should be used throughout your application:
import { connectionHandler } from 'esix';
Environment Variables
ConnectionHandler reads the following environment variables for configuration:
DB_URL
string
default:"'mongodb://127.0.0.1:27017/'"
MongoDB connection URL/URI
DB_DATABASE
string
default:"''"
required
Name of the database to connect to
DB_ADAPTER
string
default:"'default'"
Database adapter to use. Options: 'default' (real MongoDB) or 'mock' (for testing)
Maximum number of connections in the connection pool
(Deprecated) Legacy alias for DB_MAX_POOL_SIZE. Use DB_MAX_POOL_SIZE instead.
Example .env file
DB_URL=mongodb://localhost:27017/
DB_DATABASE=myapp
DB_ADAPTER=default
DB_MAX_POOL_SIZE=20
Methods
getConnection()
Returns a connection to the database. Connections are pooled automatically, so multiple calls return connections from the same pool.
async getConnection(): Promise<Db>
MongoDB database instance from the mongodb driver
Example
import { connectionHandler } from 'esix';
const db = await connectionHandler.getConnection();
const collection = db.collection('users');
const users = await collection.find({}).toArray();
Note: You typically don’t need to call this directly, as Esix models handle connections internally. However, it’s useful for custom MongoDB operations or migrations.
closeConnections()
Manually closes all open database connections. This is useful for gracefully shutting down your application or cleaning up after tests.
async closeConnections(): Promise<void>
Promise that resolves when all connections are closed
Examples
import { connectionHandler } from 'esix';
// Graceful shutdown
process.on('SIGTERM', async () => {
await connectionHandler.closeConnections();
process.exit(0);
});
// After tests
afterAll(async () => {
await connectionHandler.closeConnections();
});
Note: After closing connections, new queries will establish a fresh connection automatically.
Connection Pooling
ConnectionHandler uses MongoDB’s built-in connection pooling for optimal performance:
- Automatic pooling: Connections are reused across queries
- Configurable size: Control pool size via
DB_MAX_POOL_SIZE
- Thread-safe: Multiple queries can run concurrently using connections from the pool
- Automatic reconnection: Handles connection failures gracefully
// All these queries use connections from the same pool
const users = await User.all();
const posts = await Post.where('status', 'published').get();
const comments = await Comment.where('approved', true).get();
Database Adapters
Default Adapter
The default adapter connects to a real MongoDB instance using the official MongoDB Node.js driver.
Configuration
DB_ADAPTER=default
DB_URL=mongodb://localhost:27017/
DB_DATABASE=production
Use cases:
- Production environments
- Development with local or remote MongoDB
- Staging environments
Mock Adapter
The mock adapter uses mongo-mock for in-memory database operations, perfect for testing without a real MongoDB instance.
Configuration
DB_ADAPTER=mock
DB_DATABASE=test
Use cases:
- Unit tests
- Integration tests
- CI/CD pipelines without MongoDB
- Local development without MongoDB installed
Example test setup
// test/setup.ts
process.env.DB_ADAPTER = 'mock';
process.env.DB_DATABASE = 'test';
import { connectionHandler } from 'esix';
afterEach(async () => {
// Clean up after each test
const db = await connectionHandler.getConnection();
const collections = await db.collections();
for (const collection of collections) {
await collection.deleteMany({});
}
});
afterAll(async () => {
await connectionHandler.closeConnections();
});
Advanced Usage
Custom MongoDB Operations
While Esix models provide most functionality you need, you can access the raw MongoDB database for custom operations:
import { connectionHandler } from 'esix';
const db = await connectionHandler.getConnection();
// Create a custom index
await db.collection('users').createIndex(
{ email: 1 },
{ unique: true }
);
// Use MongoDB aggregation
const results = await db.collection('orders').aggregate([
{ $match: { status: 'completed' } },
{ $group: { _id: '$userId', total: { $sum: '$amount' } } }
]).toArray();
// Perform bulk operations
const bulk = db.collection('products').initializeUnorderedBulkOp();
bulk.find({ category: 'electronics' }).update({ $inc: { views: 1 } });
await bulk.execute();
Database Migrations
Use ConnectionHandler to write database migration scripts:
// migrations/001-add-user-indexes.ts
import { connectionHandler } from 'esix';
async function up() {
const db = await connectionHandler.getConnection();
await db.collection('users').createIndex(
{ email: 1 },
{ unique: true, background: true }
);
await db.collection('users').createIndex(
{ createdAt: -1 },
{ background: true }
);
}
async function down() {
const db = await connectionHandler.getConnection();
await db.collection('users').dropIndex('email_1');
await db.collection('users').dropIndex('createdAt_-1');
}
export { up, down };
Graceful Shutdown
Implement graceful shutdown in your application to properly close database connections:
import { connectionHandler } from 'esix';
import express from 'express';
const app = express();
const server = app.listen(3000);
// Handle shutdown signals
const shutdown = async (signal: string) => {
console.log(`${signal} received, closing connections...`);
// Stop accepting new requests
server.close(async () => {
console.log('HTTP server closed');
// Close database connections
await connectionHandler.closeConnections();
console.log('Database connections closed');
process.exit(0);
});
// Force shutdown after 10 seconds
setTimeout(() => {
console.error('Forced shutdown after timeout');
process.exit(1);
}, 10000);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
Multiple Database Connections
By default, Esix uses a single ConnectionHandler instance. If you need to connect to multiple databases, you can create separate ConnectionHandler instances:
import { ConnectionHandler } from 'esix';
// Create handlers for different databases
const primaryHandler = new ConnectionHandler();
const analyticsHandler = new ConnectionHandler();
// Set up environment for each
process.env.DB_DATABASE = 'primary';
const primaryDb = await primaryHandler.getConnection();
process.env.DB_DATABASE = 'analytics';
const analyticsDb = await analyticsHandler.getConnection();
// Close both when done
await primaryHandler.closeConnections();
await analyticsHandler.closeConnections();
Note: For most applications, a single database connection is sufficient.
Error Handling
Invalid Adapter
If an invalid adapter name is provided, ConnectionHandler throws a descriptive error:
// This will throw an error
process.env.DB_ADAPTER = 'invalid';
await connectionHandler.getConnection();
// Error: invalid is not a valid adapter name. Must be one of 'default', 'mock'.
Connection Failures
Handle connection failures gracefully:
import { connectionHandler } from 'esix';
try {
const db = await connectionHandler.getConnection();
// Use the database connection
} catch (error) {
console.error('Failed to connect to database:', error);
process.exit(1);
}
Best Practices
- Use environment variables: Never hardcode connection strings
- Implement graceful shutdown: Always close connections on application shutdown
- Use the singleton: Import and use the exported
connectionHandler instance
- Configure pool size: Adjust
DB_MAX_POOL_SIZE based on your application’s concurrency needs
- Use mock adapter for tests: Configure
DB_ADAPTER=mock in test environments
- Handle connection errors: Wrap connection calls in try-catch blocks for production
// Good: Use singleton and environment variables
import { connectionHandler } from 'esix';
const db = await connectionHandler.getConnection();
// Avoid: Creating new instances unnecessarily
const handler = new ConnectionHandler(); // Usually not needed
Type Definitions
class ConnectionHandler {
private client?: MongoClient;
async getConnection(): Promise<Db>;
async closeConnections(): Promise<void>;
private async createClient(): Promise<MongoClient>;
private async getDatabase(): Promise<Db>;
}
const connectionHandler: ConnectionHandler;
export { ConnectionHandler, connectionHandler };
- BaseModel: Uses ConnectionHandler internally for all database operations
- QueryBuilder: Accesses database connections through ConnectionHandler
- MongoDB Driver: ConnectionHandler wraps the official MongoDB Node.js driver