ORM: Drizzle и Prisma
Схемы на TypeScript, типизированные запросы, миграции; чистый SQL, Drizzle или Prisma.
Обновлено
Что такое ORM
ORM (Object-Relational Mapper) позволяет работать с базой через код на TypeScript вместо SQL в виде текста. Вы один раз описываете таблицы как схему, а ORM даёт:
- типизированные запросы — автодополнение по колонкам, ошибки при компиляции, если ошиблись в имени;
- миграции — версионированную историю изменений структуры;
- встроенную защиту от SQL-инъекций.
Способы доступа к базе
| Вариант | Пример | Плюсы | Минусы |
|---|---|---|---|
| чистый SQL (драйвер) | sql`SELECT ...` с @neondatabase/serverless |
полный контроль, ноль абстракций | нет автоматических типов, ручные миграции |
| конструктор запросов / ORM «как SQL» | Drizzle, Kysely | близко к SQL, типизировано, легко, работает на edge | нужно знать SQL |
| классический ORM | Prisma | очень простой API, отличный инструментарий (Studio) | толще абстракция, генерация клиента |
Drizzle — схема и запросы
// 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: 'Privet' }).returning()
type Post = typeof posts.$inferSelect // тип строки, из схемыОбратите внимание: userId в коде, user_id в базе — сопоставление camelCase ↔ snake_case делает ORM.
Prisma — та же идея, другой стиль
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 } })Миграции
Миграция — файл, описывающий изменение структуры (новая таблица, новая колонка), который применяется один раз, по порядку, к каждой базе (локальной, preview, продакшен).
npx drizzle-kit generate # из TS-схемы → SQL-файл миграции
npx drizzle-kit migrate # применяет миграции, которые ещё не выполнялись| Правило | Почему |
|---|---|
| миграции хранятся в Git | одинаковая история везде |
| уже применённую миграцию не правят | создают новую |
| «разрушительные» изменения (переименование, удаление колонки) — по шагам | существующие данные и старый код должны работать в промежутке |
Где они лежат в FSD
Схема и клиент БД → shared/api/. Запросы сущности → entities/<x>/api/ (как entities/lesson/api/lesson-api.ts в проекте). Компоненты никогда не импортируют клиент БД напрямую.
Коротко
- ORM = схема на TS → типизированные запросы + миграции + защита от инъекций.
- Drizzle = близко к SQL, легко; Prisma = простой API, богатый инструментарий; чистый SQL = полный контроль.
- Миграции версионируются в Git и применяются по порядку; после применения их не правят.