eduardweb.
Prisma ORMAvansat#security#typescript#prisma#postgresql#zod

Când Prisma nu mai poate: raw queries în siguranță cu $queryRaw și Zod

De Dan Ciobanu, 28 iun. 2026 · 18 vizualizări · 3 like-uri

Postat 28 iun. 2026
typescript
import { prisma } from './db';
import { z } from 'zod';

const UserReportSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  post_count: z.number(),
});

type UserReport = z.infer<typeof UserReportSchema>;

async function getActiveUsersReport(minPosts: number): Promise<UserReport[]> {
  const rawData = await prisma.$queryRaw`
    SELECT u.id, u.email, COUNT(p.id)::int as post_count
    FROM "User" u
    LEFT JOIN "Post" p ON p."authorId" = u.id
    GROUP BY u.id, u.email
    HAVING COUNT(p.id) >= ${minPosts}
  `;

  return z.array(UserReportSchema).parse(rawData);
}

Să fim sinceri: ORM-ul e minunat până când ai de făcut un raport cu trei JOIN-uri și un window function. Atunci Prisma începe să gâfâie și n-ai de ales decât să cobori în mină, la $queryRaw. În postarea asta îți arăt cum să faci asta fără să lași ușa deschisă la SQL injection și cum să recapeți type-safety-ul pierdut folosind Zod.

De ce am coborât la SQL chior (și costul ascuns)

Am avut cazul la un proiect cu vreo 12k utilizatori activi unde trebuia să generăm un feed personalizat destul de complex. Relații peste relații. Varianta scrisă clasic cu Prisma Client genera un monstru de query de 4 pagini și dura cam 800ms pe o bază de date destul de aerisită. Enorm.

Am rescris totul în SQL nativ utilizând $queryRaw și am coborât instant la 45ms. Am economisit resurse serioase și am salvat serverul de la un blocaj iminent. Dar vine la pachet cu un trade-off sincer: merge incredibil de rapid pentru query-uri complexe, dar e complet nasol pentru mentenanță. Pierzi instantaneu plasa de siguranță din TypeScript. Prisma îți dă înapoi un tip unknown sau, mai rău, un any mascat prin genericele sale implicite, iar schema ta din editor nu mai garantează absolut nimic la runtime.

Capcana SQL Injection și cum te salvează Tagged Templates

Cea mai mare greșeală pe care o văd în codul scris de developeri care vin din alte ecosisteme este concatenarea clasică de stringuri în $queryRaw. Ceva de genul prisma.$queryRawUnsafe("SELECT * FROM User WHERE id = " + userId). Nu face asta niciodată, este o invitație deschisă la dezastru.

Prisma folosește sub capotă tagged templates din ES6. Când scrii query-ul direct ca un template literal, adică prisma.$queryRawSELECT * FROM User WHERE id = ${userId}``, Prisma nu face o concatenare chioară. Ea parsează șirul și trimite bazei de date un query parametrizat, exact ca un prepared statement. Parametrii sunt trimiși separat, neutralizând complet orice tentativă de SQL injection.

Dar atenție la dinamică: dacă ai nevoie de sortare dinamică (de exemplu, ordonare după o coloană trimisă din frontend), tagged templates nu funcționează pentru numele de coloane sau tabele. Acolo nu poți pune ${columnName} direct. Va trebui să folosești Prisma.raw(), dar fă asta exclusiv după ce ai trecut inputul printr-un whitelist manual rigid în cod. Altfel, te expui direct atacurilor.

Type-safety pe bune la runtime cu Zod

Pentru că Prisma nu are cum să știe ce returnează query-ul tău custom, tentația este să folosești type assertion: const users = rawData as User[]. Să fim serioși, asta e doar o minciună pe care o spui compilatorului ca să nu mai dea erori. Dacă tipul de date din DB se schimbă subtil, aplicația ta va crăpa silențios la runtime.

Soluția profesionistă este să folosești Zod pentru validare direct la granița dintre baza de date și codul tău. Scrii o schemă Zod simplă care descrie exact structura pe care o aștepți de la query-ul tău brut. Apoi rulezi .parse() pe datele returnate. Da, adaugi o mică penalizare de performanță la validare, dar dormi liniștit noaptea știind că datele sunt 100% sigure.

Tu cum gestionezi query-urile complexe în Prisma: rămâi la ORM până la capăt sau cobori rapid la SQL brut?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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