Drizzle проти Prisma: вибір ORM для TypeScript без всього цього ажіотажу
Порівняйте Drizzle та Prisma за ознаками проектування схеми, стилю запитів, вкладених записів, міграцій, транзакцій та продуктивності, з рекомендаціями щодо того, який інструмент підходить для кожної команди.
Джерела: Drizzle vs Prisma: Choosing the Right TypeScript ORM in 2026 (Deep Dive)
Drizzle ORM та Prisma — це два найпоширеніші варіанти при виборі ORM для проекту на TypeScript. Вони вирішують одну й ту саму проблему — перетворення доступу до бази даних на типізований код, але ґрунтуються на протилежних підходах щодо того, наскільки близько слід залишатися до SQL. Жоден з них не є неправильним вибором. Якщо ваша команда вже добре знає один із цих інструментів, продовжуйте ним користуватися та присвятіть час розробці продукту. Порівняння стає важливішим, коли ви починаєте щось нове або мусите пояснити існуючу конфігурацію колегам.
Два інструменти
Drizzle з’явився у 2022 році та швидко набув популярності серед розробників, які прагнуть контролю без зайвої ваги. Це інструмент, орієнтований на TypeScript: таблиці, зв’язки та запити оголошуються у TypeScript. Це можна описати як тонкий, типізований шар над SQL. Типи визначаються автоматично, сирі SQL-запити залишаються безпечними з точки зору типів, а архітектура підходить для коду, чутливого до продуктивності. Підтримуються PostgreSQL, MySQL, SQLite та SQL Server.
Prisma з’явився раніше, у 2018 році, і сформував свою репутацію завдяки досвіду розробників, продуктивності та інструментам. Модель даних зберігається у файлі .prisma, написаному мовою схем Prisma (PSL), а команда prisma generate перетворює її на повністю типізований клієнт із API високого рівня. Взаємозв’язки між елементами обробляються автоматично, а екосистема є більшою та більш розвиненою, включаючи Prisma Studio — візуальний браузер та редактор для роботи з даними. Підтримуються такі бази даних, як PostgreSQL, MySQL, SQLite та MongoDB, серед інших.
Визначення схеми
Саме тут філософії найбільш помітно відрізняються, і цей вибір впливає на щоденну роботу та безпеку типів.
Drizzle: код на першому місці
Схема Drizzle — це звичайний TypeScript. Таблиці, стовпці та взаємозв’язки знаходяться в тій самій мові, що й додаток, тому немає кроку генерації, а зміни відображаються у редакторі миттєво.
import { pgTable, serial, varchar, text, integer, timestamp } from "drizzle-orm/pg-core";
// Authors table
export const authors = pgTable("authors", {
id: serial("id").primaryKey(),
name: varchar("name", { length: 100 }).notNull(),
bio: text("bio"),
});
// Books table referencing Authors
export const books = pgTable("books", {
id: serial("id").primaryKey(),
title: varchar("title", { length: 150 }).notNull(),
summary: text("summary"),
authorId: integer("author_id")
.notNull()
.references(() => authors.id),
publishedAt: timestamp("published_at").defaultNow(),
});
Оскільки типи визначаються автоматично, результат запиту має відому структуру без необхідності додаткових файлів схеми. Це зменшує несподіванки під час виконання.
Prisma: схема спочатку
Prisma зберігає модель у окремому файлі .prisma:
model Author {
id Int @id @default(autoincrement())
name String
bio String?
books Book[]
}
model Book {
id Int @id @default(autoincrement())
title String
summary String?
publishedAt DateTime @default(now())
authorId Int
author Author @relation(fields: [authorId], references: [id])
}
Схема чітко відокремлена від коду додатку. Після кожної зміни команда prisma generate перебудовує клієнт, щоб типи збігалися. Для тих, хто має обмежені знання SQL, така абстракція є зручнішою, оскільки працюється з генерованим клієнтом високого рівня, а не з логікою у стилі SQL. Недоліком є наявність ще одного шару між кодом та базою даних.
Складання запитів
Обидва підходи забезпечують запити з перевіркою типів, але їх складання відрізняється.
Drizzle
Інструмент для складання запитів навмисно залишається простим та передбачуваним, і його синтаксис схожий на SQL. Якщо ви знайомі з операціями об’єднання та умовами WHERE, цей підхід здається природним, а написане вами точно відповідає тому, що виконується в базі даних. У цьому прикладі книги об’єднуються з авторами, а результат фільтрується та сортується:
const results = await db
.select({
bookId: books.id,
title: books.title,
publishedAt: books.publishedAt,
authorName: authors.name,
})
.from(books)
.leftJoin(authors, eq(books.authorId, authors.id))
.where(eq(authors.id, authorId))
.orderBy(books.publishedAt);
Ви отримуєте детальний контроль та високо прогнозований SQL. Значна різниця порівняно з Prisma проявляється під час створення пов’язаних рядків. Drizzle не підтримує вкладені записи, тому кожен вставний операція у відношення обробляється вручну, зазвичай у межах транзакції, щоб усі елементи залишалися послідовними:
const result = await db.transaction(async (tx) => {
const [newAuthor] = await tx.insert(authors).values({
name: "Alice",
bio: "Fantasy author",
}).returning({ id: authors.id });
const [newBook] = await tx.insert(books).values({
title: "The Dream Forest",
authorId: newAuthor.id,
}).returning();
return { newAuthor, newBook };
});
Повна прозорість — це перевага. Більше шаблонного коду — це недолік.
Prisma
Prisma обирає протилежний підхід. Ви викликаєте методи у автоматично створеному клієнті, а база даних залишається через методи у стилі об’єктів. Те саме пошукове запитування стає:
const results = await prisma.book.findMany({
where: { authorId },
select: {
id: true,
title: true,
publishedAt: true,
author: {
select: { name: true },
},
},
orderBy: { publishedAt: "asc" },
});
Тут нікому не потрібно думати мовою SQL, що пояснює велику популярність Prisma. Він також чудово підходить для створення пов’язаних даних, оскільки підтримується вкладене записування. Книгу та її автора можна створити за один виклик:
const newBook = await prisma.book.create({
data: {
title: "The Dream Forest",
summary: "A surreal adventure.",
author: {
create: {
name: "Alice",
bio: "Fantasy author",
},
},
},
});
Не потрібно вручну налаштовувати жодні іноземні ключі. Ви жертвуєте можливістю бачити, що відбувається, адже SQL-запити, які насправді виконуються, залишаються прихованими, тож у вас менше контролю, ніж у Drizzle.
Міграції
Зміна схеми з часом має велике значення в будь-якому реальному проекті, і обидві інструменти ефективно справляються з цим.
Drizzle
Оскільки схема написана на TypeScript, вона є джерелом істини, а CLI (drizzle-kit) генерує міграції на основі відмінностей у ваших визначеннях. У робочому процесі, заснованому на міграціях, спочатку генерується:
npx drizzle-kit generate
Це порівнює поточний стан із вашою схемою TypeScript та створює файл міграції зі змінами SQL, який ви можете вручну відредагувати перед його використанням. Застосування цих змін — це окрема команда:
npx drizzle-kit migrate
Для швидкого створення прототипів також існує скорочений шлях:
npx drizzle-kit push
Він передає схему TypeScript безпосередньо до бази даних, не створюючи файлу міграції. Це швидко та зручно на початковому етапі, але через відсутність історії міграцій його не слід використовувати в продакшені.
Prisma
З Prisma файл .prisma є джерелом істини, а інструмент генерує SQL-запити для кожної зміни. Для застосування затверджених міграцій до бази даних використовується:
npx prisma migrate deploy
Під час розробки команда prisma migrate dev створює нову папку з міграціями з SQL-скриптами, застосовує їх та оновлює таблицю історії. Це забезпечує організований, переважно автоматичний процес роботи.
Тут також існує скорочення для створення прототипів:
npx prisma db push
Він оновлює базу даних безпосередньо, як у Drizzle через push-механізм, без файлів міграції, тому підходить лише для непродакшнового використання.
Транзакції
Обидва інструменти підтримують транзакції, але з деякими незначними відмінностями у підході.
Drizzle використовує db.transaction із функцією повернення. Кожен запит у межах цього блоку або успішно виконується, або провалюється разом, і оскільки ви працюєте зі SQL, ви можете точно контролювати те, що відбувається всередині блоку:
await db.transaction(async (tx) => {
const newAuthor = await tx.insert(authors).values({
name: "Alice Walker",
bio: "Pulitzer Prize-winning author",
}).returning();
await tx.insert(books).values({
title: "Journey to the Mountains",
summary: "A story about adventure and discovery.",
authorId: newAuthor[0].id,
});
});
Prisma пропонує prisma.$transaction, який приймає або масив запитів, або асинхронну функцію-колбек. Здійснення та скасування змін відбуваються автоматично:
await prisma.$transaction(async (tx) => {
const newAuthor = await tx.author.create({
data: {
name: "Alice Walker",
bio: "Pulitzer Prize-winning author",
},
});
await tx.book.create({
data: {
title: "Journey to the Mountains",
summary: "A story about adventure and discovery.",
authorId: newAuthor.id,
},
});
});
Продуктивність
Саме тут найбільше важливі основні філософії.
Drizzle є легким у використанні, а його запити майже безпосередньо відповідають операціям бази даних, тому витрати на абстракцію мінімальні. Це корисно для складних запитів, масових операцій та коду з високою пропускною здатністю. З повним контролем над об’єднаннями, транзакціями та формою запитів налаштування для прискорення є простішим, але це передбачає розуміння SQL та проектування баз даних.
Генерований клієнт Prisma приховує SQL за API високого рівня. Це трохи впливає на продуктивність, хоча у більшості звичайних додатків різниця є незначною. Відношення, вкладені запити та типовані результати надаються безкоштовно, що прискорює розробку. Однак ця зручність іноді призводить до SQL, який є менш оптимізованим, ніж ретельно написаний вручну запит.
Вибір між ними
Обидва це сучасні ORM, сумісні з TypeScript, які забезпечують безпеку типів, підтримку міграцій, відношень та транзакцій, тож вибір залежить від пріоритетів.
Оберіть Drizzle, якщо ви хочете залишатися ближче до SQL, контролювати запити та міграції та потребуєте прогнозованої продуктивності при великих чи складних навантаженнях. TypeScript є єдиним джерелом істини, і нічого не залишається прихованим.
Оберіть Prisma, якщо команда хоче працювати швидко, зосередитися на функціях та використовувати автоматизацію для міграцій, запитів та відношень. Модель, заснована на схемі, та генерований клієнт забезпечують чистий, високорівневий API, зручний для початківців.
Коротко кажучи:
- Схема: код на TypeScript (Drizzle) проти файлу
.prismaта його генерації (Prisma). - Запити: схожі на SQL та експліцитні проти об’єктного стилю та абстрактних.
- Вкладені записи: вручну в Drizzle, вбудовано в Prisma.