PrismaJuniorTechnical

Как Prisma Client обеспечивает типобезопасность? Как генерируются типы?

Prisma генерирует TypeScript-типы из schema.prisma командой npx prisma generate. Типы аргументов (UserCreateInput) и результатов автоматически учитывают select/include, обеспечивая полную статическую типобезопасность.

Типобезопасность Prisma Client

Prisma генерирует полностью типизированный клиент на основе вашей схемы (schema.prisma). Генерация запускается командой npx prisma generate и создаёт TypeScript-типы в node_modules/@prisma/client (или в кастомном output-пути).

Как работает генерация типов

Prisma CLI читает prisma/schema.prisma и генерирует:

  • Интерфейсы моделей (User, Post, …)
  • Типы аргументов для каждого метода (UserCreateInput, UserWhereUniqueInput, …)
  • Типы результатов с учётом select и include
  • Enum-типы, соответствующие enum в схеме

Пример схемы и сгенерированных типов

// prisma/schema.prisma
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id       Int    @id @default(autoincrement())
  title    String
  author   User   @relation(fields: [authorId], references: [id])
  authorId Int
}
import { PrismaClient, Prisma } from '@prisma/client';

const prisma = new PrismaClient();

// TypeScript знает, что result: User
const user = await prisma.user.findUnique({
  where: { email: 'alice@example.com' },
});
// user: { id: number; email: string; name: string | null } | null

// select сужает тип до выбранных полей
const userWithName = await prisma.user.findUnique({
  where: { id: 1 },
  select: { id: true, name: true },
});
// userWithName: { id: number; name: string | null } | null

// include добавляет связанные данные к типу
const userWithPosts = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true },
});
// userWithPosts: (User & { posts: Post[] }) | null

Утилитарные типы Prisma

// Тип для создания пользователя
type CreateUserInput = Prisma.UserCreateInput;
// { email: string; name?: string | null; posts?: PostCreateNestedManyWithoutAuthorInput }

// Получить тип результата произвольного запроса
const userWithPostsQuery = Prisma.validator<Prisma.UserDefaultArgs>()({
  include: { posts: true },
});
type UserWithPosts = Prisma.UserGetPayload<typeof userWithPostsQuery>;
// Полный тип: User & { posts: Post[] }

// Для функций, принимающих частичные данные
async function updateUser(id: number, data: Prisma.UserUpdateInput) {
  return prisma.user.update({ where: { id }, data });
}

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

  • После любого изменения schema.prisma нужно вручную запустить npx prisma generate — типы не обновляются автоматически.
  • В CI/CD шаг prisma generate должен идти до tsc, иначе типы будут устаревшими.
  • Если используется кастомный output в генераторе, импортировать нужно из этого пути, а не из @prisma/client.
  • select: { id: true } исключает все остальные поля из типа — обращение к result.email будет ошибкой компиляции.
  • Тип поля с ? в схеме (name String?) будет string | null, а не string | undefined.
  • Enum в Prisma генерирует объект-константу и тип с одинаковым именем — не путайте импорт типа и значения.
  • Prisma не генерирует типы для сырых SQL-запросов ($queryRaw) — нужно приводить вручную через as.

Common mistakes

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

What the interviewer is testing

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

Sources

Related topics