Il tuo backend parla con MongoDB ma ogni volta che scrivi un find() o un insertOne() senti di poter fare di più? Succede. Il CRUD sembra banale, ma sbagliare un indice, dimenticare un try/catch o non gestire la connessione per le operazioni bulk costa caro in produzione. Noi, di Meteora Web, abbiamo visto progetti rallentare del 300% per una query scritta male e dati persi per mancanza di atomicità. Questa guida ti porta dal CRUD base a quello che funziona sul serio, con driver Node.js e Python, pronto per essere copiato e incollato nel tuo codice.
Il CRUD non è solo Inserisci-Leggi-Aggiorna-Cancella
Partiamo da uno scenario concreto: gestisci un catalogo prodotti per un e-commerce. Ogni giorno arrivanou nuovi articoli, aggiorni prezzi, cancelli prodotti fuori catalogo. In fase di sviluppo tutto fila liscio. Poi in produzione arriva il carico: 10.000 richieste al minuto, le scritture vanno in timeout, il database va in crash per connessioni aperte e non chiuse. Il problema non è MongoDB: è come gli parli. Il CRUD operativo non è solo scrivere query funzionanti, ma query efficienti, atomiche e resilienti.
Gestione della connessione: la prima vera CRUD
Non aprire una nuova connessione per ogni operazione. Usa un client pool condiviso. Ecco l’approccio giusto per Node.js con il driver mongodb:
Sponsored Protocol
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 };
Per Python con 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']
Perché conviene: il pool di connessioni evita di aprire e chiudere socket a ogni richiesta. Tempo di risposta medio si riduce del 70% rispetto a connessioni one-shot.
Create: InsertOne e InsertMany con controllo
Quando inserisci documenti, non fidarti mai dei dati in input senza validazione. Un campo price stringa al posto di number può rompere tutte le aggregazioni. Noi usiamo quasi sempre insertMany con ordered: false per batch di grandi dimensioni: se un documento fallisce, gli altri vengono inseriti comunque.
Sponsored Protocol
Node.js: insertMany ordinato vs non ordinato
const db = await connectDB();
const products = db.collection('products');
// Inserimento bulk con ordered: false
const docs = [
{ sku: 'P001', name: 'Maglietta', price: 29.99 },
{ sku: 'P002', name: 'Jeans', price: 79.99 },
{ sku: 'P001', name: 'Duplicato' } // fallirà su unique index, ma il resto passa
];
try {
const result = await products.insertMany(docs, { ordered: false });
console.log(`Inseriti ${result.insertedCount} documenti`);
} catch (e) {
console.error('Errore su alcuni documenti:', e.writeErrors);
}
Python: insert_many con gestione errori
db = get_db()
products = db['products']
docs = [
{'sku': 'P001', 'name': 'Maglietta', 'price': 29.99},
{'sku': 'P002', 'name': 'Jeans', 'price': 79.99},
{'sku': 'P001', 'name': 'Duplicato'}
]
try:
result = products.insert_many(docs, ordered=False)
print(f"Inseriti {len(result.inserted_ids)} documenti")
except pymongo.errors.BulkWriteError as e:
print('Documenti falliti:', e.details['writeErrors'])
Errore comune: non gestire BulkWriteError fa crashare l'app. Con ordered: false eviti blocchi totali.
Sponsored Protocol
Read: find, findOne e proiezioni efficienti
Leggere senza criterio è il modo più veloce per far piangere il database. Usa sempre proiezioni per restituire solo i campi che servono e filtri con indici. In un e-commerce, per listare prodotti in homepage, non serve mandare i campi descrizione lunghi.
Node.js: query con proiezione e limit
const result = await products.find(
{ price: { $gte: 10, $lte: 100 } },
{ projection: { name: 1, price: 1, _id: 0 } }
).limit(20).toArray();
Python: idem
result = list(products.find(
{'price': {'$gte': 10, '$lte': 100}},
{'name': 1, 'price': 1, '_id': 0}
).limit(20))
Attenzione: se non metti limit in produzione, MongoDB legge tutti i documenti e poi butta via i 20 richiesti. Spreco di I/O.
Update: updateOne, updateMany e atomicità
Quando aggiorni uno stock, devi essere certo che due richieste simultanee non portino a vendite multiple dell’ultimo pezzo. Usa operatori atomici come $inc e $set.
Node.js: decremento atomico con controllo
const result = await products.updateOne(
{ sku: 'P001', stock: { $gt: 0 } },
{ $inc: { stock: -1 } }
);
if (result.modifiedCount === 0) {
console.log('Prodotto esaurito o inesistente');
}
Python: idem
result = products.update_one(
{'sku': 'P001', 'stock': {'$gt': 0}},
{'$inc': {'stock': -1}}
)
if result.modified_count == 0:
print('Prodotto esaurito o inesistente')
Perché conviene: l’operatore $inc è atomico. Se due richieste arrivano insieme, ognuna diminuisce di 1. Nessun doppione.
Sponsored Protocol
Delete: deleteOne, deleteMany e non solo
Cancellare è pericoloso. Noi adottiamo quasi sempre un soft delete tramite campo deletedAt, specialmente in contesti fiscali come la contabilità (e lo diciamo da ex contabili). Ma quando il delete è necessario, fallo con consapevolezza.
Node.js: deleteOne condizionale
const result = await products.deleteOne({ sku: 'P999', deletedAt: { $exists: false } });
if (result.deletedCount === 0) console.log('Documento non trovato');
Python: idem
result = products.delete_one({'sku': 'P999', 'deletedAt': {'$exists': False}})
if result.deleted_count == 0:
print('Documento non trovato')
Errori comuni che vediamo nei progetti dei clienti
Abbiamo ereditato progetti dove:
Sponsored Protocol
- Le connessioni venivano aperte e chiuse a ogni richiesta (lento).
- Non c’erano indici su
sku(ogni ricerca era una scansione completa). - Le scritture bulk erano ordinate (un errore e tutto il lotto veniva respinto).
- Nessun controllo su
modifiedCount— gli aggiornamenti fallivano in silenzio.
Per una trattazione completa dei pattern di progettazione, leggi la nostra guida pillar su MongoDB e NoSQL.
In sintesi — cosa fare adesso
- Rivedi la gestione della connessione: usa un pool condiviso in entrambi i driver.
- Inserisci sempre con
ordered: falsenei batch e gestisciBulkWriteError. - Leggi con proiezione e limit — evita di trasportare dati inutili.
- Usa operatori atomici (
$inc,$set,$push) per update concorrenti. - Preferisci soft delete con campo
deletedAtper reversibilità.
Il CRUD non è mai solo CRUD. È la base della solidità del tuo sistema. Noi, di Meteora Web, lo vediamo ogni giorno: un’app che scrive bene è un’app che scala. Se vuoi approfondire l’integrazione con driver specifici, consulta la documentazione ufficiale di Node.js o PyMongo.