TL;DR: Prisma is a TypeScript ORM with three core tools: a Schema (defines your database structure), a Migrate tool (applies schema changes to the database), and a Client (auto-generated, type-safe code for querying). You define models in schema.prisma, run npx prisma migrate dev to update the database, and use prisma.user.findMany() to query. AI generates all of this — your job is to understand it.

Why AI Coders Need to Know This

Prisma is the most downloaded TypeScript ORM with over 6 million weekly npm downloads. In 2025, it became the default database layer for AI-generated Next.js, Remix, and Express TypeScript projects. When you ask Claude Code or Cursor to "add a database" to a TypeScript app, the response is overwhelmingly likely to include a prisma/schema.prisma file.

The problem: Prisma has a specific workflow that many vibe coders skip, and skipping steps causes confusing errors. The sequence is rigid:

  1. Edit schema.prisma
  2. Run npx prisma migrate dev (updates the database AND regenerates the client)
  3. Use prisma.model.method() in your code

Break that sequence — edit the schema but forget to migrate, or use a client method before generating — and you get errors that look unrelated to what you actually did wrong. Understanding Prisma's three components prevents hours of debugging.

The Three Prisma Components

1. Prisma Schema

The prisma/schema.prisma file is the single source of truth for your database structure. It has three sections:

// prisma/schema.prisma

// 1. Generator — what to generate (the JS/TS client)
generator client {
  provider = "prisma-client-js"
}

// 2. Datasource — which database and how to connect
datasource db {
  provider = "postgresql"  // or "mysql", "sqlite", "sqlserver"
  url      = env("DATABASE_URL")
}

// 3. Models — your data structure (becomes database tables)
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?  // ? means optional (nullable)
  role      Role     @default(USER)  // enum reference
  posts     Post[]   // relation: one user has many posts
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt  // auto-updates on every save
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
  createdAt DateTime @default(now())

  @@index([authorId])
}

enum Role {
  USER
  ADMIN
  MODERATOR
}

2. Prisma Migrate

Migrations are version-controlled SQL scripts that transform your database to match the schema. The key commands:

# Create a migration and apply it to the database
npx prisma migrate dev --name add_user_table

# Apply pending migrations in production (no new migrations created)
npx prisma migrate deploy

# Reset the database and apply all migrations from scratch
npx prisma migrate reset

# Sync schema to DB without migration files (development only)
npx prisma db push

# Pull existing database schema into Prisma schema
npx prisma db pull

# Open Prisma Studio (visual database browser)
npx prisma studio

When you run migrate dev, Prisma compares your current schema to the last migration, generates SQL to make the database match, and runs it. It also runs prisma generate automatically so your client is always in sync.

3. Prisma Client

The Client is auto-generated TypeScript code that gives you methods for every model. After migration, your editor autocompletes every query:

import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

// findMany — get multiple records
const users = await prisma.user.findMany({
  where: { role: 'ADMIN' },
  orderBy: { createdAt: 'desc' },
  take: 10,  // LIMIT 10
  skip: 0    // OFFSET 0
});

// findUnique — get exactly one by unique field
const user = await prisma.user.findUnique({
  where: { email: 'chuck@example.com' },
  include: { posts: true }  // JOIN with posts
});

// create — insert a new record
const newUser = await prisma.user.create({
  data: {
    name: 'Chuck',
    email: 'chuck@example.com',
    posts: {
      create: [{ title: 'My First Post' }]  // Nested create
    }
  }
});

// update — modify an existing record
const updated = await prisma.user.update({
  where: { id: 1 },
  data: { name: 'Charles' }
});

// delete — remove a record
await prisma.user.delete({ where: { id: 1 } });

Real Scenario

Prompt I Would Type

Set up Prisma with PostgreSQL for a blog app:
- User model with name, email (unique), role (USER/ADMIN), createdAt
- Post model with title, content, published, authorId (relates to User)
- Show me the full setup: schema, .env, migration command, and singleton client
- Give me example queries for creating a user with a post, and fetching all
  published posts with author names

The Singleton Pattern (Critical for Next.js)

This is the most important Prisma pattern for AI-generated Next.js apps. Without it, you will hit "too many connections" errors:

// lib/prisma.ts — use this everywhere instead of new PrismaClient()
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;
}

Why this matters: Next.js hot reloads during development. Each reload would create a new PrismaClient instance, opening a new database connection. With the singleton pattern, the client is stored on globalThis and reused across reloads.

What AI Gets Wrong About Prisma

Forgetting to run migrate after schema changes

The most common Prisma workflow error. AI edits schema.prisma but does not remind you to run npx prisma migrate dev. Your code uses prisma.newModel.findMany() but the table does not exist yet. Error: "table does not exist" or "unknown field."

Rule: Schema change → always run npx prisma migrate dev.

Creating PrismaClient in API routes

AI generates const prisma = new PrismaClient() inside API route files. This creates a new connection on every request. Use the singleton pattern from a shared lib/prisma.ts file instead.

Migration drift in production

AI uses prisma db push in examples because it is faster to type. If you use db push in development but need migrate deploy in production, the migration history is incomplete and production deploys fail. Establish migrate dev early and stick with it.

Missing environment variable

AI generates schema and queries but the app fails with "Environment variable not found: DATABASE_URL." Make sure your .env file exists at the project root (not inside prisma/) and contains:

DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

Cascading delete issues

If a Post belongs to a User, deleting the User fails if posts still exist (foreign key constraint). AI does not always add cascade behavior. Fix with:

model Post {
  author   User  @relation(fields: [authorId], references: [id], onDelete: Cascade)
  authorId Int
}

The Prisma Checklist

When something breaks: (1) Did you run prisma migrate dev after changing the schema? (2) Is DATABASE_URL set in .env? (3) Are you using the singleton client pattern? Those three checks cover 80% of Prisma errors.

Common Prisma Errors and Fixes

  • P2002 Unique constraint failed: You are trying to insert a duplicate value in a unique field (like email). Check for existing records first.
  • P2025 Record not found: findUnique or update where clause matched nothing. The record does not exist.
  • P1001 Cannot reach database: The database server is not running or the connection string is wrong. Check your DATABASE_URL and database status.
  • P3006 Migration failed: The SQL migration had an error (often trying to add NOT NULL column to table with data). Read the migration file and fix the SQL.
  • Type errors on prisma.model: You changed the schema but did not regenerate the client. Run npx prisma generate.

What to Learn Next

Next Step

Run npx prisma studio in any Prisma project to open the visual database browser. It shows your tables, data, and lets you run queries without writing code. It is the fastest way to verify that your migrations ran correctly and your queries are returning what you expect.

FAQ

Prisma is an open-source TypeScript ORM that gives you a Schema for defining database structure, a Migrate tool for applying changes, and a generated Client for type-safe database queries. It works with PostgreSQL, MySQL, SQLite, and other databases.

prisma migrate dev creates SQL migration files that track changes over time — required for production deployments. prisma db push syncs the schema directly without migration files — faster for prototyping but loses history. Use migrate dev for any real project.

Yes, but prisma migrate dev runs generate automatically. If you only want to update the client without running a migration, use npx prisma generate directly. If your code has TypeScript errors about unknown Prisma methods, regenerating the client usually fixes them.

DATABASE_URL is the connection string Prisma uses to connect to your database. Format: postgresql://user:password@host:port/database. Store it in your .env file at the project root and never commit it to Git — it contains your database credentials.

Yes. Run npx prisma db pull to introspect an existing database and generate the Prisma schema automatically. This lets you start using Prisma's typed client with a database that already has tables without rewriting anything.