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