EMZETT.
Войти

Drizzle ORM

Коротко: Библиотека TypeScript, позволяющая определять таблицы базы данных как код (вместо чистого SQL) и автоматически создавать/обновлять по ним соответствующую схему базы данных.

Подробнее: Таблицы объявляются в файле схемы (например, db/schema.ts) через pgTable(...). npx drizzle-kit push сравнивает это определение с реальной базой данных и приводит её в соответствие. drizzle.config.ts сообщает Drizzle, где находятся схема и база данных.

Наш контекст: Изменения схемы (например, новые столбцы для загрузок программ) сначала проверялись только локально через drizzle-kit push — что однажды привело к тому, что боевая база данных за ними не последовала, а страница администратора упала. С тех пор каждое изменение схемы сопровождается ещё и файлом SQL-миграции.

Подробности

push и generate/migrate

Drizzle различает два легко путаемых процесса: drizzle-kit push синхронизирует схему прямо и «вживую» с базой данных (быстро, но без прослеживаемой истории — удобно для локальной разработки и быстрого прототипирования), тогда как drizzle-kit generate создаёт настоящий версионируемый файл SQL-миграции, который вы коммитите и контролируемо выполняете в каждом окружении (staging, продакшн) через drizzle-kit migrate. Только второй подход оставляет в репозитории прослеживаемую историю всех изменений схемы — каждый файл миграции — самостоятельный файл .sql с порядковым номером, точно показывающий, какие операторы ALTER TABLE/CREATE TABLE и когда выполнялись. Это важно и с точки зрения соответствия требованиям/аудита: при push нет записи о том, КОГДА и как менялась схема, при generate+migrate она есть.

Определение схемы

Простая схема таблицы выглядит, например, так:

export const users = pgTable("users", {
  id: uuid("id").primaryKey().defaultRandom(),
  email: text("email").notNull().unique(),
  createdAt: timestamp("created_at").defaultNow(),
});
export const posts = pgTable("posts", {
  id: uuid("id").primaryKey().defaultRandom(),
  authorId: uuid("author_id").references(() => users.id, { onDelete: "cascade" }),
  title: text("title").notNull(),
});
// Связи для типобезопасных запросов JOIN (API реляционных запросов)
export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));

Конструкция relations() предназначена исключительно для вывода типов TypeScript и API реляционных запросов (db.query.posts.findMany({ with: { author: true } })) — она НЕ создаёт никаких ограничений внешнего ключа в самой базе данных; это делает только .references() в определении таблицы.

Отличие от Prisma и чистого SQL

Построитель запросов намеренно близок к настоящему SQL (в отличие от более сильно абстрагированных ORM вроде Prisma, использующей собственный язык запросов и отдельный шаг генерации кода) — db.select().from(users).where(eq(users.email, "...")) читается почти как сам SQL-запрос, что упрощает отладку, потому что легче проследить, какой SQL в итоге выполняется (Drizzle также предлагает .toSQL(), выводящий сгенерированный запрос вместе с параметрами). Prisma же поставляет более зрелый визуальный интерфейс администрирования Prisma Studio, тогда как аналог Drizzle (Drizzle Studio) проще. Практическая разница в повседневной работе: у Drizzle нет собственного шага генерации кода при сборке (схема уже ЯВЛЯЕТСЯ обычным кодом TypeScript), тогда как Prisma после каждого изменения схемы требует prisma generate, чтобы пересоздать типобезопасный клиент — это обычно делает цикл разработки с Drizzle быстрее при частых изменениях схемы.

Типичные подводные камни

Частая ошибка — использовать push в продакшне, хотя он предназначен в первую очередь для локальной разработки: без истории миграций есть риск, что при более сложных изменениях (например, переименовании столбца) push истолкует изменение как «удалить столбец + создать новый» и потеряет существующие данные в этом столбце вместо переименования. drizzle-kit generate распознаёт такие переименования интерактивно и прямо спрашивает, это переименование или настоящее удаление+создание.

См. также: Neon