PrismaJuniorTechnical

В чём разница между findUnique, findFirst и findFirstOrThrow?

findUnique — только по уникальным ключам, возвращает T|null; findFirst — по любым полям с orderBy, возвращает T|null; findFirstOrThrow — то же, но бросает PrismaClientKnownRequestError (P2025) вместо null.

findUnique, findFirst и findFirstOrThrow

Три метода решают схожую задачу — получить одну запись — но отличаются гарантиями уникальности, поведением при отсутствии результата и генерируемым SQL.

findUnique

Принимает только уникальные поля: @id, поля с @unique или составные ключи @@unique. Генерирует WHERE ... LIMIT 1 с гарантией уникальности на уровне схемы. Если запись не найдена — возвращает null.

// Поиск по первичному ключу
const user = await prisma.user.findUnique({
  where: { id: 42 },
});
// user: User | null

// По полю с @unique
const byEmail = await prisma.user.findUnique({
  where: { email: 'alice@example.com' },
});

// По составному @@unique
const membership = await prisma.membership.findUnique({
  where: {
    userId_orgId: { userId: 1, orgId: 5 },
  },
});

Компилятор TypeScript запрещает передавать в where неуникальные поля — это отличает метод от остальных на уровне типов.

findFirst

Принимает произвольный where (любые поля), поддерживает orderBy, skip и cursor. Возвращает первую подходящую запись или null. Под капотом — SELECT ... LIMIT 1 без гарантии уникальности.

// Последний активный пост пользователя
const latestPost = await prisma.post.findFirst({
  where: {
    authorId: 42,
    published: true,
  },
  orderBy: { createdAt: 'desc' },
});
// latestPost: Post | null

// С пропуском нескольких записей
const secondPost = await prisma.post.findFirst({
  where: { authorId: 42 },
  orderBy: { createdAt: 'asc' },
  skip: 1,
});

findFirstOrThrow (и findUniqueOrThrow)

Идентичен findFirst по сигнатуре, но при отсутствии записи выбрасывает PrismaClientKnownRequestError с кодом P2025 вместо возврата null. Тип возврата — T (не T | null), что упрощает дальнейший код.

import { Prisma } from '@prisma/client';

try {
  const post = await prisma.post.findFirstOrThrow({
    where: { slug: 'my-post', published: true },
    orderBy: { createdAt: 'desc' },
  });
  // post: Post — гарантированно не null
  console.log(post.title);
} catch (e) {
  if (
    e instanceof Prisma.PrismaClientKnownRequestError &&
    e.code === 'P2025'
  ) {
    // 404 Not Found
  }
  throw e;
}

Аналогично работает findUniqueOrThrow — как findUnique, но бросает исключение.

Сравнительная таблица

  • findUnique — только уникальные ключи, возвращает T | null, ошибка компиляции при неуникальном поле.
  • findFirst — любой where + orderBy, возвращает T | null.
  • findFirstOrThrow — любой where + orderBy, возвращает T, бросает P2025 при отсутствии.

Подводные камни

  • findFirst без orderBy недетерминирован — при нескольких подходящих записях база вернёт любую; всегда указывайте orderBy, если порядок важен.
  • findUnique не принимает orderBy — если нужна сортировка, используйте findFirst; попытка добавить orderBy в findUnique даст ошибку типов.
  • P2025 может маскировать другие ошибки — перехватывайте только нужный код, иначе скроете, например, ошибки соединения с БД.
  • findFirst медленнее на больших таблицах без индекса — в отличие от findUnique, база не знает о гарантии уникальности и может не оптимизировать запрос; добавляйте индексы на поля в where.
  • findUniqueOrThrow vs findFirstOrThrow — путаница между ними приводит к тому, что неуникальные поля попадают в findUnique и ломают компиляцию.
  • Тип null в TypeScript — игнорирование | null у findFirst без проверки ведёт к runtime-ошибкам; используйте optional chaining или явную проверку.

Common mistakes

  • Путает Prisma Client API с гарантиями базы данных: индексы, блокировки и isolation level не создаются магически.
  • Не объясняет, где в lifecycle находится findUnique, findFirst и findFirstOrThrow.
  • Не разделяет validation, authorization, business logic и persistence.
  • Игнорирует ошибки, лимиты входных данных, observability и тестирование.

What the interviewer is testing

  • Может объяснить findUnique, findFirst и findFirstOrThrow на примере кода.
  • Называет ключевые API: findUnique(), findFirst(), findFirstOrThrow().
  • Отделяет ORM/query builder поведение от реального поведения СУБД.
  • Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.

Sources

Related topics