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: таблицы, связи и запросы объявляются на этом языке. Это тонкий типизированный слой поверх SQL. Типы определяются автоматически, исходный SQL остается типобезопасным, а архитектура подходит для кода, критичного к производительности. Поддерживаются PostgreSQL, MySQL, SQLite и SQL Server.
Prisma появился раньше, в 2018 году, и сформировал свою репутацию благодаря улучшению опыта разработчиков, повышению производительности и наличию полезных инструментов. Модель данных хранится в файле .prisma, написанном на языке схем Prisma (PSL), а команда prisma generate преобразует её в полностью типизированный клиент с API высокого уровня. Управление связями между объектами осуществляется автоматически, а экосистема Prisma более обширна и зрела, включая 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. Если вы хорошо разбираетесь в операциях объединения таблиц и условиях фильтрации, использование этого инструмента кажется естественным, и написанные запросы точно соответствуют тем, что выполняются в базе данных. В этом примере таблицы книг объединяются с таблицей авторов, а результат фильтруется и сортируется:
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.