Prisma Cursor Rules: Type-Safe Database ORM
Cursor rules for Prisma covering schema modeling, migrations, relation patterns, typed Client API, middleware, seeding, and performance optimization.

Overview
Prisma is the modern TypeScript-first ORM that replaces raw SQL and query builders with a declarative schema and a fully typed client. These cursor rules enforce idiomatic schema design, relation modeling, migration hygiene, typed query patterns, middleware for cross-cutting concerns, and seeding workflows to help AI assistants generate clean, type-safe database layers.
Note:
Enforces declarative Prisma schema modeling, @relation conventions, migration workflows with prisma migrate, typed Client queries (findUnique, findMany with include/select), middleware patterns, and seed script structure.
Rules Configuration
---
description: Enforces Prisma best practices including schema design, relation modeling, migration hygiene, typed Client queries, middleware patterns, and database seeding. Provides guidelines for maintainable, type-safe ORM usage in TypeScript backends.
globs: **/schema.prisma,**/*.ts
---
# Prisma Best Practices
You are an expert in Prisma ORM, database schema design, and TypeScript backend development.
You understand relational data modeling, migration strategies, and query optimization with Prisma Client.
### Schema Design
- Use singular model names: model User, not model Users
- Map table names with @map("users") to follow database naming conventions
- Use camelCase for field names, map to snake_case with @map("created_at")
- Define @id with @default(autoincrement()) or @default(uuid()) for primary keys
- Always add @updatedAt on updatedAt fields
- Use appropriate field types: String, Int, Boolean, DateTime, Decimal, Json, Bytes
### Relations
- Explicitly name relations with @relation(name: "AuthorPosts") when multiple relations exist between the same models
- Define foreign keys explicitly: authorId Int, author User @relation(fields: [authorId], references: [id])
- Use onDelete: Cascade or Restrict explicitly; never rely on database defaults
- Prefer optional relations with ? where the relationship is not guaranteed
- Use @@index on foreign key fields for query performance
- Avoid circular relations; use @@index + manual queries for complex traversals
### Migrations
- Use prisma migrate dev --create-only to generate migration files
- Review generated SQL in migration files before applying
- Never edit migration.sql manually after it's been applied
- Use prisma migrate deploy in CI/CD, never prisma db push in production
- Run prisma migrate status to check for unapplied migrations before deploy
- Name migrations descriptively: add_user_avatar_field, create_post_tags_table
### Client Queries
- Import { PrismaClient } and instantiate once (singleton pattern, avoid new PrismaClient() per request)
- Use findUnique + where for primary-key lookups
- Use findMany with where, orderBy, take, skip for list queries
- Use include or select (never both on the same query) for relation loading
- Use create/update/upsert for mutations; use createMany for bulk inserts
- Wrap Prisma operations in try/catch; handle PrismaClientKnownRequestError for constraint violations
- Use interactive transactions (prisma.$transaction([...])) for multi-table operations
### Middleware & Extensions
- Use prisma.$use() middleware for soft deletes, audit logging, or field encryption
- Use Prisma Client extensions for reusable query patterns (e.g., paginated findMany)
- Add computed fields with result extensions if they depend on existing fields
### Seeding
- Use prisma/seed.ts with a main() function that upserts reference data
- Use upsert (not create) in seed scripts for idempotent re-runs
- Seed with TypeScript (not SQL) to leverage type checking
- Call prisma.$disconnect() in a finally block in seed scripts and standalone tools (not per-request with the singleton client)
Installation
Create prisma.mdc in your project's .cursor/rules/ directory and paste the configuration above. Cursor and Windsurf both read .cursor/rules/ — Copilot users place it in .github/copilot-instructions.md instead.
Examples
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
avatar String?
posts Post[]
comments Comment[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("users")
}
model Post {
id Int @id @default(autoincrement())
title String @db.VarChar(200)
body String @db.Text
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int @map("author_id")
comments Comment[]
tags PostTag[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([authorId])
@@map("posts")
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts PostTag[]
@@map("tags")
}
model PostTag {
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId Int @map("post_id")
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
tagId Int @map("tag_id")
@@id([postId, tagId])
@@map("post_tags")
}
model Comment {
id Int @id @default(autoincrement())
body String @db.Text
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId Int @map("post_id")
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int @map("author_id")
createdAt DateTime @default(now()) @map("created_at")
@@index([postId])
@@map("comments")
}
// lib/prisma.ts — Singleton Prisma Client
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma || new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'warn', 'error'] : ['error'],
});
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma;
}
// services/postService.ts — Typed queries with Prisma
import { prisma } from '../lib/prisma';
import { Prisma } from '@prisma/client';
export async function getPublishedPosts(page: number, perPage: number) {
const where: Prisma.PostWhereInput = { published: true };
const [posts, total] = await prisma.$transaction([
prisma.post.findMany({
where,
include: { author: { select: { id: true, name: true } }, tags: { include: { tag: true } } },
orderBy: { createdAt: 'desc' },
skip: (page - 1) * perPage,
take: perPage,
}),
prisma.post.count({ where }),
]);
return { posts, total };
}
export async function createPost(authorId: number, data: { title: string; body: string }) {
return prisma.post.create({
data: {
title: data.title,
body: data.body,
author: { connect: { id: authorId } },
},
include: { author: { select: { id: true, name: true } } },
});
}
// prisma/seed.ts — Idempotent database seeding
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
const user = await prisma.user.upsert({
where: { email: '[email protected]' },
update: {},
create: {
email: '[email protected]',
name: 'Demo User',
},
});
const post = await prisma.post.upsert({
where: { id: 1 },
update: {},
create: {
title: 'Getting Started with Prisma',
body: 'Prisma makes database access type-safe and intuitive.',
published: true,
authorId: user.id,
},
});
console.log({ user, post });
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Related Resources
Related Articles & Guides
#mongodbMongoDB Cursor Rules: NoSQL Document Database
Cursor rules for MongoDB covering connection management, CRUD operations, aggregation pipeline, indexes, schema design, Mongoose ODM, transactions, and Atlas deployment.
#reduxRedux Cursor Rules: State Management with Redux Toolkit
Cursor rules for Redux covering Redux Toolkit configureStore, createSlice patterns, Immer immutability, memoized selectors with createSelector, async thunks, RTK Query data fetching, typed hooks, and normalized state shapes.
#supabaseSupabase Cursor Rules: Open-Source Firebase Alternative
Cursor rules for Supabase covering Postgres database, Auth, Row Level Security, Realtime subscriptions, Edge Functions, Storage, supabase-js client, and local development with the CLI.