Главная / Статьи

Drizzle против Prisma: как выбрать ORM для TypeScript без излишнего ажиотажа

Сравните Drizzle и Prisma с точки зрения проектирования схем, стиля запросов, вложенных операций записи, миграций, транзакций и производительности, а также получите рекомендации относительно того, какой инструмент лучше подойдет для каждой команды.

Издатель: REACTAPP.TOP7 мин чтения1588 слов

Источники: 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.
  • Производительность: более эффективна и легче контролируема по сравнению с незначительными дополнительными затратами ради большего удобства.