Concurrent connect callers share startup and failure cleanup. close invalidates that startup generation, waits for it to settle and releases late-created resources; connect waits for an ongoing close. Failed resource cleanup remains observable and retryable on a later close. Library-managed memory MongoDB instances follow the same ownership release chain.
monSQLize provides connection management for MongoDB applications, including safe connection reuse, parameter validation, resource cleanup, and cross-database access. This document explains the connection API and the related configuration surface.
- Safe connection reuse: concurrent
connect()calls wait for the same connection attempt. - Parameter validation: collection and database names are validated before use.
- Resource cleanup: client resources and runtime caches are released by
close(). - Error handling: failed connection attempts can be retried safely.
- Cross-database access: a single instance can access collections in other databases.
Establishes a database connection. The method is safe to call concurrently and reuses the same in-flight connection promise.
async connect(){
collection: Function, // Collection accessor for the default database.
db: Function, // Database-level accessor; db(name?) returns a DbAccessor.
use: Function, // Scoped collection/model access for another database.
instance: MonSQLize // Current MonSQLize instance.
}import MonSQLize from 'monsqlize';
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop',
config: { uri: 'mongodb://localhost:27017' }
});
const { collection, use } = await msq.connect();
// Connect to the database.
const users = collection('users');
// Use collection accessors.
const products = collection('products');
// Cross-database access.
const analyticsEvents = use('analytics').collection('events');connect() is safe to call from concurrent startup tasks or request handlers. Concurrent callers wait for the same connection attempt instead of opening duplicate MongoDB clients.
- The first caller starts the connection attempt.
- Other callers wait for that attempt to finish.
- Successful callers receive the connected runtime accessors.
- If the attempt fails, all waiting callers receive the same error and the next
connect()call can retry.
import MonSQLize from 'monsqlize';
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'example',
config: { uri: 'mongodb://localhost:27017' }
});
// High-concurrency scenario: 10 concurrent requests.
const promises = Array(10).fill(null).map(() => msq.connect());
const results = await Promise.all(promises);
// All requests return the same connection object.
console.log(results[0] === results[1]); // true
console.log(results[0] === results[9]); // true
console.log('Only one connection was established.');- Prevents connection-pool exhaustion.
- Reduces connection overhead.
- Avoids duplicate resource allocation.
- Improves stability under startup bursts.
collection() and db() fail early when a collection or database name is missing or invalid.
Rules:
- The collection name must be a non-empty string.
null,undefined, empty strings, and whitespace-only strings are rejected.- Numbers, objects, and other non-string values are rejected.
const { collection } = await msq.connect();
// Valid usage.
const users = collection('users');
const orders = collection('my-orders');
// Invalid parameters. These throw.
try {
collection('');
collection(' ');
collection(null);
collection(undefined);
collection(123);
collection({ name: 'test' });
} catch (err) {
console.error(err.code, err.message);
// INVALID_COLLECTION_NAME Collection name must be a non-empty string.
}Important: db(name) validates the database name immediately when name is provided. Omit the argument, or pass undefined, to use the default database. Pass null only if you intentionally want an INVALID_DATABASE_NAME error in JavaScript.
Rules:
- If
databaseNameis provided, it must be a non-empty string. undefinedand an omitted argument use the default database.null, empty strings, and whitespace-only strings are rejected.
const { db } = await msq.connect();
// Valid usage.
const shopDb = db('shop');
const analyticsDb = db('analytics');
// Use the default database. This is valid.
const defaultDb1 = db();
const defaultDb2 = db(undefined);
// Verify that collections can be obtained normally.
const shopOrders = shopDb.collection('orders');
const analyticsEvents = analyticsDb.collection('events');
// Invalid parameters. These throw immediately.
try {
db(null);
db('');
db(' ');
} catch (err) {
console.error(err.code, err.message);
// INVALID_DATABASE_NAME Database name must be a non-empty string.
}
// Omitted and undefined use the default database.
const users1 = db().collection('users');
const users2 = db(undefined).collection('users');
// No error is thrown.
console.log('Default database access succeeded.');| Error code | Description | Example |
|---|---|---|
INVALID_COLLECTION_NAME |
Invalid collection name. | collection('') |
INVALID_DATABASE_NAME |
Invalid database name, such as null, an empty string, or a whitespace-only string. |
db('') |
Notes:
db()anddb(undefined)use the default database configured on the MonSQLize instance.db(null)throwsINVALID_DATABASE_NAMEin JavaScript; TypeScript callers should omit the argument instead.''and strings such as' 'also trigger database-name validation errors.
Closes the database connection and releases internal resources.
async close()- Closes the MongoDB client connection.
- Releases the current connection attempt state.
- Clears cached runtime Model instances for this MonSQLize instance.
- Releases runtime-owned references that should not survive after shutdown.
import MonSQLize from 'monsqlize';
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'example',
config: { uri: 'mongodb://localhost:27017' }
});
// Connect.
const { collection } = await msq.connect();
// Use the connection.
await collection('test').find({});
// Close the connection.
await msq.close();
console.log('Connection closed and resources cleaned.');for (let i = 0; i < 5; i++) {
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'example',
config: { uri: 'mongodb://localhost:27017' }
});
await msq.connect();
const { collection } = await msq.connect();
// Use the connection.
await collection('test').find({});
// Close the connection.
await msq.close();
console.log(`Cycle ${i + 1} completed`);
}
// Repeated connect-close cycles are safe.
console.log('All cycles completed and memory was cleaned correctly.');- Calling
close()more than once is safe. - Calling
connect()afterclose()creates a new connection. - Call
close()when the application shuts down. - In tests, close the instance in
afterEachorafterhooks.
monSQLize can access collections in different databases through one instance.
import MonSQLize from 'monsqlize';
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop',
config: { uri: 'mongodb://localhost:27017' }
});
const { collection, use } = await msq.connect();
// 1. Access a collection in the default database.
const products = await collection('products').find({});
console.log('shop.products ->', products);
// 2. Access a collection in another database.
const analyticsEvents = await use('analytics').collection('events').findOne(
{ type: 'click' },
{
cache: 3000,
maxTimeMS: 1500
}
);
console.log('analytics.events ->', analyticsEvents);
// 3. Query another database multiple times.
const [user1, user2] = await Promise.all([
db('users_db').collection('users').findOne({ name: 'Alice' }, { cache: 2000 }),
db('users_db').collection('users').findOne({ name: 'Bob' })
]);
console.log(user1, user2);- All cross-database access shares the same MongoDB client connection.
- Cache keys include the database name, so identical collection names in different databases have isolated cache entries.
- Per-query options such as
maxTimeMSandcachework the same way for cross-database collections. - Cross-database collections support monSQLize features such as caching and slow-query logging.
import MonSQLize from 'monsqlize';
try {
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'example',
config: { uri: 'mongodb://invalid-host:27017' }
});
await msq.connect();
} catch (err) {
// Connection failure.
console.error('Connection failed:', err.message);
// A failed attempt does not leave the instance stuck; retrying is safe.
console.log('The connection can be retried.');
}const { collection, db } = await msq.connect();
try {
// Invalid collection name.
collection('');
} catch (err) {
if (err.code === 'INVALID_COLLECTION_NAME') {
console.error('Invalid collection name:', err.message);
console.log('Provide a valid collection name.');
}
}
try {
// Invalid database name: empty string.
// db() validates database names immediately.
db('');
} catch (err) {
if (err.code === 'INVALID_DATABASE_NAME') {
console.error('Invalid database name:', err.message);
console.log('Provide a valid database name, or omit it for the default database.');
}
}
// Correct usage: omit the argument or use undefined for the default database.
const defaultDb = db().collection('users');
const defaultDb2 = db(undefined).collection('users');// db-connection.js
import MonSQLize from 'monsqlize';
let msqInstance = null;
export async function getConnection() {
if (!msqInstance) {
msqInstance = new MonSQLize({
type: 'mongodb',
databaseName: process.env.DB_NAME || 'shop',
config: { uri: process.env.MONGODB_URI }
});
}
return await msqInstance.connect();
}
export async function closeConnection() {
if (msqInstance) {
await msqInstance.close();
msqInstance = null;
}
}import { getConnection } from './db-connection.js';
// Use the singleton connection.
async function queryUsers() {
const { collection } = await getConnection();
return await collection('users').find({});
}import MonSQLize from 'monsqlize';
class Application {
constructor() {
this.msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop',
config: { uri: process.env.MONGODB_URI }
});
}
async start() {
console.log('Starting application...');
// Establish the connection.
const { collection } = await this.msq.connect();
this.collection = collection;
console.log('Database connection established.');
}
async stop() {
console.log('Stopping application...');
// Close the connection.
await this.msq.close();
console.log('Database connection closed.');
}
}
const app = new Application();
// Use the application lifecycle.
async function main() {
await app.start();
// Application is running...
process.on('SIGINT', async () => {
// Graceful shutdown.
await app.stop();
process.exit(0);
});
}
main();async function connectWithRetry(maxRetries = 3, delay = 1000) {
const msq = new MonSQLize({
type: 'mongodb',
databaseName: 'shop',
config: { uri: process.env.MONGODB_URI }
});
for (let i = 0; i < maxRetries; i++) {
try {
await msq.connect();
console.log('Connection established.');
return msq;
} catch (err) {
console.error(`Connection failed (${i + 1}/${maxRetries}):`, err.message);
if (i < maxRetries - 1) {
console.log(`Retrying after ${delay} ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw new Error('Connection failed after the maximum retry count.');
}
}
}
}const { describe, it, before, after } = require('mocha');
import MonSQLize from 'monsqlize';
describe('user service', () => {
let msq;
let collection;
before(async () => {
// Establish the connection before tests.
msq = new MonSQLize({
type: 'mongodb',
databaseName: 'test',
config: { useMemoryServer: true }
});
const conn = await msq.connect();
collection = conn.collection;
});
after(async () => {
// Close the connection after tests.
await msq.close();
});
it('queries users', async () => {
const users = await collection('users').find({});
console.log('Users found:', users.length);
});
});useMemoryServer reuses the project-local .cache/mongodb-memory-server/binaries binary cache by default. Automatically created temporary data directories are placed under .cache/mongodb-memory-server/db and are cleaned when the instance closes. To pin a directory, pass memoryServerOptions.instance.dbPath or set MONSQLIZE_MEMORY_SERVER_DB_DIR.
Connection lifecycle and constructor options are intentionally documented separately. Use this page for connect(), collection(), db(), use(), and close(). Use Configuration Reference for the full new MonSQLize(options) surface, including MongoDB connection options, cache, Redis, distributed invalidation, Model, schema-dsl, pools, sync, slow-query logging, ObjectId conversion, and write path policy.
connect() has a built-in concurrent lock, so repeated or concurrent calls reuse the same connection.
const msq = new MonSQLize({ /* ... */ });
// Concurrent calls establish only one connection.
const [conn1, conn2, conn3] = await Promise.all([
msq.connect(),
msq.connect(),
msq.connect()
]);
console.log(conn1 === conn2); // trueCall close() when:
- The application is shutting down.
- A test finishes and should release its database resources.
- A connection will not be used for a long time.
- A connect-close lifecycle test needs deterministic cleanup.
No. Cross-database access shares the same MongoDB client connection and only targets different databases.
const { use } = await msq.connect();
// These three operations share the same connection.
await use('shop').collection('products').find({});
await use('analytics').collection('events').find({});
await use('logs').collection('errors').find({});After a connection failure, the instance can retry on the next connect() call.
async function connectWithRetry() {
const msq = new MonSQLize({ /* ... */ });
while (true) {
try {
await msq.connect();
return msq;
} catch (err) {
console.error('Connection failed; retrying in 3 seconds...');
await new Promise(r => setTimeout(r, 3000));
}
}
}