mcpField
P
prisma-patterns

prisma-patterns

github.com/ashchupliakData & databases575 installsUpdated 47w ago
Repository

About prisma-patterns

prisma-patterns is a reference skill for common Prisma ORM patterns in application development. It covers schema and client setup alongside queries, transactions, aggregations, raw SQL, and migrations.

Key features

  1. 01Prisma schema examples
  2. 02Client initialization pattern
  3. 03CRUD and relation queries
  4. 04Filtering and pagination
  5. 05Transactions and aggregations
  6. 06Raw queries and migrations

Use cases

  1. 01Set up Prisma Client in an application
  2. 02Query related records with filters
  3. 03Implement paginated data access
  4. 04Apply migrations and transactions

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/ashchupliak/dream-team --skill prisma-patterns

After installing, type/prisma-patternsin Claude Code to confirm it loaded.

Details

From the project README

Prisma ORM Patterns

Schema Definition

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

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

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  environments Environment[]
  sessions     Session[]

  @@map("users")
}

model Environment {
  id          String            @id @default(cuid())
  name        String
  description String?
  status      EnvironmentStatus @default(PENDING)
  ownerId     String            @map("owner_id")
  createdAt   DateTime          @default(now()) @map("created_at")
  updatedAt   DateTime          @updatedAt @map("updated_at")

  owner User  @relation(fields: [ownerId], references: [id], onDelete: Cascade)
  tags  Tag[]

  @@unique([ownerId, name])
  @@index([status])
  @@index([createdAt(sort: Desc)])
  @@map("environments")
}

model Tag {
  id            String      @id @default(cuid())
  key           String
  value         String
  environmentId String      @map("environment_id")
  createdAt     DateTime    @default(now()) @map("created_at")

  environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)

  @@unique([environmentId, key])
  @@map("tags")
}

enum EnvironmentStatus {
  PENDING
  RUNNING
  STOPPED
  FAILED
}

Prisma Client Setup

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

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined
}

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

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

Basic CRUD Operations

// Create
const environment = await prisma.environment.create({
  data: {
    name: 'dev-env',
    description: 'Development environment',
    ownerId: userId,
  },
})

// Read
const environment = await prisma.environment.findUnique({
  where: { id: envId },
})

const environments = await prisma.environment.findMany({
  where: { status: 'RUNNING' },
  orderBy: { createdAt: 'desc' },
  take: 10,
})

// Update
const updated = await prisma.environment.update({
  where: { id: envId },
  data: { status: 'STOPPED' },
})

// Delete
await prisma.environment.delete({
  where: { id: envId },
})

// Upsert
const env = await prisma.environment.upsert({
  where: { id: envId },
  update: { status: 'RUNNING' },
  create: {
    name: 'new-env',
    ownerId: userId,
  },
})

Relations

// Include relations
const envWithOwner = await prisma.environment.findUnique({
  where: { id: envId },
  include: {
    owner: true,
    tags: true,
  },
})

// Select specific fields
const envPartial = await prisma.environment.findUnique({
  where: { id: envId },
  select: {
    id: true,
    name: true,
    owner: {
      select: {
        name: true,
        email: true,
      },
    },
  },
})

// Nested create
const envWithTags = await prisma.environment.create({
  data: {
    name: 'tagged-env',
    ownerId: userId,
    tags: {
      create: [
        { key: 'team', value: 'platform' },
        { key: 'tier', value: 'production' },
      ],
    },
  },
  include: { tags: true },
})

// Connect existing relation
const env = await prisma.environment.create({
  data: {
    name: 'new-env',
    owner: {
      connect: { id: userId },
    },
  },
})

Filtering

// Complex filters
const environments = await prisma.environment.findMany({
  where: {
    AND: [
      { status: 'RUNNING' },
      {
        OR: [
          { name: { contains: 'prod', mode: 'insensitive' } },
          { tags: { some: { key: 'tier', value: 'production' } } },
        ],
      },
    ],
    createdAt: {
      gte: new Date('2024-01-01'),
    },
    owner: {
      email: { endsWith: '@jetbrains.com' },
    },
  },
})

// NOT filter
const nonFailedEnvs = await prisma.environment.findMany({
  where: {
    NOT: { status: 'FAILED' },
  },
})

Pagination

// Offset pagination
async function getEnvironmentsPage(page: number, pageSize: number) {
  const [items, total] = await Promise.all([
    prisma.environment.findMany({
      skip: (page - 1) * pageSize,
      take: pageSize,
      orderBy: { createdAt: 'desc' },
    }),
    prisma.environment.count(),
  ])

  return {
    items,
    total,
    page,
    pageSize,
    totalPages: Math.ceil(total / pageSize),
  }
}

// Cursor pagination
async function getEnvironmentsCursor(cursor?: string, take: number = 10) {
  const items = await prisma.environment.findMany({
    take: take + 1, // Fetch one extra to check if there's more
    ...(cursor && {
      cursor: { id: cursor },
      skip: 1, // Skip the cursor
    }),
    orderBy: { createdAt: 'desc' },
  })

  const hasMore = items.length > take
  const data = hasMore ? items.slice(0, -1) : items

  return {
    items: data,
    nextCursor: hasMore ? data[data.length - 1].id : null,
  }
}

Transactions

// Interactive transaction
const result = await prisma.$transaction(async (tx) => {
  // Create environment
  const env = await tx.environment.create({
    data: { name: 'new-env', ownerId: userId },
  })

  // Create associated tags
  await tx.tag.createMany({
    data: [
      { key: 'team', value: 'platform', environmentId: env.id },
      { key: 'cost-center', value: '12345', environmentId: env.id },
    ],
  })

  // Update user's environment count (if tracking)
  await tx.user.update({
    where: { id: userId },
    data: { environmentCount: { increment: 1 } },
  })

  return env
})

// Sequential transaction (batch)
const [deletedEnvs, deletedTags] = await prisma.$transaction([
  prisma.environment.deleteMany({ where: { status: 'FAILED' } }),
  prisma.tag.deleteMany({ where: { environment: { status: 'FAILED' } } }),
])

Aggregations

// Count by status
const statusCounts = await prisma.environment.groupBy({
  by: ['status'],
  _count: { status: true },
})

// Aggregate functions
const stats = await prisma.environment.aggregate({
  _count: { id: true },
  _min: { createdAt: true },
  _max: { createdAt: true },
})

Raw Queries (When Needed)

// Raw query
const result = await prisma.$queryRaw<Environment[]>`
  SELECT * FROM environments
  WHERE status = ${status}
  AND created_at > NOW() - INTERVAL '7 days'
`

// Raw execute
await prisma.$executeRaw`
  UPDATE environments
  SET status = 'STOPPED'
  WHERE status = 'RUNNING'
  AND updated_at < NOW() - INTERVAL '24 hours'
`

Migrations

# Create migration
npx prisma migrate dev --name add_environment_type

# Apply migrations (production)
npx prisma migrate deploy

# Reset database (development)
npx prisma migrate reset

# Generate client
npx prisma generate

Comments

Markdown supported