Привет, друзья!
В этой серии из 2 статей я хочу поделиться с вами своими заметками о Prisma.
Prisma — это современное (продвинутое) объектно-реляционное отображение (Object-Relational Mapping, ORM) для Node.js и TypeScript. Проще говоря, Prisma — это инструмент, позволяющий работать с реляционными (PostgreSQL, MySQL, SQL Server, SQLite) и нереляционной (MongoDB) базами данных с помощью JavaScript или TypeScript без использования SQL (хотя такая возможность имеется).
Содержание этой части
- Настройки
- Вложенные запросы
- Фильтры и операторы
- Фильтры для связанных записей
- Методы клиента
- Транзакции
Если вам это интересно, прошу под кат.
Клиент
Настройки
select
select определяет, какие поля включаются в возвращаемый объект.
const user = await prisma.user.findUnique({ where: { email }, select: { id: true, email: true, first_name: true, last_name: true, age: true } }) // or const usersWithPosts = await prisma.user.findMany({ select: { id: true, email: true, posts: { select: { id: true, title: true, content: true, author_id: true, created_at: true } } } }) // or const usersWithPostsAndComments = await prisma.user.findMany({ select: { id: true, email: true, posts: { include: { comments: true } } } })
include
include определяет, какие отношения (связанные записи) включаются в возвращаемый объект.
const userWithPostsAndComments = await prisma.user.findUnique({ where: { email }, include: { posts: true, comments: true } })
where
where определяет один или более фильтр (о фильтрах мы поговорим отдельно), применяемый к свойствам записи или связанных записей:
const admins = await prisma.user.findMany({ where: { email: { contains: 'admin' } } })
orderBy
orderBy определяет поля и порядок сортировки. Возможными значениями orderBy являются asc и desc.
const usersByPostCount = await prisma.user.findMany({ orderBy: { posts: { count: 'desc' } } })
distinct
distinct определяет поля, которые должны быть уникальными в возвращаемом объекте.
const distinctCities = await prisma.user.findMany({ select: { city: true, country: true }, distinct: ['city'] })
Вложенные запросы
create: { data } | [{ data1 }, { data2 }, ...{ dataN }]— добавляет новую связанную запись или набор записей в родительскую запись.createдоступен при создании (create) новой родительской записи или обновлении (update) существующей родительской записи
const user = await prisma.user.create({ data: { email, profile: { // вложенный запрос create: { first_name, last_name } } } })
createMany: [{ data1 }, { data2 }, ...{ dataN }]— добавляет набор новых связанных записей в родительскую запись.createManyдоступен при создании (create) новой родительской записи или обновлении (update) существующей родительской записи
const userWithPosts = await prisma.user.create({ data: { email, posts: { // ! createMany: { data: posts } } } })
update: { data } | [{ data1 }, { data2 }, ...{ dataN }]— обновляет одну или более связанных записей
const user = await prisma.user.update({ where: { email }, data: { profile: { // ! update: { age } } } })
updateMany: { data } | [{ data1 }, { data2 }, ...{ dataN }]— обновляет массив связанных записей. Поддерживается фильтрация
const result = await prisma.user.update({ where: { id }, data: { posts: { // ! updateMany: { where: { published: false }, data: { like_count: 0 } } } } })
upsert: { data } | [{ data1 }, { data2 }, ...{ dataN }]— обновляет существующую связанную запись или создает новую
const user = await prisma.user.update({ where: { email }, data: { profile: { // ! upsert: { create: { age }, update: { age } } } } })
delete: boolean | { data } | [{ data1 }, { data2 }, ...{ dataN }]— удаляет связанную запись. Родительская запись при этом не удаляется
const user = await prisma.user.update({ where: { email }, data: { profile: { delete: true } } })
deleteMany: { data } | [{ data1 }, { data2 }, ...{ dataN }]— удаляет связанные записи. Поддерживается фильтрация
const user = await prisma.user.update({ where: { id }, data: { age, posts: { // ! deleteMany: {} } } })
set: { data } | [{ data1 }, { data2 }, ...{ dataN }]— перезаписывает значение связанной записи
const userWithPosts = await prisma.user.update({ where: { email }, data: { posts: { // ! set: newPosts } } })
connect— подключает запись к существующей связанной записи по идентификатору или уникальному полю
const user = await prisma.post.create({ data: { title, content, author: { connect: { email } } } })
connectOrCreate— подключает запись к существующей связанной записи по идентификатору или уникальному полю либо создает связанную запись при отсутствии таковой;disconnect— отключает родительскую запись от связанной без удаления последней.disconnectдоступен только если отношение является опциональным.
Фильтры и операторы
Фильтры
equals— значение равняетсяn
const usersWithNameHarry = await prisma.user.findMany({ where: { name: { equals: 'Harry' } } }) // `equals` может быть опущено const usersWithNameHarry = await prisma.user.findMany({ where: { name: 'Harry' } })
not— значение не равняетсяn;in— значениеnсодержится в списке (массиве)
const usersWithNameAliceOrBob = await prisma.user.findMany({ where: { user_name: { // ! in: ['Alice', 'Bob'] } } })
notIn—nне содержится в списке;lt—nменьшеx
const notPopularPosts = await prisma.post.findMany({ where: { likeCount: { lt: 100 } } })
lte—nменьше или равноx;gt—nбольшеx;gte—nбольше или равноx;contains—nсодержитx
const admins = await prisma.user.findMany({ where: { email: { contains: 'admin' } } })
startsWith—nначинается сx
const usersWithNameStartsWithA = await prisma.user.findMany({ where: { user_name: { startsWith: 'A' } } })
endsWith—nзаканчиваетсяx.
Операторы
AND— все условия должны возвращатьtrue
const notPublishedPostsAboutTypeScript = await prisma.post.findMany({ where: { AND: [ { title: { contains: 'TypeScript' } }, { published: false } ] } })
Обратите внимание: оператор указывается до названия поля (снаружи поля), а фильтр после (внутри).
OR— хотя бы одно условие должно возвращатьtrue;NOT— все условия должны возвращатьfalse.
Фильтры для связанных записей
some— возвращает все связанные записи, соответствующие одному или более критерию фильтрации
const usersWithPostsAboutTypeScript = await prisma.user.findMany({ where: { posts: { some: { title: { contains: 'TypeScript' } } } } })
every— возвращает все связанные записи, соответствующие всем критериям;none— возвращает все связанные записи, не соответствующие ни одному критерию;is— возвращает все связанные записи, соответствующие критерию;notIs— возвращает все связанные записи, не соответствующие критерию.
Методы клиента
$disconnect— закрывает соединение с БД, которое было установлено после вызова метода$connect(данный метод чаще всего не требуется вызывать явно), и останавливает движок запросов (query engine)Prisma
import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() async function seedDb() { try { await prisma.model.create(data) } catch (e) { onError(e) } finally { // ! await prisma.$disconnect() } }
$use— добавляет посредника (middleware)
prisma.$use(async (params, next) => { console.log('Это посредник') // работаем с `params` return next(params) })
next— представляет «следующий уровень» в стеке посредников. Таким уровнем может быть следующий посредник или движок запросовPrisma;params— объект со следующими свойствами:
action— тип запроса, например,createилиfindMany;args— аргументы, переданные в запрос, например,whereилиdata;model— модель, например,UserилиPost;runInTransaction— возвращаетtrue, если запрос был запущен в контексте транзакции;
- методы
$queryRaw,$executeRawи$runCommandRawпредназначены для работы сSQL. Почитать о них можно здесь; $transaction— выполняет запросы в контексте транзакции (см. ниже).
Подробнее о клиенте можно почитать здесь.
Транзакции
Транзакция — это последовательность операций чтения/записи, которые обрабатываются как единое целое, т.е. либо все операции завершаются успешно, либо все операции отклоняются с ошибкой.
Prisma позволяет использовать транзакции тремя способами:
- вложенные запросы (см. выше): операции с родительскими и связанными записями выполняются в контексте одной транзакции
const newUserWithProfile = await prisma.user.create({ data: { email, profile: { // ! create: { first_name, last_name } } } })
- пакетированные/массовые (batch/bulk) транзакции: выполнение нескольких операций за один раз с помощью таких запросов, как
createMany,updateManyиdeleteMany
const removedUser = await prisma.user.delete({ where: { email } }) // ! await prisma.post.deleteMany({ where: { author_id: removedUser.id } })
- вызов метода
$transaction.
$transaction
Интерфейс $transaction может быть использован в двух формах:
$transaction([ query1, query2, ...queryN ])— принимает массив последовательно выполняемых запросов;$transaction(fn)— принимает функцию, которая может включать запросы и другой код.
Пример транзакции, возвращающей посты, в заголовке которых встречается слово TypeScript и общее количество постов:
const [postsAboutTypeScript, totalPostCount] = await prisma.$transaction([ prisma.post.findMany({ where: { title: { contains: 'TypeScript' } } }), prisma.post.count() ])
В $transaction допускается использование SQL:
const [userNames, updatedUser] = await prisma.$transaction([ prisma.$queryRaw`SELECT 'user_name' FROM users`, prisma.$executeRaw`UPDATE users SET user_name = 'Harry' WHERE id = 42` ])
Интерактивные транзакции
Интерактивные транзакции предоставляют разработчикам больший контроль над выполняемыми в контексте транзакции операциями. В данный момент они имеют статус экспериментальной возможности, которую можно включить следующим образом:
generator client { provider = "prisma-client-js" previewFeatures = ["interactiveTransactions"] }
Рассмотрим пример совершения платежа.
Предположим, что у Alice и Bob имеется по 100$ на счетах (account), и Alice хочет отправить Bob свои 100$.
import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() async function transfer(from, to, amount) { try { await prisma.$transaction(async (prisma) => { // 1. Уменьшаем баланс отправителя const sender = await prisma.account.update({ data: { balance: { decrement: amount } }, where: { email: from } }) // 2. Проверяем, что баланс отправителя после уменьшения >= 0 if (sender.balance < 0) { throw new Error(`${from} имеет недостаточно средств для отправки ${amount}`) } // 3. Увеличиваем баланс получателя const recipient = await prisma.account.update({ data: { balance: { increment: amount } }, where: { email: to } }) return recipient }) } catch(e) { // обрабатываем ошибку } } async function main() { // эта транзакция разрешится await transfer('alice@mail.com', 'bob@mail.com', 100) // а эта провалится await transfer('alice@mail.com', 'bob@mail.com', 100) } main().finally(() => { prisma.$disconnect() })
Подробнее о транзакциях можно почитать здесь.
Пожалуй, это все, что я хотел рассказать вам о Prisma.
Благодарю за внимание и happy coding!
ссылка на оригинал статьи https://habr.com/ru/company/timeweb/blog/654567/

Добавить комментарий