references/client-methods.md
# Client Methods
Prisma Client instance methods.
## $connect()
Explicitly connect to the database:
```typescript
const prisma = new PrismaClient({ adapter })
// Explicit connection
await prisma.$connect()
```
### When to use
Usually not needed - Prisma connects automatically on first query. Use for:
- Fail fast on startup
- Health checks
- Pre-warming connections
```typescript
async function main() {
try {
await prisma.$connect()
console.log('Database connected')
} catch (e) {
console.error('Failed to connect:', e)
process.exit(1)
}
}
```
## $disconnect()
Close database connection:
```typescript
await prisma.$disconnect()
```
### Graceful shutdown
```typescript
process.on('beforeExit', async () => {
await prisma.$disconnect()
})
// Or with SIGTERM
process.on('SIGTERM', async () => {
await prisma.$disconnect()
process.exit(0)
})
```
### In tests
```typescript
afterAll(async () => {
await prisma.$disconnect()
})
```
## $on()
Subscribe to events:
### Query events
```typescript
const prisma = new PrismaClient({
adapter,
log: [{ level: 'query', emit: 'event' }]
})
prisma.$on('query', (e) => {
console.log('Query:', e.query)
console.log('Params:', e.params)
console.log('Duration:', e.duration, 'ms')
})
```
### Log events
```typescript
const prisma = new PrismaClient({
adapter,
log: [
{ level: 'info', emit: 'event' },
{ level: 'warn', emit: 'event' },
{ level: 'error', emit: 'event' }
]
})
prisma.$on('info', (e) => console.log(e.message))
prisma.$on('warn', (e) => console.warn(e.message))
prisma.$on('error', (e) => console.error(e.message))
```
## $extends()
Add extensions for custom behavior:
### Add custom methods
```typescript
const prisma = new PrismaClient({ adapter }).$extends({
client: {
$log: (message: string) => console.log(message)
}
})
prisma.$log('Hello!')
```
### Add model methods
```typescript
const prisma = new PrismaClient({ adapter }).$extends({
model: {
user: {
async findByEmail(email: string) {
return prisma.user.findUnique({ where: { email } })
}
}
}
})
const user = await prisma.user.findByEmail('alice@prisma.io')
```
### Query extensions
```typescript
const prisma = new PrismaClient({ adapter }).$extends({
query: {
user: {
async findMany({ args, query }) {
// Add default filter
args.where = { ...args.where, deletedAt: null }
return query(args)
}
}
}
})
```
### Result extensions
```typescript
const prisma = new PrismaClient({ adapter }).$extends({
result: {
user: {
fullName: {
needs: { firstName: true, lastName: true },
compute(user) {
return `${user.firstName} ${user.lastName}`
}
}
}
}
})
const user = await prisma.user.findFirst()
console.log(user.fullName) // Computed field
```
### Chain extensions
```typescript
const prisma = new PrismaClient({ adapter })
.$extends(loggingExtension)
.$extends(softDeleteExtension)
.$extends(computedFieldsExtension)
```
## $transaction()
See `transactions.md` for details.
## $queryRaw() / $executeRaw()
See `raw-queries.md` for details.
## Type utilities
### Prisma namespace
```typescript
import { Prisma } from '../generated/client'
// Input types
type UserCreateInput = Prisma.UserCreateInput
type UserWhereInput = Prisma.UserWhereInput
// Output types
type User = Prisma.UserGetPayload<{}>
type UserWithPosts = Prisma.UserGetPayload<{
include: { posts: true }
}>
```
### Type-safe query fragments with satisfies
Type-safe query fragments:
```typescript
import { Prisma } from '../generated/client'
const userSelect = {
id: true,
email: true,
name: true
} satisfies Prisma.UserSelect
const user = await prisma.user.findUnique({
where: { id: 1 },
select: userSelect
})
```
With the `prisma-client` generator, use TypeScript `satisfies` for typed query fragments. You may still see older examples that use `Prisma.validator()` with `prisma-client-js`.
references/constructor.md
# PrismaClient Constructor
Configure Prisma Client when instantiating.
## Basic Instantiation
```typescript
import { PrismaClient } from '../generated/client'
import { PrismaPg } from '@prisma/adapter-pg'
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL
})
const prisma = new PrismaClient({ adapter })
```
## Constructor Options
### adapter (Required for the SQL provider workflow)
Driver adapter instance:
```typescript
import { PrismaPg } from '@prisma/adapter-pg'
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL
})
const prisma = new PrismaClient({ adapter })
```
### accelerateUrl (For Accelerate users)
```typescript
import { withAccelerate } from '@prisma/extension-accelerate'
const prisma = new PrismaClient({
accelerateUrl: process.env.DATABASE_URL, // prisma:// URL
}).$extends(withAccelerate())
```
### log
Configure logging:
```typescript
const prisma = new PrismaClient({
adapter,
log: ['query', 'info', 'warn', 'error'],
})
```
#### Log levels
| Level | Description |
|-------|-------------|
| `query` | All SQL queries |
| `info` | Informational messages |
| `warn` | Warnings |
| `error` | Errors |
#### Log to events
```typescript
const prisma = new PrismaClient({
adapter,
log: [
{ level: 'query', emit: 'event' },
{ level: 'error', emit: 'stdout' },
],
})
prisma.$on('query', (e) => {
console.log('Query:', e.query)
console.log('Duration:', e.duration, 'ms')
})
```
### errorFormat
Control error formatting:
```typescript
const prisma = new PrismaClient({
adapter,
errorFormat: 'pretty', // 'pretty' | 'colorless' | 'minimal'
})
```
### comments
Attach SQL commenter plugins for observability, tracing, or query insights:
```typescript
import { PrismaClient } from '../generated/client'
import { PrismaPg } from '@prisma/adapter-pg'
import { prismaQueryInsights } from '@prisma/sqlcommenter-query-insights'
import { queryTags, withQueryTags } from '@prisma/sqlcommenter-query-tags'
import { traceContext } from '@prisma/sqlcommenter-trace-context'
const prisma = new PrismaClient({
adapter: new PrismaPg(process.env.DATABASE_URL!),
comments: [prismaQueryInsights(), traceContext(), queryTags()],
})
await withQueryTags({ route: '/api/users', requestId: 'req-123' }, () =>
prisma.user.findMany(),
)
```
Use `comments` only for SQL providers. This is the clean way to add trace or query-shape metadata without changing your query calls.
### transactionOptions
Default transaction settings:
```typescript
const prisma = new PrismaClient({
adapter,
transactionOptions: {
maxWait: 5000, // Max wait to acquire transaction (ms)
timeout: 10000, // Max transaction duration (ms)
isolationLevel: 'Serializable',
},
})
```
## Singleton Pattern
Prevent multiple client instances in development:
```typescript
// lib/prisma.ts
import { PrismaClient } from '../generated/client'
import { PrismaPg } from '@prisma/adapter-pg'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
function createPrismaClient() {
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!
})
return new PrismaClient({ adapter })
}
export const prisma = globalForPrisma.prisma ?? createPrismaClient()
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
```
## Next.js Pattern
```typescript
// lib/prisma.ts
import { PrismaClient } from '@/generated/client'
import { PrismaPg } from '@prisma/adapter-pg'
const createAdapter = () => new PrismaPg({
connectionString: process.env.DATABASE_URL!
})
const prismaClientSingleton = () => {
return new PrismaClient({ adapter: createAdapter() })
}
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>
} & typeof global
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
export default prisma
if (process.env.NODE_ENV !== 'production') {
globalThis.prismaGlobal = prisma
}
```
## Query Events
Listen to query events:
```typescript
const prisma = new PrismaClient({
adapter,
log: [{ level: 'query', emit: 'event' }],
})
prisma.$on('query', (e) => {
console.log('Query:', e.query)
console.log('Params:', e.params)
console.log('Duration:', e.duration)
})
```
## Log Events
```typescript
prisma.$on('info', (e) => console.log(e.message))
prisma.$on('warn', (e) => console.warn(e.message))
prisma.$on('error', (e) => console.error(e.message))
```
references/filters.md
# Filter Conditions and Operators
Filter operators for the `where` clause.
## Equality
```typescript
// Exact match (implicit)
where: { email: 'alice@prisma.io' }
// Explicit equals
where: { email: { equals: 'alice@prisma.io' } }
// Not equal
where: { email: { not: 'alice@prisma.io' } }
```
## Comparison
```typescript
// Greater than
where: { age: { gt: 18 } }
// Greater than or equal
where: { age: { gte: 18 } }
// Less than
where: { age: { lt: 65 } }
// Less than or equal
where: { age: { lte: 65 } }
// Combined
where: { age: { gte: 18, lte: 65 } }
```
## Lists
```typescript
// In array
where: { role: { in: ['ADMIN', 'MODERATOR'] } }
// Not in array
where: { role: { notIn: ['GUEST', 'BANNED'] } }
```
## String Filters
```typescript
// Contains
where: { email: { contains: 'prisma' } }
// Starts with
where: { email: { startsWith: 'alice' } }
// Ends with
where: { email: { endsWith: '@prisma.io' } }
// Case-insensitive (default for some databases)
where: {
email: {
contains: 'PRISMA',
mode: 'insensitive'
}
}
```
## Null Checks
```typescript
// Is null
where: { deletedAt: null }
// Is not null
where: { deletedAt: { not: null } }
// Using isSet (for optional fields)
where: { middleName: { isSet: true } }
```
## Logical Operators
### AND (implicit)
```typescript
// Multiple conditions = AND
where: {
email: { contains: '@prisma.io' },
role: 'ADMIN'
}
```
### AND (explicit)
```typescript
where: {
AND: [
{ email: { contains: '@prisma.io' } },
{ role: 'ADMIN' }
]
}
```
### OR
```typescript
where: {
OR: [
{ email: { contains: '@gmail.com' } },
{ email: { contains: '@prisma.io' } }
]
}
```
### NOT
```typescript
where: {
NOT: {
role: 'GUEST'
}
}
// Multiple NOT conditions
where: {
NOT: [
{ role: 'GUEST' },
{ verified: false }
]
}
```
### Combined
```typescript
where: {
AND: [
{ verified: true },
{
OR: [
{ role: 'ADMIN' },
{ role: 'MODERATOR' }
]
}
],
NOT: { deletedAt: { not: null } }
}
```
## Relation Filters
### some
At least one related record matches:
```typescript
// Users with at least one published post
where: {
posts: {
some: { published: true }
}
}
```
### every
All related records match:
```typescript
// Users where all posts are published
where: {
posts: {
every: { published: true }
}
}
```
### none
No related records match:
```typescript
// Users with no published posts
where: {
posts: {
none: { published: true }
}
}
```
### is / isNot (1-to-1)
```typescript
// Users with profile in specific country
where: {
profile: {
is: { country: 'USA' }
}
}
// Users without profile
where: {
profile: {
isNot: null
}
}
```
## Array Field Filters
For fields like `String[]`:
```typescript
// Has element
where: { tags: { has: 'typescript' } }
// Has some elements
where: { tags: { hasSome: ['typescript', 'javascript'] } }
// Has every element
where: { tags: { hasEvery: ['typescript', 'prisma'] } }
// Is empty
where: { tags: { isEmpty: true } }
```
## JSON Filters
```typescript
// Path-based filter
where: {
metadata: {
path: ['settings', 'theme'],
equals: 'dark'
}
}
// String contains in JSON
where: {
metadata: {
path: ['bio'],
string_contains: 'developer'
}
}
```
## Full-Text Search
```typescript
// Requires @@fulltext index
where: {
content: {
search: 'prisma database'
}
}
```
references/model-queries.md
# Model Queries
CRUD operations for your Prisma models.
## Read Operations
### findUnique
Find a single record by unique field:
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 }
})
const user = await prisma.user.findUnique({
where: { email: 'alice@prisma.io' }
})
```
#### With composite unique key
```typescript
// Model with @@unique([firstName, lastName])
const user = await prisma.user.findUnique({
where: {
firstName_lastName: {
firstName: 'Alice',
lastName: 'Smith'
}
}
})
```
### findUniqueOrThrow
Same as findUnique but throws if not found:
```typescript
const user = await prisma.user.findUniqueOrThrow({
where: { id: 1 }
})
// Throws PrismaClientKnownRequestError if not found
```
### findFirst
Find first matching record:
```typescript
const user = await prisma.user.findFirst({
where: { role: 'ADMIN' },
orderBy: { createdAt: 'desc' }
})
```
### findFirstOrThrow
```typescript
const user = await prisma.user.findFirstOrThrow({
where: { role: 'ADMIN' }
})
```
### findMany
Find multiple records:
```typescript
const users = await prisma.user.findMany({
where: { role: 'USER' },
orderBy: { name: 'asc' },
take: 10,
skip: 0
})
```
## Create Operations
### create
Create a single record:
```typescript
const user = await prisma.user.create({
data: {
email: 'alice@prisma.io',
name: 'Alice'
}
})
```
#### With relations
```typescript
const user = await prisma.user.create({
data: {
email: 'alice@prisma.io',
posts: {
create: [
{ title: 'First Post' },
{ title: 'Second Post' }
]
}
},
include: { posts: true }
})
```
### createMany
Create multiple records:
```typescript
const result = await prisma.user.createMany({
data: [
{ email: 'alice@prisma.io', name: 'Alice' },
{ email: 'bob@prisma.io', name: 'Bob' }
],
skipDuplicates: true // Skip records with duplicate unique fields
})
// Returns { count: 2 }
```
### createManyAndReturn
Create multiple and return them:
```typescript
const users = await prisma.user.createManyAndReturn({
data: [
{ email: 'alice@prisma.io', name: 'Alice' },
{ email: 'bob@prisma.io', name: 'Bob' }
]
})
// Returns array of created users
```
## Update Operations
### update
Update a single record:
```typescript
const user = await prisma.user.update({
where: { id: 1 },
data: { name: 'Alice Smith' }
})
```
#### Atomic operations
```typescript
const post = await prisma.post.update({
where: { id: 1 },
data: {
views: { increment: 1 },
likes: { decrement: 1 },
score: { multiply: 2 },
rating: { divide: 2 },
version: { set: 5 }
}
})
```
### updateMany
Update multiple records:
```typescript
const result = await prisma.user.updateMany({
where: { role: 'USER' },
data: { verified: true }
})
// Returns { count: 42 }
```
### updateManyAndReturn
```typescript
const users = await prisma.user.updateManyAndReturn({
where: { role: 'USER' },
data: { verified: true }
})
// Returns array of updated users
```
### upsert
Update or create:
```typescript
const user = await prisma.user.upsert({
where: { email: 'alice@prisma.io' },
update: { name: 'Alice Smith' },
create: { email: 'alice@prisma.io', name: 'Alice' }
})
```
## Delete Operations
### delete
Delete a single record:
```typescript
const user = await prisma.user.delete({
where: { id: 1 }
})
// Returns deleted record
```
### deleteMany
Delete multiple records:
```typescript
const result = await prisma.user.deleteMany({
where: { role: 'GUEST' }
})
// Returns { count: 5 }
// Delete all
const result = await prisma.user.deleteMany({})
```
## Aggregation Operations
### count
```typescript
const count = await prisma.user.count({
where: { role: 'ADMIN' }
})
```
### aggregate
```typescript
const result = await prisma.post.aggregate({
_avg: { views: true },
_sum: { views: true },
_min: { views: true },
_max: { views: true },
_count: { _all: true }
})
```
### groupBy
```typescript
const groups = await prisma.user.groupBy({
by: ['country'],
_count: { _all: true },
_avg: { age: true },
having: {
age: { _avg: { gt: 30 } }
}
})
```
## Return Types
| Method | Returns |
|--------|---------|
| `findUnique` | Record \| null |
| `findUniqueOrThrow` | Record (throws if not found) |
| `findFirst` | Record \| null |
| `findFirstOrThrow` | Record (throws if not found) |
| `findMany` | Record[] |
| `create` | Record |
| `createMany` | { count: number } |
| `createManyAndReturn` | Record[] |
| `update` | Record |
| `updateMany` | { count: number } |
| `delete` | Record |
| `deleteMany` | { count: number } |
| `count` | number |
| `aggregate` | Aggregate result |
| `groupBy` | Group result[] |
references/query-options.md
# Query Options
Options for controlling query behavior.
## select
Choose specific fields to return:
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
select: {
id: true,
name: true,
email: true,
// password: false (excluded by not including)
}
})
// Returns: { id: 1, name: 'Alice', email: 'alice@prisma.io' }
```
### Select relations
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
select: {
name: true,
posts: {
select: {
title: true,
published: true
}
}
}
})
```
### Select with include inside
```typescript
const user = await prisma.user.findMany({
select: {
name: true,
posts: {
include: {
comments: true
}
}
}
})
```
### Select relation count
```typescript
const users = await prisma.user.findMany({
select: {
name: true,
_count: {
select: { posts: true }
}
}
})
// Returns: { name: 'Alice', _count: { posts: 5 } }
```
## include
Include related records:
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: true,
profile: true
}
})
```
### Filtered include
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
take: 5
}
}
})
```
### Nested include
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
include: {
comments: {
include: {
author: true
}
}
}
}
}
})
```
### Include relation count
```typescript
const users = await prisma.user.findMany({
include: {
_count: {
select: { posts: true, followers: true }
}
}
})
```
## omit
Exclude specific fields:
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
omit: {
password: true
}
})
// Returns all fields except password
```
### Omit in relations
```typescript
const users = await prisma.user.findMany({
omit: { password: true },
include: {
posts: {
omit: { content: true }
}
}
})
```
**Note:** Cannot use `select` and `omit` together.
## where
Filter records:
```typescript
const users = await prisma.user.findMany({
where: {
email: { contains: '@prisma.io' },
role: 'ADMIN'
}
})
```
See `filters.md` for detailed filter operators.
## orderBy
Sort results:
```typescript
// Single field
const users = await prisma.user.findMany({
orderBy: { name: 'asc' }
})
// Multiple fields
const users = await prisma.user.findMany({
orderBy: [
{ role: 'desc' },
{ name: 'asc' }
]
})
```
### Order by relation
```typescript
const users = await prisma.user.findMany({
orderBy: {
posts: { _count: 'desc' }
}
})
```
### Null handling
```typescript
const users = await prisma.user.findMany({
orderBy: {
name: { sort: 'asc', nulls: 'last' }
}
})
```
## take & skip
Pagination:
```typescript
// First page
const users = await prisma.user.findMany({
take: 10,
skip: 0
})
// Second page
const users = await prisma.user.findMany({
take: 10,
skip: 10
})
```
### Negative take (reverse)
```typescript
const lastUsers = await prisma.user.findMany({
take: -10,
orderBy: { id: 'asc' }
})
// Returns last 10 users
```
## cursor
Cursor-based pagination:
```typescript
// First page
const firstPage = await prisma.user.findMany({
take: 10,
orderBy: { id: 'asc' }
})
// Next page using cursor
const nextPage = await prisma.user.findMany({
take: 10,
skip: 1, // Skip the cursor record
cursor: { id: firstPage[firstPage.length - 1].id },
orderBy: { id: 'asc' }
})
```
## distinct
Return unique values:
```typescript
const cities = await prisma.user.findMany({
distinct: ['city'],
select: { city: true }
})
```
### Multiple distinct fields
```typescript
const locations = await prisma.user.findMany({
distinct: ['city', 'country']
})
```
references/raw-queries.md
# Raw Queries
Execute raw SQL when Prisma's query API isn't sufficient.
## $queryRaw
Execute SELECT queries and get typed results:
```typescript
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE email LIKE ${'%@prisma.io'}
`
```
### With type
```typescript
type User = { id: number; email: string; name: string | null }
const users = await prisma.$queryRaw<User[]>`
SELECT id, email, name FROM "User" WHERE role = ${'ADMIN'}
`
```
### Dynamic table/column names
Use `Prisma.raw()` for identifiers (not safe for user input):
```typescript
import { Prisma } from '../generated/client'
const column = 'email'
const users = await prisma.$queryRaw`
SELECT ${Prisma.raw(column)} FROM "User"
`
```
### With Prisma.sql
Build queries dynamically:
```typescript
import { Prisma } from '../generated/client'
const email = 'alice@prisma.io'
const query = Prisma.sql`SELECT * FROM "User" WHERE email = ${email}`
const users = await prisma.$queryRaw(query)
```
### Join multiple SQL fragments
```typescript
import { Prisma } from '../generated/client'
const conditions = [
Prisma.sql`role = ${'ADMIN'}`,
Prisma.sql`verified = ${true}`
]
const users = await prisma.$queryRaw`
SELECT * FROM "User"
WHERE ${Prisma.join(conditions, ' AND ')}
`
```
## $executeRaw
Execute INSERT, UPDATE, DELETE (returns affected count):
```typescript
const count = await prisma.$executeRaw`
UPDATE "User" SET verified = true WHERE email LIKE ${'%@prisma.io'}
`
console.log(`Updated ${count} users`)
```
### Delete example
```typescript
const deleted = await prisma.$executeRaw`
DELETE FROM "User" WHERE "deletedAt" < ${thirtyDaysAgo}
`
```
### Insert example
```typescript
const inserted = await prisma.$executeRaw`
INSERT INTO "Log" (message, level, timestamp)
VALUES (${message}, ${level}, ${new Date()})
`
```
## $queryRawUnsafe / $executeRawUnsafe
For fully dynamic queries (use with caution!):
```typescript
// ⚠️ SQL injection risk - only use with trusted input
const table = 'User'
const users = await prisma.$queryRawUnsafe(
`SELECT * FROM "${table}" WHERE id = $1`,
userId
)
```
### Parameterized unsafe query
```typescript
const result = await prisma.$executeRawUnsafe(
'UPDATE "User" SET name = $1 WHERE id = $2',
'Alice',
1
)
```
## SQL Injection Prevention
### Safe (parameterized)
```typescript
// ✅ User input is parameterized
const email = userInput
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE email = ${email}
`
```
### Unsafe (concatenation)
```typescript
// ❌ SQL injection vulnerability!
const email = userInput
const users = await prisma.$queryRawUnsafe(
`SELECT * FROM "User" WHERE email = '${email}'`
)
```
## Database-Specific Features
### PostgreSQL
```typescript
// Array operations
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE 'admin' = ANY(roles)
`
// JSON operations
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE metadata->>'theme' = 'dark'
`
```
### MySQL
```typescript
// Full-text search
const posts = await prisma.$queryRaw`
SELECT * FROM Post WHERE MATCH(title, content) AGAINST(${searchTerm})
`
```
## Transactions with Raw Queries
```typescript
await prisma.$transaction(async (tx) => {
await tx.$executeRaw`UPDATE "Account" SET balance = balance - ${amount} WHERE id = ${senderId}`
await tx.$executeRaw`UPDATE "Account" SET balance = balance + ${amount} WHERE id = ${recipientId}`
})
```
## Handling Results
### BigInt handling
PostgreSQL returns BigInt for COUNT:
```typescript
const result = await prisma.$queryRaw<[{ count: bigint }]>`
SELECT COUNT(*) as count FROM "User"
`
const count = Number(result[0].count)
```
### Date handling
```typescript
type Result = { createdAt: Date }
const users = await prisma.$queryRaw<Result[]>`
SELECT "createdAt" FROM "User"
`
// createdAt is already a Date object
```
references/relations.md
# Relation Queries
Query and modify related records.
## Include Relations
Load related records:
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: true,
profile: true
}
})
```
### Filtered include
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
take: 5,
select: { id: true, title: true }
}
}
})
```
### Nested include
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
include: {
comments: {
include: { author: true }
}
}
}
}
})
```
## Select Relations
```typescript
const user = await prisma.user.findUnique({
where: { id: 1 },
select: {
name: true,
posts: {
select: { title: true }
}
}
})
```
## Nested Writes
### Create with relations
```typescript
const user = await prisma.user.create({
data: {
email: 'alice@prisma.io',
posts: {
create: [
{ title: 'Post 1' },
{ title: 'Post 2' }
]
},
profile: {
create: { bio: 'Hello!' }
}
}
})
```
### Create or connect
```typescript
const post = await prisma.post.create({
data: {
title: 'New Post',
author: {
connectOrCreate: {
where: { email: 'alice@prisma.io' },
create: { email: 'alice@prisma.io', name: 'Alice' }
}
}
}
})
```
### Connect existing
```typescript
const post = await prisma.post.create({
data: {
title: 'New Post',
author: {
connect: { id: 1 }
}
}
})
// Shorthand for foreign key
const post = await prisma.post.create({
data: {
title: 'New Post',
authorId: 1
}
})
```
## Update Relations
### Update related records
```typescript
const user = await prisma.user.update({
where: { id: 1 },
data: {
posts: {
update: {
where: { id: 1 },
data: { title: 'Updated Title' }
}
}
}
})
```
### Update many related
```typescript
const user = await prisma.user.update({
where: { id: 1 },
data: {
posts: {
updateMany: {
where: { published: false },
data: { published: true }
}
}
}
})
```
### Upsert related
```typescript
const user = await prisma.user.update({
where: { id: 1 },
data: {
profile: {
upsert: {
create: { bio: 'New bio' },
update: { bio: 'Updated bio' }
}
}
}
})
```
### Disconnect
```typescript
// 1-to-1 optional
const user = await prisma.user.update({
where: { id: 1 },
data: {
profile: { disconnect: true }
}
})
// Many-to-many
const post = await prisma.post.update({
where: { id: 1 },
data: {
tags: {
disconnect: [{ id: 1 }, { id: 2 }]
}
}
})
```
### Delete related
```typescript
const user = await prisma.user.update({
where: { id: 1 },
data: {
posts: {
delete: { id: 1 }
}
}
})
// Delete many
const user = await prisma.user.update({
where: { id: 1 },
data: {
posts: {
deleteMany: { published: false }
}
}
})
```
### Set (replace all)
```typescript
// Replace all related records
const post = await prisma.post.update({
where: { id: 1 },
data: {
tags: {
set: [{ id: 1 }, { id: 2 }]
}
}
})
```
## Relation Filters
### some
At least one matches:
```typescript
const users = await prisma.user.findMany({
where: {
posts: { some: { published: true } }
}
})
```
### every
All match:
```typescript
const users = await prisma.user.findMany({
where: {
posts: { every: { published: true } }
}
})
```
### none
None match:
```typescript
const users = await prisma.user.findMany({
where: {
posts: { none: { published: true } }
}
})
```
### is / isNot (1-to-1)
```typescript
const users = await prisma.user.findMany({
where: {
profile: { is: { country: 'USA' } }
}
})
```
## Count Relations
```typescript
const users = await prisma.user.findMany({
select: {
name: true,
_count: {
select: { posts: true, followers: true }
}
}
})
// { name: 'Alice', _count: { posts: 5, followers: 100 } }
```
### Filter counted relations
```typescript
const users = await prisma.user.findMany({
select: {
name: true,
_count: {
select: {
posts: { where: { published: true } }
}
}
}
})
```
references/transactions.md
# Transactions
Execute multiple operations atomically.
## Sequential Transactions
Array of operations executed in order:
```typescript
const [user, post] = await prisma.$transaction([
prisma.user.create({ data: { email: 'alice@prisma.io' } }),
prisma.post.create({ data: { title: 'Hello', authorId: 1 } })
])
```
### All or nothing
If any operation fails, all are rolled back:
```typescript
try {
await prisma.$transaction([
prisma.user.create({ data: { email: 'alice@prisma.io' } }),
prisma.user.create({ data: { email: 'alice@prisma.io' } }) // Duplicate!
])
} catch (e) {
// Both operations rolled back
}
```
## Interactive Transactions
For complex logic and dependent operations:
```typescript
await prisma.$transaction(async (tx) => {
// Decrement sender balance
const sender = await tx.account.update({
where: { id: senderId },
data: { balance: { decrement: amount } }
})
// Check balance
if (sender.balance < 0) {
throw new Error('Insufficient funds')
}
// Increment recipient balance
await tx.account.update({
where: { id: recipientId },
data: { balance: { increment: amount } }
})
})
```
### Transaction options
```typescript
await prisma.$transaction(
async (tx) => {
// operations
},
{
maxWait: 5000, // Max wait to acquire lock (ms)
timeout: 10000, // Max transaction duration (ms)
isolationLevel: 'Serializable' // Isolation level
}
)
```
### Isolation levels
| Level | Description |
|-------|-------------|
| `ReadUncommitted` | Lowest isolation, can read uncommitted changes |
| `ReadCommitted` | Only read committed changes |
| `RepeatableRead` | Consistent reads within transaction |
| `Serializable` | Highest isolation, serialized execution |
## Nested Writes
Automatic transactions for nested operations:
```typescript
// This is automatically a transaction
const user = await prisma.user.create({
data: {
email: 'alice@prisma.io',
posts: {
create: [
{ title: 'Post 1' },
{ title: 'Post 2' }
]
},
profile: {
create: { bio: 'Hello!' }
}
}
})
```
## Transaction Client
The `tx` parameter is a Prisma Client scoped to the transaction:
```typescript
await prisma.$transaction(async (tx) => {
// Use tx instead of prisma
await tx.user.create({ ... })
await tx.post.create({ ... })
// Can call methods
const count = await tx.user.count()
})
```
## OrThrow in Transactions
Use with interactive transactions:
```typescript
await prisma.$transaction(async (tx) => {
// If not found, throws and rolls back entire transaction
const user = await tx.user.findUniqueOrThrow({
where: { id: 1 }
})
await tx.post.create({
data: { title: 'New Post', authorId: user.id }
})
})
```
## Best Practices
### Keep transactions short
```typescript
// Good - only DB operations in transaction
const data = prepareData() // Outside transaction
await prisma.$transaction(async (tx) => {
await tx.user.create({ data })
})
```
### Handle errors
```typescript
try {
await prisma.$transaction(async (tx) => {
// operations
})
} catch (e) {
if (e.code === 'P2002') {
// Handle unique constraint violation
}
throw e
}
```
### Use appropriate isolation
```typescript
// Default is fine for most cases
await prisma.$transaction(async (tx) => {
// operations
})
// Use Serializable for strict consistency
await prisma.$transaction(
async (tx) => { /* operations */ },
{ isolationLevel: 'Serializable' }
)
```
## Sequential vs Interactive
| Feature | Sequential | Interactive |
|---------|------------|-------------|
| Syntax | Array | Async function |
| Dependent ops | No | Yes |
| Conditional logic | No | Yes |
| Performance | Better | More flexible |
| Use case | Simple batch | Complex logic |