Thin MongoDB ODM built for Standard Schema
mongodb zod deno

add connection pooling and management

knotbin.com 9add0dd1 3888b2b5

verified
Changed files
+189 -4
tests
+13 -1
README.md
···
type UserInsert = Input<typeof userSchema>;
async function main() {
-
// Use the latest connection string format and options
+
// Basic connection
await connect("mongodb://localhost:27017", "your_database_name");
+
+
// Or with connection pooling options
+
await connect("mongodb://localhost:27017", "your_database_name", {
+
clientOptions: {
+
maxPoolSize: 10, // Maximum connections in pool
+
minPoolSize: 2, // Minimum connections in pool
+
maxIdleTimeMS: 30000, // Close idle connections after 30s
+
connectTimeoutMS: 10000, // Connection timeout
+
socketTimeoutMS: 45000, // Socket timeout
+
}
+
});
+
const UserModel = new Model("users", userSchema);
// Your operations go here
+30 -2
client.ts
···
-
import { type Db, MongoClient } from "mongodb";
+
import { type Db, type MongoClientOptions, MongoClient } from "mongodb";
interface Connection {
client: MongoClient;
···
let connection: Connection | null = null;
+
export interface ConnectOptions {
+
/**
+
* MongoDB connection options (pooling, timeouts, etc.)
+
* See: https://mongodb.github.io/node-mongodb-native/6.18/interfaces/MongoClientOptions.html
+
*/
+
clientOptions?: MongoClientOptions;
+
}
+
+
/**
+
* Connect to MongoDB with connection pooling and other options
+
*
+
* The MongoDB driver handles connection pooling automatically.
+
* Configure pooling via `clientOptions`:
+
*
+
* @example
+
* ```ts
+
* await connect("mongodb://localhost:27017", "mydb", {
+
* clientOptions: {
+
* maxPoolSize: 10, // Maximum connections in pool
+
* minPoolSize: 2, // Minimum connections in pool
+
* maxIdleTimeMS: 30000, // Close idle connections after 30s
+
* connectTimeoutMS: 10000, // Connection timeout
+
* socketTimeoutMS: 45000, // Socket timeout
+
* }
+
* });
+
* ```
+
*/
export async function connect(
uri: string,
dbName: string,
+
options?: ConnectOptions,
): Promise<Connection> {
if (connection) {
return connection;
}
-
const client = new MongoClient(uri);
+
const client = new MongoClient(uri, options?.clientOptions);
await client.connect();
const db = client.db(dbName);
+1 -1
mod.ts
···
export { type InferModel, type Input } from "./schema.ts";
-
export { connect, disconnect } from "./client.ts";
+
export { connect, disconnect, type ConnectOptions } from "./client.ts";
export { Model } from "./model.ts";
+145
tests/connection_test.ts
···
+
import { assert, assertEquals } from "@std/assert";
+
import { connect, disconnect, type ConnectOptions } from "../mod.ts";
+
import { MongoMemoryServer } from "mongodb-memory-server-core";
+
+
let mongoServer: MongoMemoryServer | null = null;
+
+
async function setupTestServer() {
+
if (!mongoServer) {
+
mongoServer = await MongoMemoryServer.create();
+
}
+
return mongoServer.getUri();
+
}
+
+
Deno.test.afterEach(async () => {
+
await disconnect();
+
});
+
+
Deno.test.afterAll(async () => {
+
if (mongoServer) {
+
await mongoServer.stop();
+
mongoServer = null;
+
}
+
});
+
+
Deno.test({
+
name: "Connection: Basic - should connect without options",
+
async fn() {
+
const uri = await setupTestServer();
+
const connection = await connect(uri, "test_db");
+
+
assert(connection);
+
assert(connection.client);
+
assert(connection.db);
+
assertEquals(connection.db.databaseName, "test_db");
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Connection: Options - should connect with pooling options",
+
async fn() {
+
const uri = await setupTestServer();
+
const options: ConnectOptions = {
+
clientOptions: {
+
maxPoolSize: 10,
+
minPoolSize: 2,
+
maxIdleTimeMS: 30000,
+
connectTimeoutMS: 5000,
+
},
+
};
+
+
const connection = await connect(uri, "test_db", options);
+
+
assert(connection);
+
assert(connection.client);
+
assert(connection.db);
+
+
// Verify connection is working
+
const adminDb = connection.db.admin();
+
const serverStatus = await adminDb.serverStatus();
+
assert(serverStatus);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Connection: Singleton - should reuse existing connection",
+
async fn() {
+
const uri = await setupTestServer();
+
+
const connection1 = await connect(uri, "test_db");
+
const connection2 = await connect(uri, "test_db");
+
+
// Should return the same connection instance
+
assertEquals(connection1, connection2);
+
assertEquals(connection1.client, connection2.client);
+
assertEquals(connection1.db, connection2.db);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Connection: Disconnect - should disconnect and allow reconnection",
+
async fn() {
+
const uri = await setupTestServer();
+
+
const connection1 = await connect(uri, "test_db");
+
assert(connection1);
+
+
await disconnect();
+
+
// Should be able to reconnect
+
const connection2 = await connect(uri, "test_db");
+
assert(connection2);
+
+
// Should be a new connection instance
+
assert(connection1 !== connection2);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Connection: Options - should apply maxPoolSize option",
+
async fn() {
+
const uri = await setupTestServer();
+
const options: ConnectOptions = {
+
clientOptions: {
+
maxPoolSize: 5,
+
},
+
};
+
+
const connection = await connect(uri, "test_db", options);
+
+
// Verify connection works with custom pool size
+
const collections = await connection.db.listCollections().toArray();
+
assert(Array.isArray(collections));
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Connection: Multiple Databases - should handle different database names",
+
async fn() {
+
const uri = await setupTestServer();
+
+
// Connect to first database
+
const connection1 = await connect(uri, "db1");
+
assertEquals(connection1.db.databaseName, "db1");
+
+
// Disconnect first
+
await disconnect();
+
+
// Connect to second database
+
const connection2 = await connect(uri, "db2");
+
assertEquals(connection2.db.databaseName, "db2");
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+