MongoBolt
I am Aryan Mankame, SDE-2 at Deutsche Bank CSE 2024 Batch passout at Maulana Azad National Institute of Technology, Bhopal. With a strong passion for technology and a focus on full stack development, I possess a diverse skill set that includes: Frontend: React.js: I am proficient in building captivating and interactive user interfaces using React.js, ensuring a seamless user experience. Redux.js: I am adept at utilizing Redux.js for efficient state management, enabling smooth data flow within applications. HTML5 & CSS3: I have a keen eye for crafting visually appealing and responsive web pages using the latest HTML5 and CSS3 techniques. Backend: Node.js & Express.js: With expertise in Node.js and Express.js, I develop scalable and robust server-side applications. RESTful APIs: I am skilled in designing and implementing RESTful APIs that facilitate seamless communication between frontend and backend systems. Database: MongoDB, Firebase, PostgreSQL, MySQL: I have hands-on experience working with both SQL and NoSQL databases, ensuring efficient data storage and retrieval. My commitment to innovation and problem-solving has been demonstrated through my active participation in various hackathons. Notable achievements include securing the 2nd Runners up position in Wittyhacks 3.0, attaining the 7th position in the Ecell NITB Hackathon, and ranking among the top 15 participants in BitsHackathon. Driven by a desire to make a meaningful impact in the field of technology, my goal is to leverage my skills and enthusiasm to contribute to cutting-edge projects and drive the advancement of software development. Overall, I am a talented and dedicated full stack developer with a proven track record in both frontend and backend technologies. My strong technical foundation, coupled with my passion for innovation, positions me as an invaluable asset to any software development team.
MongoDB Wrapper
A lightweight, modern MongoDB wrapper built on top of Mongoose, designed to simplify database operations with a clean and intuitive API. This package provides a robust interface for connecting to MongoDB, managing collections, performing CRUD operations, handling transactions, building aggregation pipelines, and managing indexes, making it ideal for Node.js applications.
Table of Contents
Installation
Install the package via npm:
npm install mongodb-wrapper
Ensure Mongoose is installed, as it is a peer dependency:
npm install mongoose
Getting Started
To use the MongoDB wrapper, import the MongoDB class, configure it with your MongoDB connection details, and establish a connection.
import MongoDB from 'mongodb-wrapper';
const config = {
uri: 'mongodb://localhost:27017/mydb',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
},
};
const db = new MongoDB(config);
async function init() {
try {
await db.connect();
console.log('Connected to MongoDB');
} catch (error) {
console.error('Connection failed:', error);
}
}
init();
Configuration
The MongoDB constructor accepts a config object with the following properties:
| Property | Type | Description | Required |
uri | string | MongoDB connection URI (e.g., mongodb://localhost:27017/mydb) | Yes |
options | object | Mongoose connection options (e.g., { useNewUrlParser: true }) | No |
Example:
const config = {
uri: 'mongodb://localhost:27017/mydb',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
serverSelectionTimeoutMS: 5000,
},
};
Methods
connect
Establishes a connection to the MongoDB database using the provided configuration.
Signature:
async connect(): Promise<void>
Returns:
Promise<void>: Resolves when the connection is successful, or rejects with an error if the connection fails.
Example:
const db = new MongoDB(config);
await db.connect();
console.log('Database connected');
createStore
Creates a new collection with a specified schema.
Signature:
async createStore<T>(
collectionName: string,
schema: SchemaDefinition<SchemaDefinitionType<T>>
): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
collectionName | string | Name of the collection to create. | Yes |
schema | SchemaDefinition<SchemaDefinitionType<T>> | Mongoose schema definition for the collection. | Yes |
Returns:
Promise<void>: Resolves when the collection is created, or rejects with an error.
Example:
(import { Schema } from 'mongoose';
const userSchema = {
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
};
const db = new MongoDB(config);
await db.connect();
await db.createStore('users', userSchema);
console.log('Users collection created');
insertItem
Inserts a single document into a collection.
Signature:
async insertItem<T>(collectionName: string, documentData: T): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
collectionName | string | Name of the collection to insert into. | Yes |
documentData | T | The document data to insert. | Yes |
Returns:
Promise<void>: Resolves when the document is inserted, or rejects with an error.
Example:
const user = {
name: 'John Doe',
email: 'john@example.com',
};
await db.insertItem('users', user);
console.log('User inserted');
insertItems
Inserts multiple documents into a collection.
Signature:
async insertItems<T>(collectionName: string, documentsData: T[]): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
collectionName | string | Name of the collection to insert into. | Yes |
documentsData | T[] | Array of document data to insert. | Yes |
Returns:
Promise<void>: Resolves when all documents are inserted, or rejects with an error.
Example:
const users = [
{ name: 'Jane Doe', email: 'jane@example.com' },
{ name: 'Bob Smith', email: 'bob@example.com' },
];
await db.insertItems('users', users);
console.log('Users inserted');
findItem
Retrieves documents from a collection based on a query.
Signature:
async findItem<T>(
collectionName: string,
query: Record<string, any> = {}
): Promise<T[]>
Parameters:
| Parameter | Type | Description | Required |
collectionName | string | Name of the collection to query. | Yes |
query | Record<string, any> | Query object to filter documents (default: {}). | No |
Returns:
Promise<T[]>: An array of documents matching the query.
Example:
const users = await db.findItem('users', { name: 'John Doe' });
console.log(users); // [{ name: 'John Doe', email: 'john@example.com', ... }]
updateItems
Updates multiple documents in a collection based on a query.
Signature:
async updateItems(
collectionName: string,
query: Record<string, any>,
update: Record<string, any>
): Promise<number>
Parameters:
| Parameter | Type | Description | Required |
collectionName | string | Name of the collection to update. | Yes |
query | Record<string, any> | Query to select documents to update. | Yes |
update | Record<string, any> | Update operations to apply (e.g., { $set: { ... } }). | Yes |
Returns:
Promise<number>: The number of documents updated.
Example:
const updatedCount = await db.updateItems(
'users',
{ name: 'John Doe' },
{ $set: { email: 'john.doe@example.com' } }
);
console.log(`${updatedCount} users updated`);
deleteStore
Deletes a collection from the database.
Signature:
async deleteStore(collectionName: string): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
collectionName | string | Name of the collection to delete. | Yes |
Returns:
Promise<void>: Resolves when the collection is deleted, or rejects with an error.
Example:
await db.deleteStore('users');
console.log('Users collection deleted');
dropItem
Deletes a single document from a collection based on a query.
Signature:
async dropItem(
storeName: string,
itemInfo: Record<string, any>
): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to delete from. | Yes |
itemInfo | Record<string, any> | Query to identify the document to delete. | Yes |
Returns:
Promise<void>: Resolves when the document is deleted, or rejects with an error.
Example:
await db.dropItem('users', { email: 'john@example.com' });
console.log('User dropped');
dropItems
Deletes multiple documents from a collection based on a query.
Signature:
async dropItems(
storeName: string,
itemInfo: Record<string, any>
): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to delete from. | Yes |
itemInfo | Record<string, any> | Query to identify documents to delete. | Yes |
Returns:
Promise<void>: Resolves when the documents are deleted, or rejects with an error.
Example:
await db.dropItems('users', { name: 'John Doe' });
console.log('Users dropped');
truncateStore
Removes all documents from a collection without deleting the collection itself.
Signature:
async truncateStore(storeName: string): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to truncate. | Yes |
Returns:
Promise<void>: Resolves when the collection is truncated, or rejects with an error.
Example:
await db.truncateStore('users');
console.log('Users collection truncated');
alterStore
Modifies the schema of an existing collection and updates documents as needed.
Signature:
async alterStore<T, U>(
storeName: string,
newSchema: SchemaDefinition<SchemaDefinitionType<U>>,
changeValues: Record<string, any>
): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to modify. | Yes |
newSchema | SchemaDefinition<SchemaDefinitionType<U>> | New schema definition for the collection. | Yes |
changeValues | Record<string, any> | Update operations to apply to existing documents. | Yes |
Returns:
Promise<void>: Resolves when the schema is updated, or rejects with an error.
Example:
const newSchema = {
name: { type: String, required: true },
email: { type: String, required: true },
age: { type: Number, default: 0 },
};
await db.alterStore('users', newSchema, { $set: { age: 18 } });
console.log('Users schema updated');
findAndPopulateById
Retrieves documents by ID and populates specified fields.
Signature:
async findAndPopulateById<T>(
storeName: string,
thingsToBePopulated: string | string[],
query: Record<string, any>
): Promise<T[]>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to query. | Yes |
thingsToBePopulated | `string | string[]` | Field(s) to populate (e.g., 'posts' or ['posts', 'comments']). |
query | Record<string, any> | Query to filter documents (e.g., { _id: '123' }). | Yes |
Returns:
Promise<T[]>: An array of documents with populated fields.
Example:
const user = await db.findAndPopulateById('users', 'posts', { _id: '123' });
console.log(user); // [{ name: 'John', posts: [{ title: 'Post 1', ... }, ...] }]
aggregate
Executes an aggregation pipeline on a collection.
Signature:
async aggregate<T>(
storeName: string,
pipeline: any
): Promise<any[]>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to aggregate. | Yes |
pipeline | any | Array of aggregation pipeline stages. | Yes |
Returns:
Promise<any[]>: An array of aggregation results.
Example:
const pipeline = [
{ $match: { name: 'John Doe' } },
{ $group: { _id: '$email', count: { $sum: 1 } } },
];
const results = await db.aggregate('users', pipeline);
console.log(results); // [{ _id: 'john@example.com', count: 1 }, ...]
pipelineBuilder
Creates an instance of AggregationBuilder for constructing aggregation pipelines.
Signature:
pipelineBuilder(): AggregationBuilder
Returns:
AggregationBuilder: An instance of the aggregation builder.
Example:
const builder = db.pipelineBuilder();
const pipeline = builder
.match({ name: 'John Doe' })
.group({ _id: '$email', count: { $sum: 1 } })
.build();
const results = await db.aggregate('users', pipeline);
console.log(results);
createIndex
Creates an index on a specified column in a collection.
Signature:
async createIndex(
storeName: string,
column: string,
order: string,
options: any
): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to create the index on. | Yes |
column | string | Field to index (e.g., 'email'). | Yes |
order | string | Index order (e.g., '1' for ascending, '-1' for descending). | Yes |
options | any | Additional index options (e.g., { unique: true }). | Yes |
Returns:
Promise<void>: Resolves when the index is created, or rejects with an error.
Example:
await db.createIndex('users', 'email', '1', { unique: true });
console.log('Index created on email');
viewIndexes
Retrieves all indexes for a collection.
Signature:
async viewIndexes(storeName: string): Promise<IndexDefinition[]>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to view indexes for. | Yes |
Returns:
Promise<IndexDefinition[]>: An array of index definitions.
Example:
const indexes = await db.viewIndexes('users');
console.log(indexes); // [{ key: { email: 1 }, name: 'email_1', ... }, ...]
dropIndex
Drops a specified index from a collection.
Signature:
async dropIndex(storeName: string, indexName: string): Promise<void>
Parameters:
| Parameter | Type | Description | Required |
storeName | string | Name of the collection to drop the index from. | Yes |
indexName | string | Name of the index to drop (e.g., 'email_1'). | Yes |
Returns:
Promise<void>: Resolves when the index is dropped, or rejects with an error.
Example:
await db.dropIndex('users', 'email_1');
console.log('Index dropped');
findAndPopulateByKey
Retrieves documents from one collection and populates fields from another collection based on a key relationship.
Signature:
async findAndPopulateByKey(
fromStoreName: string,
query: Record<string, any>,
fromKey: string,
findInStoreName: string,
inKey: string
): Promise<any[]>
Parameters:
| Parameter | Type | Description | Required |
fromStoreName | string | Name of the source collection. | Yes |
query | Record<string, any> | Query to filter source documents. | Yes |
fromKey | string | Field in the source collection to match. | Yes |
findInStoreName | string | Name of the collection to populate from. | Yes |
inKey | string | Field in the target collection to match against fromKey. | Yes |
Returns:
Promise<any[]>: An array of documents with populated fields.
Example:
const users = await db.findAndPopulateByKey(
'users',
{ name: 'John Doe' },
'userId',
'posts',
'authorId'
);
console.log(users); // [{ name: 'John Doe', posts: [{ title: 'Post 1', ... }, ...] }]
startTransaction
Starts a new transaction session.
Signature:
async startTransaction(): Promise<void>
Returns:
Promise<void>: Resolves when the transaction session is started, or rejects with an error.
Example:
await db.startTransaction();
console.log('Transaction started');
commitTransaction
Commits the current transaction.
Signature:
async commitTransaction(): Promise<void>
Returns:
Promise<void>: Resolves when the transaction is committed, or rejects with an error.
Example:
await db.commitTransaction();
console.log('Transaction committed');
abortTransaction
Aborts the current transaction.
Signature:
async abortTransaction(): Promise<void>
Returns:
Promise<void>: Resolves when the transaction is aborted, or rejects with an error.
Example:
await db.abortTransaction();
console.log('Transaction aborted');
getSession
Retrieves the current Mongoose session, if one exists.
Signature:
async getSession(): Promise<mongoose.ClientSession | null>
Returns:
Promise<mongoose.ClientSession | null>: The current session ornullif no session exists.
Example:
const session = await db.getSession();
if (session) {
console.log('Active session retrieved');
} else {
console.log('No active session');
}
getMongoose
Retrieves the Mongoose instance used by the wrapper.
Signature:
async getMongoose(): Promise<typeof mongoose>
Returns:
Promise<typeof mongoose>: The Mongoose instance.
Example:
const mongoose = await db.getMongoose();
console.log('Mongoose instance retrieved:', mongoose.version);
disconnect
Closes the connection to the MongoDB database.
Signature:
async disconnect(): Promise<void>
Returns:
Promise<void>: Resolves when the connection is closed, or rejects with an error.
Example:
await db.disconnect();
console.log('Database disconnected');
Advanced Usage
Transaction Management
The wrapper supports MongoDB transactions for atomic operations across multiple collections. Use startTransaction, commitTransaction, and abortTransaction to manage transactions, and getSession to access the session for custom operations.
Example:
const db = new MongoDB(config);
await db.connect();
try {
await db.startTransaction();
const user = { name: 'Alice', email: 'alice@example.com' };
await db.insertItem('users', user, { session: await db.getSession() });
const post = { title: 'First Post', author: 'Alice' };
await db.insertItem('posts', post, { session: await db.getSession() });
await db.commitTransaction();
console.log('Transaction committed');
} catch (error) {
await db.abortTransaction();
console.error('Transaction aborted:', error);
} finally {
await db.disconnect();
}
Aggregation Pipelines
The aggregate method and pipelineBuilder provide powerful tools for data aggregation. The pipelineBuilder offers a fluent interface for constructing pipelines.
Example:
const builder = db.pipelineBuilder();
const pipeline = builder
.match\(\)match({ status: 'active' })
.group({ _id: '$userId', totalSpent: { $sum: '$amount' } })
.sort({ totalSpent: -1 })
.limit(10)
.build();
const topSpenders = await db.aggregate('orders', pipeline);
console.log(topSpenders);
Index Management
Indexes improve query performance. Use createIndex, viewIndexes, and dropIndex to manage indexes.
Example:
// Create a unique index
await db.createIndex('users', 'email', '1', { unique: true });
// View all indexes
const indexes = await db.viewIndexes('users');
console.log(indexes);
// Drop an index
await db.dropIndex('users', 'email_1');
Examples
Complete CRUD Workflow
import MongoDB from 'mongodb-wrapper';
import { Schema } from 'mongoose';
const config = {
uri: 'mongodb://localhost:27017/mydb',
options: { useNewUrlParser: true, useUnifiedTopology: true },
};
const db = new MongoDB(config);
async function run() {
try {
// Connect to database
await db.connect();
// Create a collection
const userSchema = {
name: { type: String, required: true },
email: { type: String, required: true },
};
await db.createStore('users', userSchema);
// Insert a document
await db.insertItem('users', { name: 'Alice', email: 'alice@example.com' });
// Find documents
const users = await db.findItem('users', { name: 'Alice' });
console.log(users);
// Update documents
await db.updateItems('users', { name: 'Alice' }, { $set: { email: 'alice.new@example.com' } });
// Delete a document
await db.dropItem('users', { email: 'alice.new@example.com' });
// Disconnect
await db.disconnect();
} catch (error) {
console.error('Error:', error);
}
}
run();
Populating Related Data
const userSchema = {
name: { type: String, required: true },
postIds: [{ type: Schema.Types.ObjectId, ref: 'posts' }],
};
const postSchema = {
title: { type: String, required: true },
authorId: { type: Schema.Types.ObjectId, ref: 'users' },
};
await db.createStore('users', userSchema);
await db.createStore('posts', postSchema);
const users = await db.findAndPopulateById('users', 'postIds', { name: 'John' });
console.log(users); // [{ name: 'John', postIds: [{ title: 'Post 1', ... }, ...] }]
Error Handling
All methods throw errors if the database connection is not established or if invalid parameters are provided. Use try-catch blocks to handle errors gracefully:
try {
await db.insertItem('users', { name: 'Invalid User' });
} catch (error) {
console.error('Failed to insert user:', error.message);
}
License
MIT License. See LICENSE for details.