ORM: Prisma обзор

Prisma — современный ORM (Object-Relational Mapper) для Node.js и TypeScript, использующий декларативную схему schema.prisma для определения моделей и генерирующий типобезопасный клиент для работы с БД.

Зачем нужно

Prisma устраняет необходимость писать SQL вручную и получать нетипизированные объекты. Она генерирует TypeScript-типы из схемы, что даёт автодополнение и type-checking прямо в IDE. В отличие от Sequelize, Prisma имеет отдельный инструмент миграций (prisma migrate) и интроспекцию существующей БД.

Где используется

  • REST и GraphQL API на Node.js/TypeScript
  • Проекты с PostgreSQL, MySQL, SQLite, MongoDB
  • Команды, которым важна типобезопасность запросов
  • В связке с NestJS, Express, Fastify

Основной контент

Установка

npm install @prisma/client
npm install --save-dev prisma

npx prisma init  # создаёт prisma/schema.prisma и .env

Схема (prisma/schema.prisma)

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        Int      @id @default(autoincrement)
  email     String   @unique
  name      String?
  role      Role     @default(USER)
  posts     Post
  createdAt DateTime @default(now)
  updatedAt DateTime @updatedAt
}

model Post {
  id        Int      @id @default(autoincrement)
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
}

enum Role {
  USER
  ADMIN
}

Миграции

# Создать и применить миграцию
npx prisma migrate dev --name init

# Применить в production (без создания новых)
npx prisma migrate deploy

# Обновить Prisma Client после изменения схемы
npx prisma generate

# Открыть Prisma Studio (GUI для БД)
npx prisma studio

Prisma Client — CRUD

// db/prisma.js — singleton
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
module.exports = prisma;

// --- Использование ---
const prisma = require('./db/prisma');

// Create
const user = await prisma.user.create({
  data: { email: 'alice@example.com', name: 'Alice' }
});

// Read
const users = await prisma.user.findMany({
  where: { role: 'ADMIN' },
  orderBy: { createdAt: 'desc' },
  take: 10,
  skip: 20,
  select: { id: true, email: true, name: true } // проекция
});

const user = await prisma.user.findUnique({ where: { email: 'alice@example.com' } });
const user = await prisma.user.findFirst({ where: { name: { contains: 'ali' } } });

// Update
const updated = await prisma.user.update({
  where: { id: 1 },
  data: { name: 'Alice Updated' }
});

// Delete
await prisma.user.delete({ where: { id: 1 } });

// Upsert
await prisma.user.upsert({
  where: { email: 'bob@example.com' },
  create: { email: 'bob@example.com', name: 'Bob' },
  update: { name: 'Bob Updated' }
});

Связи и include

// Загрузить пользователя с его постами
const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: {
    posts: {
      where: { published: true },
      orderBy: { createdAt: 'desc' }
    }
  }
});

// Создать пост с привязкой к пользователю
await prisma.post.create({
  data: {
    title: 'Hello Prisma',
    author: { connect: { id: 1 } }
  }
});

Транзакции

// Атомарная операция
const [user, post] = await prisma.$transaction([
  prisma.user.create({ data: { email: 'x@x.com' } }),
  prisma.post.create({ data: { title: 'First', authorId: 1 } })
]);

// Интерактивная транзакция
await prisma.$transaction(async (tx) => {
  const sender = await tx.user.update({ where: { id: 1 }, data: { balance: { decrement: 100 } } });
  if (sender.balance < 0) throw new Error('Insufficient funds');
  await tx.user.update({ where: { id: 2 }, data: { balance: { increment: 100 } } });
});

Частые ошибки

  • Один PrismaClient на запрос — создавать singleton, иначе исчерпание соединений с БД
  • Не вызывать prisma.$disconnect — в скриптах и тестах нужно закрывать соединение явно
  • Забыть npx prisma generate — после изменения схемы клиент не обновится автоматически
  • findUnique может вернуть null — всегда проверять результат или использовать findUniqueOrThrow

Связанные темы

Ресурсы