What it is
Prisma is a next-generation ORM with a declarative schema, generated type-safe client, and a migration system that produces reviewable SQL.
Model the database in schema.prisma, run a migration, and Prisma generates a fully typed client. Relations, filters, ordering and pagination are all type-checked.
Installation
npm install prisma --save-dev && npm install @prisma/clientGetting started
The smallest useful thing you can do with it, and what each part means.
model Book {
id Int @id @default(autoincrement())
title String
year Int
author Author @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
@@index([year])
}const books = await prisma.book.findMany({
where: { year: { gte: 1990 }, title: { contains: 'Dune' } },
include: { author: true }, // avoids a second query
orderBy: { year: 'desc' },
take: 10,
});
// books[0].author.name is typed; books[0].publisher is a compile error
const created = await prisma.book.create({
data: { title: 'Dune', year: 1965, author: { connect: { id: 1 } } },
});Advanced usage
Where the library earns its place over a simpler alternative.
// Interactive transaction — rolls back if the callback throws.
const order = await prisma.$transaction(async (tx) => {
const stock = await tx.stock.update({
where: { sku },
data: { quantity: { decrement: qty } },
});
if (stock.quantity < 0) throw new Error('insufficient stock');
return tx.order.create({ data: { sku, qty } });
});
// Prisma batches relation loads, but nested loops still cost queries.
// Prefer one query with include over N queries in a map().Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Unique constraint failed on the fields
- Catch PrismaClientKnownRequestError and check error.code === 'P2002'; the meta field names the offending column.
- Too many database connections in development
- Hot reload creates a new client per reload. Cache it on globalThis outside production.
Best practices
- Instantiate PrismaClient once and reuse it; a client per request exhausts the connection pool.
- Use include or select to load relations rather than querying inside a loop.
- Review the generated migration SQL before applying it in production.
- Use select to fetch only needed columns on wide tables — it narrows the return type too.
Background
Why it exists, and what it was reacting to.
Prisma replaced its earlier GraphQL-centric product with a schema-first ORM. Generating the client from the schema means autocompletion reflects the actual database, which is why it became the default for TypeScript back ends.
