f in x
CRUD with MongoDB in Node.js and Python: Production-Ready Operations
> cd .. / HUB_EDITORIALE
Sviluppo di siti web

CRUD with MongoDB in Node.js and Python: Production-Ready Operations

[2026-06-17] Author: Ing. Calogero Bono

Your backend speaks MongoDB but every time you write a find() or insertOne() you feel there's room for improvement? That happens. CRUD looks trivial, but missing an index, forgetting a try/catch, or not handling connection pooling costs a lot in production. We at Meteora Web have seen projects slow down by 300% due to a poorly written query and data lost because of missing atomicity. This guide takes you from basic CRUD to what actually works in production, using Node.js and Python drivers — ready to copy and paste into your code.

CRUD Is Not Just Insert-Read-Update-Delete

Let's start with a concrete scenario: you manage a product catalog for an e-commerce site. Daily, new items arrive, prices change, products get discontinued. Everything runs fine during development. Then in production, traffic spikes: 10,000 requests per minute, write operations timeout, the database crashes from too many open connections. The problem isn't MongoDB — it's how you talk to it. Operational CRUD isn't just writing working queries; it's writing efficient, atomic, and resilient queries.

Sponsored Protocol

Connection Management: The First Real CRUD

Never open a new connection for each operation. Use a shared connection pool. Here's the proper approach for Node.js with the mongodb driver:

const { MongoClient } = require('mongodb');

const uri = process.env.MONGODB_URI;
const client = new MongoClient(uri, { 
  maxPoolSize: 10, 
  serverSelectionTimeoutMS: 5000 
});

async function connectDB() {
  if (!client.isConnected()) await client.connect();
  return client.db('ecommerce');
}

module.exports = { connectDB };

For Python with pymongo:

from pymongo import MongoClient
import os

uri = os.getenv('MONGODB_URI')
client = MongoClient(uri, maxPoolSize=10, serverSelectionTimeoutMS=5000)

def get_db():
    return client['ecommerce']

Why it pays off: the connection pool avoids opening and closing sockets per request. Average response time drops by 70% compared to one-shot connections.

Create: InsertOne and InsertMany with Control

When inserting documents, never trust input data without validation. A price field as a string instead of a number can break all aggregations. We almost always use insertMany with ordered: false for large batches: if one document fails, the rest still get inserted.

Sponsored Protocol

Node.js: ordered vs unordered insertMany

const db = await connectDB();
const products = db.collection('products');

// Bulk insert with ordered: false
const docs = [
  { sku: 'P001', name: 'T-shirt', price: 29.99 },
  { sku: 'P002', name: 'Jeans', price: 79.99 },
  { sku: 'P001', name: 'Duplicate' } // will fail on unique index, but rest pass
];

try {
  const result = await products.insertMany(docs, { ordered: false });
  console.log(`Inserted ${result.insertedCount} documents`);
} catch (e) {
  console.error('Errors on some documents:', e.writeErrors);
}

Python: insert_many with error handling

db = get_db()
products = db['products']

docs = [
    {'sku': 'P001', 'name': 'T-shirt', 'price': 29.99},
    {'sku': 'P002', 'name': 'Jeans', 'price': 79.99},
    {'sku': 'P001', 'name': 'Duplicate'}
]

try:
    result = products.insert_many(docs, ordered=False)
    print(f"Inserted {len(result.inserted_ids)} documents")
except pymongo.errors.BulkWriteError as e:
    print('Failed documents:', e.details['writeErrors'])

Common mistake: not handling BulkWriteError crashes the app. With ordered: false you avoid total blocks.

Sponsored Protocol

Read: find, findOne and Efficient Projections

Reading without criteria is the fastest way to make the database cry. Always use projections to return only the fields you need and filters with indexes. In an e-commerce product listing, there's no need to include long description fields.

Node.js: query with projection and limit

const result = await products.find(
  { price: { $gte: 10, $lte: 100 } },
  { projection: { name: 1, price: 1, _id: 0 } }
).limit(20).toArray();

Python: same

result = list(products.find(
    {'price': {'$gte': 10, '$lte': 100}},
    {'name': 1, 'price': 1, '_id': 0}
).limit(20))

Warning: without limit in production, MongoDB reads all matching documents and then discards 20. Waste of I/O.

Update: updateOne, updateMany and Atomicity

When updating stock, you must be sure two simultaneous requests don't sell the last item twice. Use atomic operators like $inc and $set.

Sponsored Protocol

Node.js: atomic decrement with guard

const result = await products.updateOne(
  { sku: 'P001', stock: { $gt: 0 } },
  { $inc: { stock: -1 } }
);
if (result.modifiedCount === 0) {
  console.log('Product out of stock or not found');
}

Python: same

result = products.update_one(
    {'sku': 'P001', 'stock': {'$gt': 0}},
    {'$inc': {'stock': -1}}
)
if result.modified_count == 0:
    print('Product out of stock or not found')

Why it pays off: the $inc operator is atomic. If two requests arrive simultaneously, each decreases by 1. No duplicates.

Delete: deleteOne, deleteMany and More

Deleting is dangerous. We almost always use soft delete via a deletedAt field, especially in fiscal contexts (and we say this as former accountants). But when a true delete is necessary, do it with awareness.

Node.js: conditional deleteOne

const result = await products.deleteOne({ sku: 'P999', deletedAt: { $exists: false } });
if (result.deletedCount === 0) console.log('Document not found');

Python: same

result = products.delete_one({'sku': 'P999', 'deletedAt': {'$exists': False}})
if result.deleted_count == 0:
    print('Document not found')

Common Mistakes We See in Client Projects

We've inherited projects where:

Sponsored Protocol

  • Connections were opened and closed per request (slow).
  • No index on sku (every search was a full collection scan).
  • Bulk writes were ordered (one error rejected the whole batch).
  • No check on modifiedCount — updates failed silently.

For a comprehensive overview of design patterns, read our pillar guide on MongoDB and NoSQL.

In Summary — What to Do Now

  1. Review connection management: use a shared pool in both drivers.
  2. Always insert with ordered: false in batches and handle BulkWriteError.
  3. Read with projection and limit — avoid carrying useless data.
  4. Use atomic operators ($inc, $set, $push) for concurrent updates.
  5. Prefer soft delete with a deletedAt field for reversibility.

CRUD is never just CRUD. It's the foundation of your system's solidity. We, at Meteora Web, see it every day: an app that writes well scales well. If you want to dive deeper into driver specifics, check the official documentation for Node.js or PyMongo.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()