--- 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 ```bash npm install @prisma/client prisma npx prisma init ``` ### Schema Definition ```prisma 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript // 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 ```prisma // GOOD: Add index @@index([createdAt]) ``` 3. **Raw Queries Without Parameters**: SQL injection risk ```typescript // 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 ```typescript // GOOD: Use transaction for related operations await prisma.$transaction([update1, update2]); ``` 5. **Exposing Internal IDs**: Leaking database structure ```prisma // GOOD: Use CUID or UUID model User { id String @id @default(cuid()) } ``` ## Quick Reference | Operation | Command | |-----------|---------| | **Create migration** | `npx prisma migrate dev --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:** ```typescript 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 - [Prisma Documentation](https://www.prisma.io/docs) - Official Prisma docs - [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference) - [Detailed Code Examples](./references/REFERENCE.md) - [Repository Pattern](../repository-pattern/SKILL.md) - Data access patterns - [Caching Patterns](../caching-patterns/SKILL.md) - Query caching - [Testing Patterns](../testing-patterns/SKILL.md) - Database mocking