Prisma для JavaScript (Node.js): полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Prisma для JavaScript (Node.js): полное руководство с примерами

Prisma — это современный ORM (Object-Relational Mapping) для Node.js и TypeScript, который кардинально меняет подход к работе с базами данных. В отличие от традиционных ORM, Prisma предлагает декларативную модель данных, автоматическую генерацию типобезопасного клиента и мощный инструмент для миграций. Если вы устали от ручного написания SQL-запросов, путаницы с типами данных и сложных ORM-конфигураций, Prisma станет вашим спасением.

Зачем нужна Prisma? Во-первых, она обеспечивает типобезопасность на уровне базы данных. Это значит, что ошибки, связанные с неверными типами полей или отсутствующими связями, будут обнаружены на этапе компиляции, а не в рантайме. Во-вторых, Prisma предоставляет интуитивно понятный API для CRUD-операций, фильтрации, сортировки и агрегации данных. В-третьих, она автоматически генерирует документацию по вашей схеме данных и позволяет легко управлять изменениями схемы через миграции.

Prisma поддерживает основные реляционные базы данных: PostgreSQL, MySQL, SQLite, SQL Server, MongoDB (в режиме Document). Благодаря своей архитектуре, она идеально подходит как для небольших проектов, так и для enterprise-приложений. В этой статье мы подробно разберем установку, основные методы и реальные примеры использования Prisma в среде Node.js.

Установка

Установка Prisma состоит из двух этапов: установка CLI (Command Line Interface) и инициализация проекта. Выполните следующие команды в терминале вашего проекта:

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

После установки инициализируйте Prisma в проекте:

npx prisma init

Эта команда создаст директорию prisma с файлом schema.prisma и файл .env для переменных окружения. В файле .env укажите строку подключения к вашей базе данных. Например, для PostgreSQL:

DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

Для MySQL:

DATABASE_URL="mysql://user:password@localhost:3306/mydb"

Для SQLite (файловая база данных):

DATABASE_URL="file:./dev.db"

Быстрый старт: минимальный рабочий пример

Давайте создадим простую модель пользователя и выполним базовые операции. Отредактируйте файл prisma/schema.prisma:

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite" // или "postgresql", "mysql"
  url      = env("DATABASE_URL")
}

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

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

Теперь выполните миграцию для создания таблиц в базе данных:

npx prisma migrate dev --name init

После успешной миграции сгенерируйте Prisma Client:

npx prisma generate

Теперь создайте файл index.js и напишите простой скрипт:

// index.js
const { PrismaClient } = require('@prisma/client');

const prisma = new PrismaClient();

async function main() {
  // Создаем нового пользователя
  const user = await prisma.user.create({
    data: {
      email: 'alice@example.com',
      name: 'Alice',
    },
  });
  console.log('Created user:', user);

  // Создаем пост для пользователя
  const post = await prisma.post.create({
    data: {
      title: 'My first post',
      content: 'Hello, Prisma!',
      authorId: user.id,
    },
  });
  console.log('Created post:', post);

  // Получаем всех пользователей с их постами
  const allUsers = await prisma.user.findMany({
    include: { posts: true },
  });
  console.log('All users with posts:', JSON.stringify(allUsers, null, 2));
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Запустите скрипт:

node index.js

Вы увидите созданные записи в консоли. Поздравляю, вы только что использовали Prisma!

Основные методы Prisma Client

Prisma Client предоставляет богатый набор методов для работы с данными. Рассмотрим 8 ключевых методов с примерами.

1. create — создание записи

Сигнатура: model.create({ data })

Описание: Создает новую запись в таблице. Возвращает созданный объект.

const newUser = await prisma.user.create({
  data: {
    email: 'bob@example.com',
    name: 'Bob',
  },
});
console.log(newUser); // { id: 2, email: 'bob@example.com', name: 'Bob' }

2. findUnique — поиск по уникальному полю

Сигнатура: model.findUnique({ where: { uniqueField: value } })

Описание: Находит одну запись по уникальному полю (id, email и т.д.). Возвращает null, если запись не найдена.

const user = await prisma.user.findUnique({
  where: { email: 'alice@example.com' },
});
console.log(user); // { id: 1, email: 'alice@example.com', name: 'Alice' }

3. findFirst — поиск первой записи по условию

Сигнатура: model.findFirst({ where: { field: value } })

Описание: Находит первую запись, удовлетворяющую условию. Полезно для поиска по неуникальным полям.

const firstPost = await prisma.post.findFirst({
  where: { published: false },
});
console.log(firstPost); // { id: 1, title: 'My first post', ... }

4. findMany — поиск множества записей

Сигнатура: model.findMany({ where?, orderBy?, take?, skip?, include? })

Описание: Находит все записи, удовлетворяющие условиям. Поддерживает пагинацию, сортировку и включение связанных данных.

const publishedPosts = await prisma.post.findMany({
  where: { published: true },
  orderBy: { title: 'asc' },
  take: 10,
  include: { author: true },
});
console.log(publishedPosts); // Массив постов с авторами

5. update — обновление записи

Сигнатура: model.update({ where: { uniqueField: value }, data: { field: newValue } })

Описание: Обновляет существующую запись. Возвращает обновленный объект.

const updatedUser = await prisma.user.update({
  where: { id: 1 },
  data: { name: 'Alice Johnson' },
});
console.log(updatedUser); // { id: 1, email: 'alice@example.com', name: 'Alice Johnson' }

6. upsert — создание или обновление

Сигнатура: model.upsert({ where: { uniqueField: value }, update: { data }, create: { data } })

Описание: Если запись существует — обновляет её, если нет — создает новую. Атомарная операция.

const upsertedUser = await prisma.user.upsert({
  where: { email: 'charlie@example.com' },
  update: { name: 'Charlie Updated' },
  create: { email: 'charlie@example.com', name: 'Charlie New' },
});
console.log(upsertedUser); // Создаст или обновит пользователя

7. delete — удаление записи

Сигнатура: model.delete({ where: { uniqueField: value } })

Описание: Удаляет запись по уникальному полю. Возвращает удаленный объект.

const deletedUser = await prisma.user.delete({
  where: { id: 2 },
});
console.log(deletedUser); // { id: 2, email: 'bob@example.com', name: 'Bob' }

8. count — подсчет записей

Сигнатура: model.count({ where? })

Описание: Возвращает количество записей, удовлетворяющих условию.

const totalUsers = await prisma.user.count();
console.log(totalUsers); // 2

const publishedPostsCount = await prisma.post.count({
  where: { published: true },
});
console.log(publishedPostsCount); // 0

Реальный практический пример: блог с комментариями

Давайте создадим более сложный пример — систему блога с пользователями, постами и комментариями. Расширим нашу схему:

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

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

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  posts     Post[]
  comments  Comment[]
}

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

model Comment {
  id        Int      @id @default(autoincrement())
  text      String
  createdAt DateTime @default(now())
  post      Post     @relation(fields: [postId], references: [id])
  postId    Int
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
}

Теперь напишем скрипт, который демонстрирует полный цикл работы с данными:

// blog.js
const { PrismaClient } = require('@prisma/client');

const prisma = new PrismaClient();

async function main() {
  try {
    // 1. Создаем пользователей
    const alice = await prisma.user.create({
      data: {
        email: 'alice@blog.com',
        name: 'Alice',
      },
    });
    console.log('Created Alice:', alice.id);

    const bob = await prisma.user.create({
      data: {
        email: 'bob@blog.com',
        name: 'Bob',
      },
    });
    console.log('Created Bob:', bob.id);

    // 2. Создаем посты
    const post1 = await prisma.post.create({
      data: {
        title: 'Introduction to Prisma',
        content: 'Prisma is a modern ORM...',
        published: true,
        authorId: alice.id,
      },
    });
    console.log('Created post 1:', post1.id);

    const post2 = await prisma.post.create({
      data: {
        title: 'Advanced Prisma Techniques',
        content: 'Learn about transactions...',
        published: false,
        authorId: bob.id,
      },
    });
    console.log('Created post 2:', post2.id);

    // 3. Добавляем комментарии
    const comment1 = await prisma.comment.create({
      data: {
        text: 'Great article!',
        postId: post1.id,
        authorId: bob.id,
      },
    });
    console.log('Created comment 1:', comment1.id);

    const comment2 = await prisma.comment.create({
      data: {
        text: 'Thanks Bob!',
        postId: post1.id,
        authorId: alice.id,
      },
    });
    console.log('Created comment 2:', comment2.id);

    // 4. Получаем все опубликованные посты с авторами и комментариями
    const publishedPosts = await prisma.post.findMany({
      where: { published: true },
      include: {
        author: {
          select: { name: true, email: true },
        },
        comments: {
          include: {
            author: {
              select: { name: true },
            },
          },
          orderBy: { createdAt: 'desc' },
        },
      },
    });
    console.log('\nPublished posts:');
    console.log(JSON.stringify(publishedPosts, null, 2));

    // 5. Подсчитываем статистику
    const stats = await prisma.$transaction([
      prisma.user.count(),
      prisma.post.count(),
      prisma.comment.count(),
      prisma.post.count({ where: { published: true } }),
    ]);
    console.log('\nStatistics:');
    console.log(`Total users: ${stats[0]}`);
    console.log(`Total posts: ${stats[1]}`);
    console.log(`Total comments: ${stats[2]}`);
    console.log(`Published posts: ${stats[3]}`);

    // 6. Обновляем статус поста
    const updatedPost = await prisma.post.update({
      where: { id: post2.id },
      data: { published: true },
    });
    console.log('\nUpdated post 2 to published:', updatedPost.published);

    // 7. Удаляем комментарий
    await prisma.comment.delete({
      where: { id: comment2.id },
    });
    console.log('Deleted comment 2');

    // 8. Проверяем итоговое состояние
    const finalStats = await prisma.$transaction([
      prisma.comment.count(),
      prisma.post.count({                    

Рекомендаций