Налаштування ORM Prisma для веб-застосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування ORM Prisma для веб-застосунку
Середня
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Налаштування 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 днів залежно від розміру кодової бази.