Files
pos-system/.agent/rules/database-prisma.md

5.3 KiB

trigger
trigger
always_on

Prisma Database Patterns

When to Use This Skill

Use this skill when:

  • Setting up Prisma for a new service
  • Creating or modifying database schemas
  • Writing database migrations
  • Implementing repository patterns
  • Optimizing database queries
  • Setting up database connections
  • Implementing transactions
  • Working with Neon PostgreSQL

Core Concepts

Architecture

  • Repository pattern for data access
  • Prisma as ORM for type safety
  • Neon PostgreSQL as primary database
  • Connection pooling for performance
  • Transaction support for data consistency

Key Patterns

Prisma Setup

npm install @prisma/client prisma
npx prisma init

Schema Definition

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

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

Database Connection

import { PrismaClient } from '@prisma/client';

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

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

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

Repository Pattern

export class UserRepository {
  async findById(id: string) {
    return prisma.user.findUnique({ where: { id }, include: { profile: true } });
  }

  async findAll({ page = 1, limit = 10, search }: QueryOptions) {
    const where = search ? { OR: [
      { email: { contains: search, mode: 'insensitive' } },
      { name: { contains: search, mode: 'insensitive' } }
    ]} : {};

    const [data, total] = await Promise.all([
      prisma.user.findMany({ where, skip: (page - 1) * limit, take: limit }),
      prisma.user.count({ where })
    ]);
    return { data, total };
  }

  async create(data: CreateUserDto) {
    return prisma.user.create({ data });
  }
}

Transactions

await prisma.$transaction(async (tx) => {
  await tx.account.update({ where: { id: from }, data: { balance: { decrement: amount } } });
  await tx.account.update({ where: { id: to }, data: { balance: { increment: amount } } });
}, { maxWait: 5000, timeout: 10000 });

Best Practices

  • Schema Design: Use appropriate field types, add indexes for frequently queried fields
  • Performance: Use select to fetch only needed fields, implement pagination
  • Security: Never expose sensitive fields, use parameterized queries
  • Maintenance: Keep migrations small and focused, test before production

Common Mistakes

  1. N+1 Query Problem: Fetching related data in a loop

    // BAD: N+1 queries
    for (const user of users) { await prisma.post.findMany({ where: { authorId: user.id } }); }
    // GOOD: Include relations
    await prisma.user.findMany({ include: { posts: true } });
    
  2. No Indexes: Missing indexes on frequently queried columns

    // GOOD: Add index
    @@index([createdAt])
    
  3. Raw Queries Without Parameters: SQL injection risk

    // BAD: prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = '${email}'`);
    // GOOD: prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`;
    
  4. Not Using Transactions: Data inconsistency risk

    // GOOD: Use transaction for related operations
    await prisma.$transaction([update1, update2]);
    
  5. Exposing Internal IDs: Leaking database structure

    // GOOD: Use CUID or UUID
    model User { id String @id @default(cuid()) }
    

Quick Reference

Operation Command
Create migration npx prisma migrate dev --name <name>
Apply migrations npx prisma migrate deploy
Reset database npx prisma migrate reset
Generate client npx prisma generate
Open Studio npx prisma studio
Seed database npx prisma db seed

Common Query Patterns:

await prisma.user.findUnique({ where: { id } });           // Find unique
await prisma.user.findMany({ include: { posts: true } });  // With relations
await prisma.user.findMany({ select: { id: true } });      // Select fields
await prisma.user.findMany({ skip: 10, take: 10 });        // Pagination
await prisma.$transaction(async (tx) => { ... });          // Transaction

Schema Field Types:

Type PostgreSQL Description
String TEXT Variable-length string
Int INTEGER 32-bit integer
BigInt BIGINT 64-bit integer
Float DOUBLE PRECISION Floating point
Boolean BOOLEAN True/false
DateTime TIMESTAMP Date and time
Json JSONB JSON data

Resources