Thin MongoDB ODM built for Standard Schema
mongodb zod deno
1import { type Db, type MongoClientOptions, MongoClient } from "mongodb"; 2import { ConnectionError } from "./errors.ts"; 3 4interface Connection { 5 client: MongoClient; 6 db: Db; 7} 8 9let connection: Connection | null = null; 10 11export interface ConnectOptions extends MongoClientOptions {}; 12 13/** 14 * Health check details of the MongoDB connection 15 * 16 * @property healthy - Overall health status of the connection 17 * @property connected - Whether a connection is established 18 * @property responseTimeMs - Response time in milliseconds (if connection is healthy) 19 * @property error - Error message if health check failed 20 * @property timestamp - Timestamp when health check was performed 21 */ 22export interface HealthCheckResult { 23 healthy: boolean; 24 connected: boolean; 25 responseTimeMs?: number; 26 error?: string; 27 timestamp: Date; 28} 29 30/** 31 * Connect to MongoDB with connection pooling, retry logic, and resilience options 32 * 33 * The MongoDB driver handles connection pooling and automatic retries. 34 * Retry logic is enabled by default for both reads and writes in MongoDB 4.2+. 35 * 36 * @param uri - MongoDB connection string 37 * @param dbName - Name of the database to connect to 38 * @param options - Connection options (pooling, retries, timeouts, etc.) 39 * 40 * @example 41 * Basic connection with pooling: 42 * ```ts 43 * await connect("mongodb://localhost:27017", "mydb", { 44 * maxPoolSize: 10, 45 * minPoolSize: 2, 46 * maxIdleTimeMS: 30000, 47 * connectTimeoutMS: 10000, 48 * socketTimeoutMS: 45000, 49 * }); 50 * ``` 51 * 52 * @example 53 * Production-ready connection with retry logic and resilience: 54 * ```ts 55 * await connect("mongodb://localhost:27017", "mydb", { 56 * // Connection pooling 57 * maxPoolSize: 10, 58 * minPoolSize: 2, 59 * 60 * // Automatic retry logic (enabled by default) 61 * retryReads: true, // Retry failed read operations 62 * retryWrites: true, // Retry failed write operations 63 * 64 * // Timeouts 65 * connectTimeoutMS: 10000, // Initial connection timeout 66 * socketTimeoutMS: 45000, // Socket operation timeout 67 * serverSelectionTimeoutMS: 10000, // Server selection timeout 68 * 69 * // Connection resilience 70 * maxIdleTimeMS: 30000, // Close idle connections 71 * heartbeatFrequencyMS: 10000, // Server health check interval 72 * 73 * // Optional: Compression for reduced bandwidth 74 * compressors: ['snappy', 'zlib'], 75 * }); 76 * ``` 77 */ 78export async function connect( 79 uri: string, 80 dbName: string, 81 options?: ConnectOptions, 82): Promise<Connection> { 83 if (connection) { 84 return connection; 85 } 86 87 try { 88 const client = new MongoClient(uri, options); 89 await client.connect(); 90 const db = client.db(dbName); 91 92 connection = { client, db }; 93 return connection; 94 } catch (error) { 95 throw new ConnectionError( 96 `Failed to connect to MongoDB: ${error instanceof Error ? error.message : String(error)}`, 97 uri 98 ); 99 } 100} 101 102export async function disconnect(): Promise<void> { 103 if (connection) { 104 await connection.client.close(); 105 connection = null; 106 } 107} 108 109export function getDb(): Db { 110 if (!connection) { 111 throw new ConnectionError("MongoDB not connected. Call connect() first."); 112 } 113 return connection.db; 114} 115 116/** 117 * Check the health of the MongoDB connection 118 * 119 * Performs a ping operation to verify the database is responsive 120 * and returns detailed health information including response time. 121 * 122 * @example 123 * ```ts 124 * const health = await healthCheck(); 125 * if (health.healthy) { 126 * console.log(`Database healthy (${health.responseTimeMs}ms)`); 127 * } else { 128 * console.error(`Database unhealthy: ${health.error}`); 129 * } 130 * ``` 131 */ 132export async function healthCheck(): Promise<HealthCheckResult> { 133 const timestamp = new Date(); 134 135 // Check if connection exists 136 if (!connection) { 137 return { 138 healthy: false, 139 connected: false, 140 error: "No active connection. Call connect() first.", 141 timestamp, 142 }; 143 } 144 145 try { 146 // Measure ping response time 147 const startTime = performance.now(); 148 await connection.db.admin().ping(); 149 const endTime = performance.now(); 150 const responseTimeMs = Math.round(endTime - startTime); 151 152 return { 153 healthy: true, 154 connected: true, 155 responseTimeMs, 156 timestamp, 157 }; 158 } catch (error) { 159 return { 160 healthy: false, 161 connected: true, 162 error: error instanceof Error ? error.message : String(error), 163 timestamp, 164 }; 165 } 166}