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({ Также в библиотеке
Eigen для C++: полное руководство с примерами
Геолокация и карты в Swift для iOS/macOS: полное руководство
Работа с JWT токенами в TypeScript: библиотека jsonwebtoken и полное руководство
CLI фреймворк для JavaScript (Node.js): Commander.js – создание консольных приложений