mrkeyoor.com_
Wed 05 Aug 05:03 UTC
npmDataupdated 05 Aug 2026

prisma

Prisma is a database toolkit for Node.js and TypeScript: you describe your tables in a schema.prisma file, the CLI generates a fully typed client for queries, Prisma Migrate turns schema changes into SQL migrations, and Prisma Studio gives you a GUI over the data. The prisma npm package is the CLI; your app imports the generated client. Version 7 dropped the Rust query engine in favor of a TypeScript client that talks to the database through driver adapters.

Verdict

Still the most complete typed database toolkit for TypeScript, and v7's engine-free client fixed the old binary headaches. Weigh the setup ceremony and the fact that active invention has moved to the Prisma Next rewrite; for SQL-comfortable teams Drizzle or Kysely are lighter and less eventful.

API stability3/5The client query API is stable, but v7 changed the setup contract: new generator provider, required prisma.config.ts, driver adapters, no automatic .env loading. Major versions here mean real migration work, and the Prisma Next rewrite signals more change ahead.
Docs5/5prisma.io/docs is thorough with per-database getting-started paths, a full config reference and honest upgrade guides; error messages link to docs.
Maintenance4/5Very active repo (pushed today) and regular 7.x releases, but ORM 7 now lives on the v7 branch while headline development goes into the Prisma Next rewrite on main; expect maintenance-mode pacing for the current line.
Ecosystem4/5Huge community, adapters for major SQL databases, Studio, and integrations across Next.js and serverless platforms; third-party generators exist for zod, ERDs and more.

Use it if

  • You want end-to-end typed queries where renaming a column breaks the build instead of production
  • Your team is stronger in TypeScript than SQL and wants migrations, client and data browser from one tool
  • You are on PostgreSQL, MySQL, SQLite, SQL Server or CockroachDB, the databases the schema DSL models best
  • You value a declarative schema file as the single source of truth that code review can actually read
Skip it if

Setup reality

More moving parts than any competing ORM. You need schema.prisma with a generator block plus output path, a prisma.config.ts for the CLI, and a driver adapter package like @prisma/adapter-pg passed to the client constructor. Since v7 .env files are not loaded automatically with prisma.config.ts, so add dotenv/config or node --env-file yourself. Every schema change requires npx prisma generate before types update, which trips up everyone at least once.

Patterns

Define models in schema.prisma (v7 shape)define-schema

generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

model User {
  id    Int    @id @default(autoincrement())
  email String @unique
  posts Post[]
}

model Post {
  id       Int    @id @default(autoincrement())
  title    String
  author   User   @relation(fields: [authorId], references: [id])
  authorId Int
}

v7 uses provider prisma-client with a required output path; the old prisma-client-js default and node_modules output are gone.

prisma.config.ts for the CLIconfigure-cli

import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'

export default defineConfig({
  schema: 'prisma/schema.prisma',
  migrations: { path: 'prisma/migrations' },
  datasource: { url: env('DATABASE_URL') },
})

With prisma.config.ts, .env is NOT auto-loaded anymore; the dotenv/config import at the top is doing real work.

Create the client with a driver adapterinstantiate-client

import { PrismaClient } from './generated/prisma/client'
import { PrismaPg } from '@prisma/adapter-pg'

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })
export const prisma = new PrismaClient({ adapter })

v7 requires a driver adapter (@prisma/adapter-pg here) and you import from your generated output path, not from @prisma/client directly.

Create and apply a migrationmigrate

npx prisma migrate dev --name add_posts   # dev: diff, apply, regenerate
npx prisma migrate deploy                  # prod: apply pending migrations
npx prisma generate                        # regenerate client after schema edits

migrate dev is for development only; it can reset data. CI and production should run migrate deploy.

Create and query recordscrud-create-read

const user = await prisma.user.create({
  data: {
    email: 'alice@example.com',
    posts: { create: { title: 'hello' } },
  },
})

const users = await prisma.user.findMany({
  where: { email: { endsWith: '@example.com' } },
  select: { id: true, email: true },
})

Nested create writes parent and child in one call; select trims the payload and the return type follows it.

Load relations with includerelations-include

const usersWithPosts = await prisma.user.findMany({
  include: {
    posts: {
      where: { title: { contains: 'prisma' } },
      orderBy: { id: 'desc' },
      take: 3,
    },
  },
})

include and select are mutually exclusive at the same level; combining them is a common first error.

Update or upsert a recordupdate-upsert

await prisma.post.update({
  where: { id: 42 },
  data: { title: 'updated' },
})

await prisma.user.upsert({
  where: { email: 'alice@example.com' },
  update: { email: 'alice@example.com' },
  create: { email: 'alice@example.com' },
})

update throws if the record does not exist; use updateMany or upsert when absence is a normal case.

Run queries in a transactiontransactions

await prisma.$transaction(async (tx) => {
  const from = await tx.account.update({
    where: { id: 1 },
    data: { balance: { decrement: 100 } },
  })
  if (from.balance < 0) throw new Error('insufficient funds')
  await tx.account.update({
    where: { id: 2 },
    data: { balance: { increment: 100 } },
  })
})

Throwing inside the callback rolls everything back; use the tx handle, not the outer prisma, or queries escape the transaction.

Cursor-based paginationpagination

const page = await prisma.post.findMany({
  take: 20,
  skip: cursor ? 1 : 0,
  cursor: cursor ? { id: cursor } : undefined,
  orderBy: { id: 'asc' },
})
const nextCursor = page.at(-1)?.id

skip: 1 excludes the cursor row itself; offset pagination (skip/take alone) degrades on large tables.

Escape hatch to raw SQLraw-sql

const rows = await prisma.$queryRaw`
  SELECT author_id, COUNT(*)::int AS posts
  FROM "Post"
  GROUP BY author_id
  HAVING COUNT(*) > ${min}
`

The tagged template parameterizes ${min} safely; $queryRawUnsafe with string concatenation is the injection foot-gun.

Handle known Prisma errorserror-handling

import { Prisma } from './generated/prisma/client'

try {
  await prisma.user.create({ data: { email } })
} catch (e) {
  if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
    return { error: 'email already taken' }
  }
  throw e
}

P2002 is the unique-constraint code; matching on error codes beats parsing messages that change between versions.

Alternatives

PackageRegistryPick it when
drizzle-ormnpmYou want typed queries that stay close to SQL with no codegen step and a much smaller runtime
kyselynpmYou want a pure type-safe SQL query builder and are happy managing migrations separately
typeormnpmDecorator-and-entity style ORM in an existing NestJS codebase where it is already the convention