eduardweb.
PerformanceIntermediar#nodejs#performance#prisma#postgresql

Cum prinzi și rezolvi N+1 în Prisma înainte să-ți moară baza de date

De Diana Oprea, 5 iul. 2026 · 14 vizualizări · 2 like-uri

Postat 5 iul. 2026
typescript
// ❌ Varianta proastă (generează N+1 query-uri)
const posts = await prisma.post.findMany({ take: 50 });
const postsWithAuthors = await Promise.all(
  posts.map(async (post) => {
    const author = await prisma.user.findUnique({ where: { id: post.authorId } });
    return { ...post, author };
  })
);

// ✔️ Varianta optimizată (2 query-uri în spate prin Prisma Batching)
const postsOptimized = await prisma.post.findMany({
  take: 50,
  include: {
    author: true,
  },
});

Am pățit-o toți: codul arată super curat în Express sau NestJS, dar în spate Prisma face mii de interogări pentru o listă banală. Problema de N+1 te lovește fix când începe să crească baza de date și te întrebi de ce răspunde API-ul în 2 secunde în loc de 50 de milisecunde. În postarea asta vedem cum depistezi buba asta rapid folosind $metrics și cum o rezolvi fără să-ți complici viața.

Cum am dat de dracu' la un proiect cu 12k useri

La un proiect destul de măricel, aveam de randat un feed de activitate. Clasic: luam ultimele 100 de postări, iar pentru fiecare postare aveam nevoie de avatarul autorului și de ultimele trei comentarii. Am scris un map rapid peste postări și am apelat prisma.user.findUnique() în buclă pentru fiecare autor. La code review a trecut fără probleme, că deh, codul era „curat” și ușor de citit.

În producție, când am trecut de 12.000 de utilizatori activi, baza de date a început să transpire grav. CPU-ul pe Postgres stătea constant în 85%. Ne-am dat seama că pentru o singură încărcare a feed-ului, Prisma genera 101 query-uri separate. Rețeaua era gâtuită de atâtea conexiuni dus-întors.

Cum detectezi problema cu $metrics

În loc să pui manual console.time() peste tot sau să stai cu ochii în logurile din Postgres, Prisma are o metodă nativă foarte utilă, dar destul de ignorată: $metrics. O poți folosi în middleware-uri sau în teste ca să monitorizezi ce se întâmplă sub capotă.

Eu prefer să expun un endpoint ascuns de admin (sau să loghez în mediul de staging) rezultatul de la prisma.$metrics.json(). Te uiți direct la proprietatea prisma_client_queries_total. Dacă vezi că numărul de query-uri active crește exponențial când schimbi limita de elemente din paginare, ai un N+1 de toată frumusețea.

Rezolvarea: include vs Fluent API

Cea mai la îndemână metodă de rezolvare este să folosești opțiunea include în query-ul inițial. În felul ăsta, Prisma știe de la început ce are de adus și va optimiza interogările în spate, de obicei folosind un singur query cu JOIN sau două query-uri cu operatorul IN.

Totuși, există un trade-off sincer aici. include este excelent pentru relații simple de 1-2 niveluri. Dacă ai relații adânci (de genul postare -> autor -> profil -> setări), codul de interogare devine un monstru JSON greu de întreținut. În plus, încarci enorm de multă informație în memoria Node.js, ceea ce poate duce la probleme de garbage collection dacă ai trafic mare.

Pentru relații foarte complexe, e mai sănătos să folosești Fluent API sau să spargi logica în query-uri separate controlate prin Promise.all, unde poți mapa datele manual în memorie. Am reușit să reducem timpul de răspuns cu 40% la un endpoint critic doar prin înlocuirea unui include gigant cu două query-uri paralele bine targetate.

Voi cum gestionați problemele astea de performanță în Prisma? Vă bazați pe logurile din Postgres sau aveți alerte setate direct pe metricile ORM-ului?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.