Skip to content
FLAVIO COPES
flaviocopes.com

How to use Prisma

By

Learn how to set up modern Prisma ORM with PostgreSQL, define a model, create migrations, generate Prisma Client, and run CRUD queries in a Next.js app.

~~~

Prisma is an ORM for working with databases from TypeScript and JavaScript.

You define models in a Prisma schema, create database migrations from those models, and generate a type-safe client for queries.

This guide uses PostgreSQL and a Next.js TypeScript application. The same workflow also works outside Next.js, but the database driver and application integration can differ.

Install Prisma

From your application directory, install the Prisma CLI and PostgreSQL types as development dependencies:

npm install --save-dev prisma @types/pg

Install Prisma Client, the PostgreSQL driver adapter, and its runtime dependencies:

npm install @prisma/client @prisma/adapter-pg pg dotenv

Initialize Prisma:

npx prisma init \
  --datasource-provider postgresql \
  --output ../generated/prisma

Current Prisma projects use the prisma-client generator with an explicit output directory. Initialization creates:

See the current prisma init reference before copying initialization output from an older project.

Configure the database connection

Put the PostgreSQL connection string in .env:

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

Do not commit this file when it contains real credentials.

prisma.config.ts reads the connection URL and tells Prisma where to find the schema and migrations:

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')
  }
})

Use a separate database and credentials for local development. Production credentials belong in the deployment platform’s secret or environment-variable settings.

Define a model

Open prisma/schema.prisma:

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

datasource db {
  provider = "postgresql"
}

model Car {
  id        Int      @id @default(autoincrement())
  brand     String
  model     String
  createdAt DateTime @default(now())
  bought    Boolean  @default(false)
}

The Car model becomes a database table.

id is its primary key and receives an automatically increasing value. createdAt defaults to the current time, and bought defaults to false.

The Prisma schema reference documents field types, attributes, relations, indexes, and database-specific behavior.

Create the first migration

Create and apply a development migration:

npx prisma migrate dev --name init

Then generate Prisma Client:

npx prisma generate

Current Prisma versions do not automatically run prisma generate after prisma migrate dev, so keep the two steps explicit.

Commit the generated SQL migration in prisma/migrations/. Do not edit the live production database by running migrate dev against it.

In a deployment pipeline, apply committed migrations with:

npx prisma migrate deploy

The migrate dev documentation explains its development-only shadow database workflow.

Create one Prisma Client

Create lib/prisma.ts:

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

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined
}

const adapter = new PrismaPg({
  connectionString: process.env.DATABASE_URL!
})

const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    adapter
  })

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma
}

export default prisma

Reusing the client through globalThis prevents Next.js hot reloads from creating many connection pools during development.

Use Prisma only in server-side code. Do not import the client into a Client Component or browser bundle.

Prisma’s Next.js guide shows the current generated-client and driver-adapter setup.

Create a record

Prisma’s create operations receive values inside data:

const car = await prisma.car.create({
  data: {
    brand: 'Ford',
    model: 'Fiesta'
  }
})

Read records

Retrieve every car:

const cars = await prisma.car.findMany()

Filter the query:

const cars = await prisma.car.findMany({
  where: {
    brand: 'Ford'
  },
  orderBy: {
    createdAt: 'desc'
  }
})

Find one car by its unique ID:

const car = await prisma.car.findUnique({
  where: {
    id: 1
  }
})

findUnique() returns null when no matching record exists. Handle that case before using the result.

Update a record

const car = await prisma.car.update({
  where: {
    id: 1
  },
  data: {
    bought: true
  }
})

update() throws if the record does not exist. Choose updateMany() or handle the error when absence is expected.

Delete a record

await prisma.car.delete({
  where: {
    id: 1
  }
})

Validate authorization before updating or deleting data. A type-safe query is not automatically an authorized one.

Start from an existing database

If the database already has tables, introspect it:

npx prisma db pull
npx prisma generate

Review the generated schema before building on it. Database names, unsupported features, and relation conventions may need manual adjustments.

If you want to compare schema formats, the schema converter translates between SQL, Prisma, and Drizzle.

Keep generated code current

Run npx prisma generate after changing the schema, changing generator settings, or pulling schema changes from another branch.

Many deployment systems cache dependencies, so add generation to the build or install workflow when required:

{
  "scripts": {
    "postinstall": "prisma generate"
  }
}

Prisma’s client generation guide explains when generated code must be refreshed.

Tagged: Database · All topics
~~~

Related posts about database: