ORM: Drizzle și Prisma
Scheme în TypeScript, query-uri tipate, migrații; SQL direct vs Drizzle vs Prisma.
Actualizat
Ce este un ORM
Un ORM (Object-Relational Mapper) îți permite să lucrezi cu baza de date prin cod TypeScript în loc de SQL scris ca text. Definești tabelele o dată, ca schemă, iar ORM-ul îți dă:
- query-uri tipate — autocomplete pe coloane, erori la compilare dacă greșești un nume;
- migrații — istoric versionat al schimbărilor de structură;
- protecție implicită contra SQL injection.
Variante de acces la baza de date
| Variantă | Exemplu | Plusuri | Minusuri |
|---|---|---|---|
| SQL direct (driver) | sql`SELECT ...` cu @neondatabase/serverless |
control total, zero abstracție | fără tipuri automate, migrații manuale |
| query builder / ORM „SQL-like” | Drizzle, Kysely | aproape de SQL, tipat, ușor, merge pe edge | trebuie să știi SQL |
| ORM clasic | Prisma | API foarte simplu, tooling excelent (Studio) | abstracție mai groasă, generare de client |
Drizzle — schema și query-uri
// shared/api/schema.ts
import { pgTable, serial, text, integer, timestamp } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
})
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
userId: integer('user_id').notNull().references(() => users.id),
title: text('title').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
})import { drizzle } from 'drizzle-orm/neon-http'
import { desc, eq } from 'drizzle-orm'
const db = drizzle(process.env.DATABASE_URL!)
const latest = await db.select().from(posts).where(eq(posts.userId, 1)).orderBy(desc(posts.createdAt)).limit(10)
const [created] = await db.insert(posts).values({ userId: 1, title: 'Salut' }).returning()
type Post = typeof posts.$inferSelect // tipul unui rând, din schemăObservă: userId în cod, user_id în baza de date — maparea camelCase ↔ snake_case e făcută de ORM.
Prisma — aceeași idee, alt stil
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}const latest = await prisma.post.findMany({ where: { authorId: 1 }, orderBy: { id: 'desc' }, take: 10, include: { author: true } })Migrații
O migrație e un fișier care descrie o schimbare de structură (tabel nou, coloană nouă), aplicat o singură dată, în ordine, pe fiecare bază de date (local, preview, producție).
npx drizzle-kit generate # din schema TS → fișier SQL de migrație
npx drizzle-kit migrate # aplică migrațiile care nu au rulat încă| Regulă | De ce |
|---|---|
| migrațiile intră în Git | aceeași istorie peste tot |
| nu modifici o migrație deja aplicată | creezi una nouă |
| schimbări „distructive” (redenumire, ștergere coloană) în pași | datele existente și codul vechi trebuie să meargă între timp |
Unde stau în FSD
Schema și clientul DB → shared/api/. Query-urile unei entități → entities/<x>/api/ (ca entities/lesson/api/lesson-api.ts din proiect). Componentele nu importă niciodată direct clientul DB.
Pe scurt
- ORM = schemă în TS → query-uri tipate + migrații + protecție la injection.
- Drizzle = aproape de SQL, ușor; Prisma = API simplu, tooling bogat; SQL direct = control total.
- Migrațiile sunt versionate în Git și aplicate în ordine, niciodată editate după aplicare.