pnk-node-mongo
v1.2.2
Published
A lightweight Node.js MongoDB utility library providing CRUD operations, pagination, filtering, sorting, and aggregation with Mongoose support.
Maintainers
Readme
pnk-node-mongo
A modern MongoDB helper library built on top of Mongoose with optional Pinaka Response support and 100% backward compatibility.
A lightweight wrapper around Mongoose that reduces boilerplate code while keeping the flexibility of native Mongoose.
Designed for both new projects and legacy Pinaka Framework applications, pnk-node-mongo provides a clean, modern API while maintaining full backward compatibility.
Features
- Modern Promise-based API
- Built on top of Mongoose
- Zero configuration
- Supports MongoDB 6+
- Supports Mongoose 6 / 7 / 8
- Supports Node.js 18+
- CRUD helper methods
- Aggregation helpers
- Populate support
- Pagination support
- Distinct support
- Optional Pinaka Response format
- Legacy API compatibility
- TypeScript definitions included
- No performance overhead
- Easy migration from older projects
Why pnk-node-mongo?
Most MongoDB helper libraries are either:
- Too simple and require writing repetitive Mongoose code.
- Too complex and introduce unnecessary abstractions.
- Not backward compatible with existing codebases.
pnk-node-mongo is designed to provide the best balance between simplicity, flexibility, and productivity.
Working with Mongoose often involves writing repetitive code for:
- Pagination
- Filtering
- Sorting
- Field selection
- Population
- Aggregation
- Response formatting
This library removes that boilerplate while keeping the power of Mongoose.
Instead of writing:
const users = await User.find({
status: true
})
.sort({ created_on: -1 })
.skip(20)
.limit(10)
.select("name email");You can simply write:
const users = await db.find(User, {
filter: {
status: true
},
sort: {
created_on: -1
},
page: 3,
limit: 10,
select: [
"name",
"email"
]
});Less code.
More readable.
Easier to maintain.
Table of Contents
- Installation
- Quick Start
- CRUD Operations
- find()
- findOne()
- create()
- createMany()
- updateOne()
- updateMany()
- deleteOne()
- deleteMany()
- distinct()
- findOneAndUpdate()
- Populate
- Aggregation
- Response Mode
- Legacy Compatibility
- Migration Guide
- TypeScript
- API Reference
- Examples
- FAQ
- Roadmap
- Contributing
- License
- Changelog
Requirements
| Package | Version | |---------|----------| | Node.js | 18+ | | MongoDB | 6+ | | Mongoose | 6.x / 7.x / 8.x |
Installation
npm install pnk-node-mongoor
yarn add pnk-node-mongoQuick Start
Import
const db = require("pnk-node-mongo");Basic Example
const users = await db.find(User, {
filter: {
status: true
}
});
console.log(users);Response Mode Example
const result = await db.find(User, {
response: "users",
filter: {
status: true
}
});Output
{
users: {
metarow: {
totalRecordsInCurrentSet: 5,
totalRecordsInDb: 25,
totalRecordsInSet: 10,
setNo: 1
},
rows: [
...
]
},
PinakaResponse: {
ServerStatus: {
value: "SUCCESS"
}
}
}Project Philosophy
The goal of pnk-node-mongo is simple:
- Keep the simplicity of Mongoose
- Reduce repetitive code
- Provide consistent APIs
- Preserve backward compatibility
- Make migration effortless
- Improve developer productivity
The library is intentionally lightweight and acts as a thin wrapper over Mongoose without introducing unnecessary abstractions or performance overhead.
CRUD Operations
All CRUD methods are Promise-based and internally use Mongoose.
Import the library:
const db = require("pnk-node-mongo");find()
Fetch multiple documents.
Syntax
db.find(Model, options)Parameters
| Option | Type | Description | |---------|------|-------------| | filter | Object | Query filter | | sort | Object | Sorting | | page | Number | Page number (starts from 1) | | limit | Number | Records per page | | select | Array | Fields to include | | exclude | Array | Fields to exclude | | response | String | Optional Pinaka Response key |
Example 1
const users = await db.find(User,{
filter:{
status:true
}
});Example 2 - Pagination
const users = await db.find(User,{
page:2,
limit:20
});Example 3 - Sorting
const users = await db.find(User,{
sort:{
created_on:-1
}
});Example 4 - Filter + Pagination
const users = await db.find(User,{
filter:{
city:"Pune",
status:true
},
page:1,
limit:10
});Example 5 - Select Fields
const users = await db.find(User,{
select:[
"name",
"email",
"mobile"
]
});Example 6 - Exclude Fields
const users = await db.find(User,{
exclude:[
"__v",
"password"
]
});Example 7 - Response Mode
const result = await db.find(User,{
response:"users",
filter:{
status:true
}
});Legacy Equivalent
PnkFetchAll(...)findOne()
Fetch a single document.
Syntax
db.findOne(Model, options)Example 1
const user = await db.findOne(User,{
filter:{
_id:userId
}
});Example 2
const user = await db.findOne(User,{
filter:{
email:"[email protected]"
}
});Example 3
const user = await db.findOne(User,{
filter:{
mobile:"9876543210"
},
select:[
"name",
"email"
]
});Example 4
const result = await db.findOne(User,{
response:"user",
filter:{
email:"[email protected]"
}
});Legacy Equivalent
PnkFetchOne(...)create()
Insert one document.
Syntax
db.create(Model,data,options)Example
await db.create(User,{
name:"John",
email:"[email protected]",
mobile:"9999999999"
});Response Mode
await db.create(
User,
{
name:"John"
},
{
response:"user"
}
);Legacy Equivalent
PnkAddNew(...)createMany()
Insert multiple documents.
Example
await db.createMany(User,[
{
name:"John"
},
{
name:"David"
},
{
name:"Robert"
}
]);updateOne()
Update a single document.
New Style
await db.updateOne(User,{
filter:{
_id:id
},
update:{
name:"Peter"
}
});Old Style
await db.updateOne(
User,
{
_id:id
},
{
$set:{
name:"Peter"
}
}
);Response Mode
await db.updateOne(
User,
{
filter:{
_id:id
},
update:{
status:true
},
response:"user"
}
);Legacy Equivalent
PnkUpdate(...)updateMany()
Update multiple documents.
await db.updateMany(User,{
filter:{
city:"Pune"
},
update:{
verified:true
}
});Old Style
await db.updateMany(
User,
{
city:"Pune"
},
{
$set:{
verified:true
}
}
);deleteOne()
Delete a single document.
await db.deleteOne(User,{
_id:id
});Response Mode
await db.deleteOne(
User,
{
_id:id
},
{
response:"user"
}
);Legacy Equivalent
PnkDelete(...)deleteMany()
Delete multiple documents.
await db.deleteMany(User,{
status:false
});distinct()
Fetch unique values.
Example
const cities = await db.distinct(User,{
distinct_key:"city"
});With Filter
const cities = await db.distinct(User,{
distinct_key:"city",
filter:{
status:true
}
});Legacy Equivalent
PnkFetchDistinct(...)findOneAndUpdate()
Update and return updated document.
Example
const user = await db.findOneAndUpdate(User,{
filter:{
_id:id
},
update:{
status:true
}
});Return Selected Fields
const user = await db.findOneAndUpdate(User,{
filter:{
_id:id
},
update:{
status:true
},
onlyFields:"name,email,status"
});Exclude Fields
const user = await db.findOneAndUpdate(User,{
filter:{
_id:id
},
update:{
status:true
},
exceptFields:"password,__v"
});Legacy Equivalent
PnkFindAndModify(...)Notes
- All methods return Promises.
- All methods support async/await.
- Pagination starts from page 1.
responseis optional.- Without
response, methods return native Mongoose results. - With
response, methods return Pinaka Response format. - All methods work with existing Mongoose models.
Migration Guide
Backward Compatibility
pnk-node-mongo v1.2.x is 100% backward compatible.
You can upgrade without changing any existing code.
All legacy methods continue to work exactly as before.
const db = require("pnk-node-mongo");
const result = await db.PnkFetchAll(
"users",
UserModel,
{
page: 1,
per_set: 10,
search_data: {
status: true
},
sorting: {
name: 1
}
}
);New Recommended API
The same query can now be written in a much cleaner way.
const db = require("pnk-node-mongo");
const result = await db.find(UserModel, {
response: "users",
page: 1,
limit: 10,
filter: {
status: true
},
sort: {
name: 1
}
});Find One
Legacy
await db.PnkFetchOne(
"user",
UserModel,
{
search_data: {
_id: userId
}
}
);New
await db.findOne(UserModel, {
response: "user",
filter: {
_id: userId
}
});Create Document
Legacy
await db.PnkAddNew({
respKey: "user",
model: UserModel,
data: req.body
});New
await db.create(
UserModel,
req.body,
{
response: "user"
}
);Create Multiple Documents
Legacy
await db.PnkAddNew({
respKey: "users",
model: UserModel,
data: usersArray,
multi: true
});New
await db.createMany(
UserModel,
usersArray,
{
response: "users"
}
);Update One
Legacy
await db.PnkUpdate({
respKey: "user",
model: UserModel,
filter: {
_id: userId
},
updateData: {
$set: {
status: true
}
}
});New
await db.updateOne(UserModel, {
response: "user",
filter: {
_id: userId
},
update: {
$set: {
status: true
}
}
});Update Many
Legacy
await db.PnkUpdate({
respKey: "users",
model: UserModel,
filter: {
status: false
},
updateData: {
$set: {
status: true
}
},
multi: true
});New
await db.updateMany(UserModel, {
response: "users",
filter: {
status: false
},
update: {
$set: {
status: true
}
}
});Delete One
Legacy
await db.PnkDelete({
respKey: "user",
model: UserModel,
filter: {
_id: userId
}
});New
await db.deleteOne(UserModel, {
_id: userId
});Delete Many
Legacy
await db.PnkDelete({
respKey: "users",
model: UserModel,
filter: {
status: false
},
multi: true
});New
await db.deleteMany(UserModel, {
status: false
});Populate
Legacy
await db.PnkFetchWithPopulate(
"users",
UserModel,
{
search_data: {
status: true
},
populate: "role"
}
);New
await db.findPopulate(UserModel, {
response: "users",
filter: {
status: true
},
populate: "role"
});Distinct
Legacy
await db.PnkFetchDistinct(
"cities",
UserModel,
{
distinct_key: "city"
}
);New
await db.distinct(UserModel, {
response: "cities",
distinct_key: "city"
});Aggregate
Legacy
await db.PnkAggregate(
"report",
UserModel,
options
);New
await db.aggregate(
UserModel,
options
);Find One And Update
Legacy
await db.PnkFindAndModify({
respKey: "user",
model: UserModel,
filter: {
_id: userId
},
update: {
$set: {
status: true
}
}
});New
await db.findOneAndUpdate(UserModel, {
response: "user",
filter: {
_id: userId
},
update: {
$set: {
status: true
}
}
});Upgrade Without Fear
✅ No breaking changes
✅ Existing code keeps working
✅ Gradually migrate to the cleaner API whenever you want
✅ Both APIs can be used together in the same project
API Reference
find()
Fetch multiple documents.
Syntax
db.find(model, options)Parameters
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | model | Mongoose Model | ✅ | Mongoose model | | options | Object | ❌ | Query options |
Available Options
| Option | Type | Description | |---------|------|-------------| | response | String | Response key for legacy response format | | filter | Object | MongoDB filter | | page | Number | Page number | | limit | Number | Records per page | | sort | Object | Sorting | | select | Array | Fields to return | | exclude | Array | Fields to exclude |
Example
const users = await db.find(UserModel, {
response: "users",
page: 1,
limit: 20,
filter: {
status: true
},
sort: {
name: 1
}
});findOne()
Fetch a single document.
Syntax
db.findOne(model, options)Example
const user = await db.findOne(UserModel, {
response: "user",
filter: {
_id: userId
}
});findPopulate()
Fetch documents with populated references.
Syntax
db.findPopulate(model, options)Example
const users = await db.findPopulate(UserModel, {
response: "users",
filter: {
status: true
},
populate: [
{
path: "company_id",
select: "company_name"
},
{
path: "builder_id",
select: "builder_name"
}
]
});distinct()
Returns distinct values of a field.
Syntax
db.distinct(model, options)Example
const cities = await db.distinct(UserModel, {
response: "cities",
distinct_key: "city"
});create()
Insert a single document.
Syntax
db.create(model, data, options)Example
await db.create(
UserModel,
{
name: "John",
city: "Mumbai"
},
{
response: "user"
}
);createMany()
Insert multiple documents.
Syntax
db.createMany(model, data, options)Example
await db.createMany(
UserModel,
[
{
name: "John"
},
{
name: "David"
}
],
{
response: "users"
}
);updateOne()
Update one document.
Syntax
db.updateOne(model, options)Example
await db.updateOne(UserModel, {
response: "user",
filter: {
_id: userId
},
update: {
$set: {
city: "Pune"
}
}
});updateMany()
Update multiple documents.
Syntax
db.updateMany(model, options)Example
await db.updateMany(UserModel, {
response: "users",
filter: {
active: false
},
update: {
$set: {
active: true
}
}
});deleteOne()
Delete one document.
Syntax
db.deleteOne(model, filter)Example
await db.deleteOne(UserModel, {
_id: userId
});deleteMany()
Delete multiple documents.
Syntax
db.deleteMany(model, filter)Example
await db.deleteMany(UserModel, {
status: false
});aggregate()
Execute aggregation pipeline.
Syntax
db.aggregate(model, options)Example
const report = await db.aggregate(UserModel, {
search_content: {
status: true
},
group_content: {
_id: "$city",
total: {
$sum: 1
}
}
});aggregateFetch()
Execute aggregation with pagination.
Syntax
db.aggregateFetch(model, options)Example
const report = await db.aggregateFetch(UserModel, {
match: {
status: true
},
page: 1,
per_set: 20
});findOneAndUpdate()
Update and return the updated document.
Syntax
db.findOneAndUpdate(model, options)Example
const user = await db.findOneAndUpdate(UserModel, {
response: "user",
filter: {
_id: userId
},
update: {
$set: {
city: "Pune"
}
}
});Legacy APIs
The following APIs remain available for backward compatibility.
db.PnkFetchAll()
db.PnkFetchOne()
db.PnkFetchDistinct()
db.PnkFetchWithPopulate()
db.PnkUpdate()
db.PnkAddNew()
db.PnkDelete()
db.PnkAggregate()
db.PnkAggregateFetch()
db.PnkFindAndModify()These methods behave exactly as they did in previous versions.
Real World Examples
Basic Find
const users = await db.find(UserModel, {
filter: {
status: true
}
});Pagination
const users = await db.find(UserModel, {
page: 2,
limit: 20
});Search with Multiple Filters
const users = await db.find(UserModel, {
filter: {
status: true,
city: "Pune",
role: "Admin"
}
});Sorting
const users = await db.find(UserModel, {
sort: {
createdAt: -1
}
});Ascending
sort: {
name: 1
}Descending
sort: {
name: -1
}Select Specific Fields
const users = await db.find(UserModel, {
select: [
"name",
"email",
"mobile"
]
});Exclude Fields
const users = await db.find(UserModel, {
exclude: [
"__v",
"password"
]
});Find by ObjectId
const user = await db.findOne(UserModel, {
filter: {
_id: "687b4caa02d66fd733df8244"
}
});String ObjectIds are automatically converted into MongoDB ObjectIds.
Insert One Document
await db.create(
UserModel,
{
name: "John",
email: "[email protected]",
city: "Pune"
}
);Insert Multiple Documents
await db.createMany(
UserModel,
[
{
name: "John"
},
{
name: "David"
},
{
name: "Alex"
}
]
);Update One
await db.updateOne(UserModel, {
filter: {
_id: userId
},
update: {
$set: {
city: "Mumbai"
}
}
});Update Many
await db.updateMany(UserModel, {
filter: {
city: "Pune"
},
update: {
$set: {
active: true
}
}
});Delete One
await db.deleteOne(UserModel, {
_id: userId
});Delete Many
await db.deleteMany(UserModel, {
status: false
});Populate Single Reference
const users = await db.findPopulate(UserModel, {
populate: "company_id"
});Populate Multiple References
const users = await db.findPopulate(UserModel, {
populate: [
{
path: "company_id",
select: "company_name"
},
{
path: "builder_id",
select: "builder_name"
}
]
});Distinct Values
const cities = await db.distinct(UserModel, {
distinct_key: "city"
});Find One and Update
const user = await db.findOneAndUpdate(UserModel, {
filter: {
_id: userId
},
update: {
$set: {
status: true
}
}
});Aggregate Example
const report = await db.aggregate(UserModel, {
search_content: {
status: true
},
group_content: {
_id: "$city",
totalUsers: {
$sum: 1
}
}
});Aggregate with Pagination
const report = await db.aggregateFetch(UserModel, {
match: {
status: true
},
page: 1,
per_set: 25
});Legacy Response Format
const users = await db.find(UserModel, {
response: "users",
filter: {
status: true
}
});Returns
{
users: {
metarow: {
totalRecordsInCurrentSet: 10,
totalRecordsInDb: 120,
totalRecordsInSet: 10,
setNo: 1
},
rows: [...]
},
PinakaResponse: {
ServerStatus: {
value: "SUCCESS"
}
}
}Modern Response
If response is omitted, the library returns plain Mongoose data.
const users = await db.find(UserModel, {
filter: {
status: true
}
});Returns
[
{
_id: "...",
name: "John"
},
{
_id: "...",
name: "David"
}
]Async / Await Example
async function getUsers() {
try {
const users = await db.find(UserModel, {
filter: {
status: true
}
});
console.log(users);
} catch (err) {
console.error(err);
}
}Express.js Example
exports.GetUsers = async (req, res) => {
try {
const users = await db.find(UserModel, {
filter: {
status: true
},
sort: {
createdAt: -1
}
});
res.json(users);
} catch (err) {
res.status(500).json({
error: err.message
});
}
};Advanced Examples
Search + Pagination + Sorting
const users = await db.find(UserModel, {
response: "users",
page: 1,
limit: 20,
filter: {
status: true,
city: "Pune"
},
sort: {
createdAt: -1
}
});Search with Multiple Conditions
const users = await db.find(UserModel, {
filter: {
status: true,
company_id: companyId,
builder_id: builderId,
role: "Sales"
}
});Find Using MongoDB Operators
const users = await db.find(UserModel, {
filter: {
age: {
$gte: 18
},
salary: {
$lt: 50000
}
}
});Using Regular Expressions
const users = await db.find(UserModel, {
filter: {
name: {
$regex: "ram",
$options: "i"
}
}
});Using $in
const users = await db.find(UserModel, {
filter: {
city: {
$in: [
"Pune",
"Mumbai",
"Delhi"
]
}
}
});Using $or
const users = await db.find(UserModel, {
filter: {
$or: [
{
city: "Pune"
},
{
city: "Mumbai"
}
]
}
});Nested Object Search
const users = await db.find(UserModel, {
filter: {
"address.city": "Pune"
}
});Find Active Users
const users = await db.find(UserModel, {
filter: {
status: true,
deleted: false
}
});Populate with Selected Fields
const users = await db.findPopulate(UserModel, {
populate: {
path: "company_id",
select: "company_name company_email"
}
});Populate Multiple Models
const bookings = await db.findPopulate(BookingModel, {
populate: [
{
path: "builder_id",
select: "builder_name"
},
{
path: "company_id",
select: "company_name"
},
{
path: "customer_id",
select: "customer_name mobile"
}
]
});Aggregate Total Users
const report = await db.aggregate(UserModel, {
group_content: {
_id: "$city",
totalUsers: {
$sum: 1
}
}
});Aggregate with Match
const report = await db.aggregate(UserModel, {
search_content: {
status: true
},
group_content: {
_id: "$city",
totalUsers: {
$sum: 1
}
}
});Aggregate with Lookup
const report = await db.aggregateFetch(BookingModel, {
match: {
status: true
},
lookups: [
{
from: "crm_companies",
localField: "company_id",
foreignField: "_id",
as: "company"
}
],
unwind: [
"$company"
],
page: 1,
per_set: 20
});Update if Record Exists
await db.updateOne(UserModel, {
filter: {
email: "[email protected]"
},
update: {
$set: {
city: "Pune"
}
}
});Upsert Example
await db.findOneAndUpdate(UserModel, {
filter: {
email: "[email protected]"
},
update: {
$set: {
city: "Pune"
}
},
options: {
upsert: true
}
});Bulk Insert
const users = [];
for (let i = 1; i <= 1000; i++) {
users.push({
name: `User ${i}`,
status: true
});
}
await db.createMany(UserModel, users);Soft Delete
await db.updateOne(UserModel, {
filter: {
_id: userId
},
update: {
$set: {
deleted: true
}
}
});Hard Delete
await db.deleteOne(UserModel, {
_id: userId
});Using Legacy Response Format
const result = await db.find(UserModel, {
response: "users",
filter: {
status: true
}
});Using Modern Response
const result = await db.find(UserModel, {
filter: {
status: true
}
});Error Handling
try {
const users = await db.find(UserModel);
console.log(users);
}
catch (err) {
console.error(err);
}Express Route Example
exports.GetCompanyList = async (req, res) => {
try {
const result = await db.find(CompanyModel, {
response: "companylist",
page: 1,
limit: 20,
sort: {
company_name: 1
}
});
res.send(result);
}
catch (err) {
res.status(500).send(err);
}
};Backward Compatible
Already using previous versions?
No problem.
db.PnkFetchAll(...)
db.PnkFetchOne(...)
db.PnkUpdate(...)
db.PnkDelete(...)continue to work exactly as before.
Modern API
The library now supports clean method names.
find()
findOne()
create()
createMany()
updateOne()
updateMany()
deleteOne()
deleteMany()
aggregate()
aggregateFetch()
findPopulate()
findOneAndUpdate()Automatic ObjectId Conversion
No need to manually convert string IDs.
Instead of
{
_id: new mongoose.Types.ObjectId(id)
}just write
{
_id: id
}The library automatically converts valid ObjectId strings.
Legacy Response Support
If your application depends on the original Pinaka response format, simply provide the response key.
const result = await db.find(UserModel, {
response: "users"
});Response
{
users: {
rows: [ ],
metarow: { }
},
PinakaResponse: { }
}Plain Mongoose Response
Prefer plain arrays?
Simply omit the response property.
const users = await db.find(UserModel);Returns
[
{
_id: "...",
name: "John"
}
]Minimal Learning Curve
If you already know Mongoose, you already know pnk-node-mongo.
There are no custom query languages or complex configuration files.
Production Ready
Designed for real-world applications including:
- CRM Systems
- ERP Applications
- HRMS
- Hospital Management
- School Management
- Inventory Systems
- Billing Software
- Real Estate CRM
- Multi-Tenant SaaS Applications
Built on Mongoose
No wrappers around MongoDB drivers.
No custom database engine.
No vendor lock-in.
Just clean APIs built on top of Mongoose.
Easy Migration
You can migrate gradually.
Old and new APIs can be used together in the same project.
No breaking changes.
No code rewrite required.
Best Practices
Following these recommendations will help you build cleaner, faster, and more maintainable applications with pnk-node-mongo.
1. Prefer the New API
✅ Recommended
const users = await db.find(UserModel, {
filter: {
status: true
}
});❌ Legacy (still supported)
const users = await db.PnkFetchAll(
"users",
UserModel,
options
);2. Always Use Pagination
Avoid loading an entire collection.
✅ Good
const users = await db.find(UserModel, {
page: 1,
limit: 20
});❌ Bad
const users = await db.find(UserModel);on collections containing millions of records.
3. Fetch Only Required Fields
Instead of loading every field,
const users = await db.find(UserModel, {
select: [
"name",
"email",
"mobile"
]
});or exclude unwanted fields
const users = await db.find(UserModel, {
exclude: [
"__v",
"password"
]
});4. Use MongoDB Indexes
Large collections should always have indexes on frequently searched fields.
Example
UserSchema.index({
email: 1
});
UserSchema.index({
company_id: 1,
status: 1
});5. Use createMany() for Bulk Inserts
Instead of
for(const user of users){
await db.create(UserModel,user);
}Use
await db.createMany(UserModel, users);This is significantly faster.
6. Use updateMany() for Bulk Updates
Instead of
for(const id of ids){
await db.updateOne(...)
}Use
await db.updateMany(UserModel,{
filter:{
status:false
},
update:{
$set:{
status:true
}
}
});7. Populate Only When Needed
Populate performs additional queries.
Use it only when referenced data is required.
populate:"company_id"instead of populating every reference.
8. Keep Filters Simple
Good
filter:{
company_id:id,
status:true
}Avoid deeply nested queries unless necessary.
9. Use Aggregation for Reports
Instead of processing thousands of records in JavaScript,
use MongoDB Aggregation.
const report=await db.aggregate(UserModel,{
group_content:{
_id:"$city",
total:{
$sum:1
}
}
});10. Use findOne() for Single Documents
Instead of
const users=await db.find(...);use
const user=await db.findOne(...);when only one document is expected.
11. Handle Errors
Always use try/catch.
try{
const users=await db.find(UserModel);
}
catch(err){
console.error(err);
}12. Prefer Async/Await
Recommended
const users=await db.find(UserModel);instead of nested Promise chains.
13. Keep Business Logic Outside Queries
Good
const users=await db.find(UserModel,{
filter:{
status:true
}
});Business logic should remain inside services/controllers rather than query helpers.
14. Use Plain Responses for New Projects
New applications should use the default plain Mongoose response.
const users=await db.find(UserModel);Use
response:"users"only when maintaining compatibility with existing applications.
15. Upgrade Gradually
All legacy APIs remain available.
You can migrate one module at a time without changing your entire application.
This makes upgrading safe even for large production systems.
Changelog
v1.2.2
Added
- Modern MongoDB inspired API.
find()findOne()findPopulate()distinct()create()createMany()updateOne()updateMany()deleteOne()deleteMany()aggregate()aggregateFetch()findOneAndUpdate()
Improved
- Cleaner method names.
- Simplified options object.
- Automatic option normalization.
- Automatic
_idconversion from string toObjectId. - Better pagination handling.
- Better projection support.
- Better populate support.
- Improved aggregate helpers.
- Improved TypeScript definitions.
- Improved README with complete examples.
Fixed
- Pagination issues when page was
0. - Better handling of empty result sets.
- Improved update response handling.
- Improved delete response handling.
- Better aggregate pagination.
Compatibility
- Fully backward compatible with all legacy methods.
- Existing projects require no code changes.
- Legacy APIs continue to work.
v1.2.0
- Initial release of the modern API.
- Introduced wrapper methods over legacy functions.
- Added option normalization.
- Added TypeScript support.
- Improved package structure.
v1.1.x
- Legacy Pinaka Mongo helper functions.
- Response wrapper support.
- Aggregate helpers.
- Populate helpers.
- CRUD helper methods.
Future Roadmap
The goal of pnk-node-mongo is to become a complete MongoDB utility library that reduces boilerplate while keeping the API clean, flexible, and production-ready.
Planned Features
Repository Pattern
const users = await UserRepository.find({
filter: {
status: true
}
});Fluent Query Builder
const users = await db
.query(User)
.where({
status: true
})
.sort({
name: 1
})
.limit(10)
.page(1)
.find();Transactions
await db.transaction(async (session) => {
await db.create(User, userData, {
session
});
await db.updateOne(Account, {
filter: {
_id: accountId
},
update: {
balance: 1000
},
options: {
session
}
});
});Bulk Operations
await db.bulkWrite(User, [
{
insertOne: {
document: user
}
},
{
updateOne: {
filter: {
_id: id
},
update: {
$set: {
active: true
}
}
}
}
]);Soft Delete Support
await db.softDelete(User, {
_id: userId
});Restore Deleted Records
await db.restore(User, {
_id: userId
});Audit Logging
Automatically store:
- Created By
- Created On
- Updated By
- Updated On
- Deleted By
- Deleted On
- IP Address
- Device Information
Query Middleware
db.beforeFind((filter) => {
filter.company_id = currentCompany;
});
db.afterFind((data) => {
return data;
});Validation Layer
await db.create(User, data, {
validate: true
});Schema Helpers
timestamps();
softDelete();
auditFields();
companyFilter();Performance Metrics
- Query execution time
- Slow query logging
- Query statistics
- Database performance metrics
Better TypeScript Support
- Strong typing
- Generic models
- Better IntelliSense
- Auto-completion
Plugin System
db.use(CachePlugin);
db.use(AuditPlugin);
db.use(EncryptionPlugin);Caching
- Redis support
- In-memory cache
- Automatic cache invalidation
AI Query Generator
Convert natural language into MongoDB queries.
Example:
Find all active customers from Bangalore created this month↓
{
filter: {
city: "Bangalore",
status: true,
created_on: {
$gte: ...
}
}
}More MongoDB Features
- Atlas Search
- Vector Search
- Time Series Collections
- Change Streams
- GridFS Helpers
- Full Text Search
- Geospatial Queries
Community Contributions
Feature requests, bug reports, improvements, and pull requests are always welcome.
If you have ideas to improve pnk-node-mongo, feel free to open an issue or submit a pull request.
Together, we can make pnk-node-mongo one of the easiest and most productive MongoDB helper libraries for Node.js.
Contributing
Thank you for your interest in contributing to pnk-node-mongo.
Contributions of all kinds are welcome, including bug fixes, new features, documentation improvements, examples, performance enhancements, and TypeScript improvements.
Reporting Bugs
If you find a bug, please create an issue containing:
- Package version
- Node.js version
- Mongoose version
- MongoDB version
- Operating System
- Steps to reproduce
- Expected behavior
- Actual behavior
- Sample code (if possible)
Feature Requests
Feature requests are always welcome.
Before creating a request:
- Check existing issues.
- Explain the use case.
- Provide sample code if possible.
Development Setup
Clone the repository.
git clone https://github.com/YOUR_USERNAME/pnk-node-mongo.gitInstall dependencies.
npm installBuild the package.
npm run buildRun your project using the local package.
Pull Requests
Before submitting a Pull Request:
- Follow existing coding style.
- Keep changes focused.
- Update documentation when necessary.
- Add examples for new features.
- Test your changes thoroughly.
Coding Guidelines
- Use modern JavaScript (ES2020+)
- Prefer async/await
- Keep functions small and reusable
- Avoid breaking backward compatibility
- Write readable and maintainable code
Project Goals
The primary goals of this library are:
- Simple API
- Minimal boilerplate
- High performance
- Clean architecture
- Production-ready code
- Full backward compatibility
Ways to Contribute
You can contribute by:
- Reporting bugs
- Improving documentation
- Adding examples
- Improving TypeScript definitions
- Optimizing performance
- Suggesting new features
- Fixing issues
- Improving test coverage
Code of Conduct
Please be respectful and constructive while contributing.
We welcome developers of all experience levels and appreciate every contribution that helps improve the project.
Thank You ❤️
Every contribution, whether it's a bug report, documentation improvement, or code enhancement, helps make pnk-node-mongo better for the community.
Thank you for your support!
FAQ
Is this library a replacement for Mongoose?
No.
pnk-node-mongo is built on top of Mongoose and simplifies common CRUD operations while remaining fully compatible with Mongoose models.
Can I still use Mongoose methods?
Yes.
You can freely mix Mongoose and pnk-node-mongo.
await User.find();
await db.find(User);Does it support TypeScript?
Yes.
Type definitions are included.
Is backward compatibility maintained?
Yes.
All legacy Pnk* methods continue to work.
Can I use aggregation?
Yes.
Both aggregate helper methods are supported.
Does it support populate?
Yes.
Use findPopulate().
Does it work with MongoDB Atlas?
Yes.
Since it uses Mongoose internally, it works with MongoDB Atlas without additional configuration.
Can I use transactions?
Yes.
Transactions can be passed through Mongoose session options.
Which Node.js versions are supported?
Node.js 18+
Recommended:
- Node.js 20+
- Mongoose | 6.x / 7.x / 8.x |
License
MIT License
Copyright (c) 2026 Pinaka Digital Technologies
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software.
See the LICENSE file for full details.
Acknowledgements
Special thanks to:
- MongoDB Team
- Mongoose Team
- Node.js Community
- Open Source Contributors
for building the technologies that power this library.
Support
If you encounter any issues or have feature requests:
- Open a GitHub Issue
- Submit a Pull Request
- Share suggestions for improvements
Community feedback helps make pnk-node-mongo better with every release.
Star the Project ⭐
If pnk-node-mongo saves you development time, please consider giving the repository a ⭐ on GitHub.
Your support helps the project grow and encourages future development.
Thank you for using pnk-node-mongo!
