Prisma Client Queries
Query data with Prisma Client findUnique/findMany, create/update/delete, upsert, select, include
When to Use
- Reading data from the database with type-safe queries
- Creating, updating, or deleting records
- Selecting specific fields or including related records
- Performing upserts (create-or-update) operations
Instructions
- Instantiate the client once and reuse it. Never create a new
PrismaClientper request:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
- Find a single record — use
findUniquefor lookups by@idor@uniquefields. UsefindFirstwhen filtering by non-unique fields:
const user = await prisma.user.findUnique({ where: { id: userId } });
const admin = await prisma.user.findFirst({ where: { role: 'ADMIN' } });
- Find multiple records —
findManyreturns an array. Always paginate large result sets:
const users = await prisma.user.findMany({
where: { role: 'USER' },
take: 20,
skip: 0,
orderBy: { createdAt: 'desc' },
});
- Select specific fields to avoid over-fetching:
const names = await prisma.user.findMany({
select: { id: true, name: true, email: true },
});
// Type: { id: string; name: string | null; email: string }[]
- Include related records with
include:
const userWithPosts = await prisma.user.findUnique({
where: { id: userId },
include: { posts: { where: { published: true }, take: 10 } },
});
- Create a record — all required fields must be provided:
const user = await prisma.user.create({
data: { email: 'new@example.com', name: 'New User' },
});
- Update a record — requires a unique
whereclause:
const updated = await prisma.user.update({
where: { id: userId },
data: { name: 'Updated Name' },
});
- Upsert — create if not found, update if found:
const user = await prisma.user.upsert({
where: { email: 'user@example.com' },
create: { email: 'user@example.com', name: 'New' },
update: { name: 'Existing' },
});
- Delete — use
deletefor single records,deleteManyfor bulk:
await prisma.user.delete({ where: { id: userId } });
await prisma.post.deleteMany({ where: { authorId: userId } });
- Nested writes — create or connect related records in one operation:
const user = await prisma.user.create({
data: {
email: 'author@example.com',
posts: { create: [{ title: 'First Post' }, { title: 'Second Post' }] },
},
include: { posts: true },
});
Details
Prisma Client is auto-generated from the schema and provides full TypeScript types for every query. The generated types enforce that you only pass valid field names, filter operators, and relation includes.
select vs include: These are mutually exclusive at the same level. select returns only the specified fields (plus any nested select/include). include returns all scalar fields plus the specified relations.
findUnique vs findFirst: findUnique can only filter by @id or @unique fields and benefits from Prisma's internal query deduplication (DataLoader batching). findFirst can filter by any field but does not deduplicate.
findUniqueOrThrow / findFirstOrThrow: These variants throw a PrismaClientKnownRequestError with code P2025 instead of returning null. Use them when absence is an error condition.
Nested writes are atomic: create with nested create/connect/connectOrCreate runs in a single implicit transaction. If any nested operation fails, the entire write is rolled back.
Return types narrow automatically: When you use select, the return type includes only the selected fields. This is enforced at the TypeScript level, so your code stays type-safe.
Common mistakes:
- Using
findFirstwherefindUniquewould work — loses batching optimization - Deeply nesting
include— each level adds a separate database query. Three levels deep on a list page causes N+1 problems - Not handling
nullfromfindUnique— always check or usefindUniqueOrThrow - Forgetting that
updateMany/deleteManydo not return the affected records — only a count
Source
https://prisma.io/docs/orm/prisma-client/queries
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.