Настройка ORM Prisma для веб-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка ORM Prisma для веб-приложения
Средняя
~1 рабочий день
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Настройка ORM Prisma для веб-приложения

Prisma — ORM для Node.js и TypeScript с генерацией типов из схемы базы данных. Запросы типизированы автоматически: автодополнение для полей, ошибка компиляции при обращении к несуществующей колонке, корректные типы возврата. Это меняет скорость разработки ощутимо.

Установка

npm install prisma @prisma/client
npx prisma init --datasource-provider postgresql

Схема

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

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

model User {
  id        String    @id @default(cuid())
  email     String    @unique
  name      String
  role      Role      @default(USER)
  posts     Post[]
  profile   Profile?
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt

  @@index([email])
  @@map("users")
}

model Profile {
  id        String  @id @default(cuid())
  bio       String? @db.Text
  avatarUrl String?
  userId    String  @unique
  user      User    @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@map("profiles")
}

model Post {
  id          String    @id @default(cuid())
  title       String
  content     String?   @db.Text
  published   Boolean   @default(false)
  authorId    String
  author      User      @relation(fields: [authorId], references: [id])
  tags        Tag[]     @relation("PostToTag")
  viewCount   Int       @default(0)
  publishedAt DateTime?
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt

  @@index([authorId])
  @@index([published, createdAt(sort: Desc)])
  @@map("posts")
}

model Tag {
  id    String @id @default(cuid())
  name  String @unique
  slug  String @unique
  posts Post[] @relation("PostToTag")

  @@map("tags")
}

enum Role {
  USER
  MODERATOR
  ADMIN
}

Миграции

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

# Production
npx prisma migrate deploy

# Сбросить базу в dev
npx prisma migrate reset

# Посмотреть статус
npx prisma migrate status

Инициализация клиента

Singleton паттерн для Next.js / hot reload:

// lib/prisma.ts
import { PrismaClient } from '@prisma/client'

const globalForPrisma = global as unknown as { prisma: PrismaClient }

export const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    log: process.env.NODE_ENV === 'development'
      ? [{ emit: 'event', level: 'query' }, 'warn', 'error']
      : ['warn', 'error'],
    errorFormat: 'minimal',
  })

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma

  // Логирование запросов в dev
  prisma.$on('query', (e) => {
    console.log(`Query: ${e.query} (${e.duration}ms)`)
  })
}

Паттерны запросов

// Создание с вложенными данными
const user = await prisma.user.create({
  data: {
    email: '[email protected]',
    name: 'Alice',
    profile: {
      create: { bio: 'Frontend developer' }
    }
  },
  include: { profile: true }
})

// Пагинация с курсором (эффективнее offset для больших таблиц)
async function getPosts(cursor?: string, limit = 20) {
  const posts = await prisma.post.findMany({
    take: limit + 1,
    ...(cursor && { cursor: { id: cursor }, skip: 1 }),
    where: { published: true },
    orderBy: { createdAt: 'desc' },
    select: {
      id: true,
      title: true,
      createdAt: true,
      author: { select: { id: true, name: true } }
    }
  })

  const hasMore = posts.length > limit
  return {
    posts: hasMore ? posts.slice(0, -1) : posts,
    nextCursor: hasMore ? posts[limit - 1].id : null
  }
}

// Транзакция
async function publishPost(postId: string, authorId: string) {
  return prisma.$transaction(async (tx) => {
    const post = await tx.post.findUniqueOrThrow({
      where: { id: postId, authorId }
    })

    if (post.published) throw new Error('already published')

    return tx.post.update({
      where: { id: postId },
      data: {
        published: true,
        publishedAt: new Date()
      }
    })
  })
}

// Upsert
const tag = await prisma.tag.upsert({
  where: { slug },
  update: { name },
  create: { name, slug }
})

// Raw SQL когда Prisma не справляется
const result = await prisma.$queryRaw<{ count: bigint }[]>`
  SELECT count(*) FROM posts
  WHERE published = true
  AND created_at > ${new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)}
`

Middleware для soft delete

prisma.$use(async (params, next) => {
  // Soft delete
  if (params.action === 'delete' && params.model === 'Post') {
    params.action = 'update'
    params.args.data = { deletedAt: new Date() }
  }

  // Фильтрация удалённых
  if (['findFirst', 'findMany', 'findUnique'].includes(params.action) && params.model === 'Post') {
    params.args.where = { ...params.args.where, deletedAt: null }
  }

  return next(params)
})

Seed данные

// prisma/seed.ts
import { prisma } from '../lib/prisma'

async function main() {
  await prisma.user.upsert({
    where: { email: '[email protected]' },
    update: {},
    create: {
      email: '[email protected]',
      name: 'Admin',
      role: 'ADMIN',
    }
  })

  const tags = ['typescript', 'nodejs', 'prisma', 'postgresql']
  for (const name of tags) {
    await prisma.tag.upsert({
      where: { slug: name },
      update: {},
      create: { name, slug: name }
    })
  }
}

main()
  .then(() => console.log('Seed complete'))
  .catch(console.error)
  .finally(() => prisma.$disconnect())
// package.json
{
  "prisma": {
    "seed": "ts-node prisma/seed.ts"
  }
}

Сроки

Настройка Prisma с нуля (схема, миграции, seed, типизированный клиент): 1 день. Интеграция в существующий проект с написанием репозиториев и тестов: 2–3 дня. Миграция с сырого SQL или другого ORM на Prisma: 3–5 дней в зависимости от размера кодовой базы.