Thin MongoDB ODM built for Standard Schema
mongodb zod deno

add connection pooling and management

knotbin.com 9add0dd1 3888b2b5

verified
+189 -4
+13 -1
README.md
··· 65 65 type UserInsert = Input<typeof userSchema>; 66 66 67 67 async function main() { 68 - // Use the latest connection string format and options 68 + // Basic connection 69 69 await connect("mongodb://localhost:27017", "your_database_name"); 70 + 71 + // Or with connection pooling options 72 + await connect("mongodb://localhost:27017", "your_database_name", { 73 + clientOptions: { 74 + maxPoolSize: 10, // Maximum connections in pool 75 + minPoolSize: 2, // Minimum connections in pool 76 + maxIdleTimeMS: 30000, // Close idle connections after 30s 77 + connectTimeoutMS: 10000, // Connection timeout 78 + socketTimeoutMS: 45000, // Socket timeout 79 + } 80 + }); 81 + 70 82 const UserModel = new Model("users", userSchema); 71 83 72 84 // Your operations go here
+30 -2
client.ts
··· 1 - import { type Db, MongoClient } from "mongodb"; 1 + import { type Db, type MongoClientOptions, MongoClient } from "mongodb"; 2 2 3 3 interface Connection { 4 4 client: MongoClient; ··· 7 7 8 8 let connection: Connection | null = null; 9 9 10 + export interface ConnectOptions { 11 + /** 12 + * MongoDB connection options (pooling, timeouts, etc.) 13 + * See: https://mongodb.github.io/node-mongodb-native/6.18/interfaces/MongoClientOptions.html 14 + */ 15 + clientOptions?: MongoClientOptions; 16 + } 17 + 18 + /** 19 + * Connect to MongoDB with connection pooling and other options 20 + * 21 + * The MongoDB driver handles connection pooling automatically. 22 + * Configure pooling via `clientOptions`: 23 + * 24 + * @example 25 + * ```ts 26 + * await connect("mongodb://localhost:27017", "mydb", { 27 + * clientOptions: { 28 + * maxPoolSize: 10, // Maximum connections in pool 29 + * minPoolSize: 2, // Minimum connections in pool 30 + * maxIdleTimeMS: 30000, // Close idle connections after 30s 31 + * connectTimeoutMS: 10000, // Connection timeout 32 + * socketTimeoutMS: 45000, // Socket timeout 33 + * } 34 + * }); 35 + * ``` 36 + */ 10 37 export async function connect( 11 38 uri: string, 12 39 dbName: string, 40 + options?: ConnectOptions, 13 41 ): Promise<Connection> { 14 42 if (connection) { 15 43 return connection; 16 44 } 17 45 18 - const client = new MongoClient(uri); 46 + const client = new MongoClient(uri, options?.clientOptions); 19 47 await client.connect(); 20 48 const db = client.db(dbName); 21 49
+1 -1
mod.ts
··· 1 1 export { type InferModel, type Input } from "./schema.ts"; 2 - export { connect, disconnect } from "./client.ts"; 2 + export { connect, disconnect, type ConnectOptions } from "./client.ts"; 3 3 export { Model } from "./model.ts";
+145
tests/connection_test.ts
··· 1 + import { assert, assertEquals } from "@std/assert"; 2 + import { connect, disconnect, type ConnectOptions } from "../mod.ts"; 3 + import { MongoMemoryServer } from "mongodb-memory-server-core"; 4 + 5 + let mongoServer: MongoMemoryServer | null = null; 6 + 7 + async function setupTestServer() { 8 + if (!mongoServer) { 9 + mongoServer = await MongoMemoryServer.create(); 10 + } 11 + return mongoServer.getUri(); 12 + } 13 + 14 + Deno.test.afterEach(async () => { 15 + await disconnect(); 16 + }); 17 + 18 + Deno.test.afterAll(async () => { 19 + if (mongoServer) { 20 + await mongoServer.stop(); 21 + mongoServer = null; 22 + } 23 + }); 24 + 25 + Deno.test({ 26 + name: "Connection: Basic - should connect without options", 27 + async fn() { 28 + const uri = await setupTestServer(); 29 + const connection = await connect(uri, "test_db"); 30 + 31 + assert(connection); 32 + assert(connection.client); 33 + assert(connection.db); 34 + assertEquals(connection.db.databaseName, "test_db"); 35 + }, 36 + sanitizeResources: false, 37 + sanitizeOps: false, 38 + }); 39 + 40 + Deno.test({ 41 + name: "Connection: Options - should connect with pooling options", 42 + async fn() { 43 + const uri = await setupTestServer(); 44 + const options: ConnectOptions = { 45 + clientOptions: { 46 + maxPoolSize: 10, 47 + minPoolSize: 2, 48 + maxIdleTimeMS: 30000, 49 + connectTimeoutMS: 5000, 50 + }, 51 + }; 52 + 53 + const connection = await connect(uri, "test_db", options); 54 + 55 + assert(connection); 56 + assert(connection.client); 57 + assert(connection.db); 58 + 59 + // Verify connection is working 60 + const adminDb = connection.db.admin(); 61 + const serverStatus = await adminDb.serverStatus(); 62 + assert(serverStatus); 63 + }, 64 + sanitizeResources: false, 65 + sanitizeOps: false, 66 + }); 67 + 68 + Deno.test({ 69 + name: "Connection: Singleton - should reuse existing connection", 70 + async fn() { 71 + const uri = await setupTestServer(); 72 + 73 + const connection1 = await connect(uri, "test_db"); 74 + const connection2 = await connect(uri, "test_db"); 75 + 76 + // Should return the same connection instance 77 + assertEquals(connection1, connection2); 78 + assertEquals(connection1.client, connection2.client); 79 + assertEquals(connection1.db, connection2.db); 80 + }, 81 + sanitizeResources: false, 82 + sanitizeOps: false, 83 + }); 84 + 85 + Deno.test({ 86 + name: "Connection: Disconnect - should disconnect and allow reconnection", 87 + async fn() { 88 + const uri = await setupTestServer(); 89 + 90 + const connection1 = await connect(uri, "test_db"); 91 + assert(connection1); 92 + 93 + await disconnect(); 94 + 95 + // Should be able to reconnect 96 + const connection2 = await connect(uri, "test_db"); 97 + assert(connection2); 98 + 99 + // Should be a new connection instance 100 + assert(connection1 !== connection2); 101 + }, 102 + sanitizeResources: false, 103 + sanitizeOps: false, 104 + }); 105 + 106 + Deno.test({ 107 + name: "Connection: Options - should apply maxPoolSize option", 108 + async fn() { 109 + const uri = await setupTestServer(); 110 + const options: ConnectOptions = { 111 + clientOptions: { 112 + maxPoolSize: 5, 113 + }, 114 + }; 115 + 116 + const connection = await connect(uri, "test_db", options); 117 + 118 + // Verify connection works with custom pool size 119 + const collections = await connection.db.listCollections().toArray(); 120 + assert(Array.isArray(collections)); 121 + }, 122 + sanitizeResources: false, 123 + sanitizeOps: false, 124 + }); 125 + 126 + Deno.test({ 127 + name: "Connection: Multiple Databases - should handle different database names", 128 + async fn() { 129 + const uri = await setupTestServer(); 130 + 131 + // Connect to first database 132 + const connection1 = await connect(uri, "db1"); 133 + assertEquals(connection1.db.databaseName, "db1"); 134 + 135 + // Disconnect first 136 + await disconnect(); 137 + 138 + // Connect to second database 139 + const connection2 = await connect(uri, "db2"); 140 + assertEquals(connection2.db.databaseName, "db2"); 141 + }, 142 + sanitizeResources: false, 143 + sanitizeOps: false, 144 + }); 145 +