Skip to main content
Prisma Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

PostgreSQL

To add Prisma ORM to a project that already uses PostgreSQL, you will run orm init, infer a contract from the live schema, sign the database, and run a couple of queries.

Use this path when you already have an application and database. Make sure the app can already reach its PostgreSQL database and runs on Node.js 22.18 or newer (on the 24 line, 24.11 or newer; Node.js 24 is recommended). If you want Prisma ORM to create a new app for you, use the PostgreSQL quickstart.

If your project already runs TypeScript scripts, you can skip this step.

Otherwise, install the script tooling:

title="bun"
bun add --dev tsx typescript
pnpm
pnpm add --save-dev tsx typescript
yarn
yarn add --dev tsx typescript
npm
npm install --save-dev tsx typescript

From the root of your existing project, run:

bunx prisma@latest orm init --target postgres
Bash
pnpm dlx prisma@latest orm init --target postgres
Bash
yarn dlx prisma@latest orm init --target postgres
Bash
npx prisma@latest orm init --target postgres

This command is for a project that already exists: it preselects PostgreSQL, adds Prisma ORM files and package scripts to the app you already have, and does not scaffold a new framework project.

orm init also changes the module settings in tsconfig.json, and adds "type": "module" to package.json when the file has no "type" field. If your app is CommonJS, follow In a CommonJS project before you run the app again. The scripts in this guide run through tsx, which works in both kinds of project.

It also adds prisma-8.md, a short project-level reference your coding agent can read. It does not install agent skills; the Prisma ORM skill ships inside the @prisma/orm-postgres package your project installs. If you later run prisma init or prisma skills sync, Prisma writes skill files for coding agents into your repo. To stop that, pass --skills=none to init or set the skills.agents config field to []; the next skills sync removes any copies already written.

When Prisma ORM asks the remaining setup questions:

  • choose PSL
  • keep the default schema path, src/prisma/contract.prisma. Pass --schema-path if you want the contract somewhere else; the rest of this page assumes the default.
  • answer the last question, Also write a .env file from .env.example? (gitignored), with Yes. It defaults to No, and --write-env skips the prompt and writes the file.

This command is for a project that already exists: it preselects PostgreSQL, adds Prisma ORM files and package scripts to the app you already have, and does not scaffold a new framework project.

orm init also changes the module settings in tsconfig.json, and adds "type": "module" to package.json when the file has no "type" field. If your app is CommonJS, follow In a CommonJS project before you run the app again. The scripts in this guide run through tsx, which works in both kinds of project.

It also adds prisma-8.md, a short project-level reference your coding agent can read. It does not install agent skills; the Prisma ORM skill ships inside the @prisma/orm-postgres package your project installs. If you later run prisma init or prisma skills sync, Prisma writes skill files for coding agents into your repo. To stop that, pass --skills=none to init or set the skills.agents config field to []; the next skills sync removes any copies already written.

When Prisma ORM asks the remaining setup questions:

  • choose PSL
  • keep the default schema path, src/prisma/contract.prisma. Pass --schema-path if you want the contract somewhere else; the rest of this page assumes the default.
  • answer the last question, Also write a .env file from .env.example? (gitignored), with Yes. It defaults to No, and --write-env skips the prompt and writes the file.

orm init always writes .env.example, and writes .env only if you asked it to. Put the connection string for the database your app already uses into .env:

title=".env"
DATABASE_URL="postgres://username:password@host:5432/database?sslmode=require"

orm init also writes src/prisma/db.ts, the file your application imports. It builds the Prisma ORM client from the emitted contract and reads the connection string from the environment:

title="src/prisma/db.ts"
import "dotenv/config";

import postgres from "@prisma/orm-postgres/runtime";

import type { Contract } from "./contract.d";

import contractJson from "./contract.json" with { type: "json" };

export const db = postgres<Contract>({

  contractJson,

  url: process.env["DATABASE_URL"]!,

});

The first line is import "dotenv/config", so any script that imports db loads .env for itself. You do not need to pass the URL again at the call site.

This step gives you a starting contract by reading the schema that already exists in PostgreSQL.

Run:

title="bun"
bunx prisma contract infer --output ./src/prisma/contract.prisma
pnpm
pnpm prisma contract infer --output ./src/prisma/contract.prisma
yarn
yarn prisma contract infer --output ./src/prisma/contract.prisma
npm
npx prisma contract infer --output ./src/prisma/contract.prisma

The command writes a first draft of src/prisma/contract.prisma.

Open that file and review it before you go on. This is the moment to clean up model names, keep only the tables you want Prisma ORM to know about first, and make the file easier to read. A model's table is the model name exactly as written, so a table such as User gets a model with no @@map, and a table such as user or user_profile gets @@map with its name. Keep those @@map lines when you rename a model, so the table stays the same.

Once the contract looks right, this step turns it into the generated files the runtime and CLI use.

After you are happy with the contract, run:

bunx prisma contract emit
Bash
pnpm prisma contract emit
Bash
yarn prisma contract emit
Bash
npx prisma contract emit

This refreshes src/prisma/contract.json and src/prisma/contract.d.ts so the runtime and query APIs match the contract you just reviewed.

This refreshes src/prisma/contract.json and src/prisma/contract.d.ts so the runtime and query APIs match the contract you just reviewed.

Record that the live database matches the emitted contract:

bunx prisma db sign
Bash
pnpm prisma db sign
Bash
yarn prisma db sign
Bash
npx prisma db sign

Expected result: Database signed. The command also stores the contract snapshot under migrations/snapshots/ and points the db ref at it, so a later migration plan starts from the schema you just adopted instead of from an empty database.

This step matters in two common cases:

  • the database has never been signed by Prisma ORM before
  • the database was signed earlier, but under an older contract hash

Expected result: Database signed. The command also stores the contract snapshot under migrations/snapshots/ and points the db ref at it, so a later migration plan starts from the schema you just adopted instead of from an empty database.

This step matters in two common cases:

  • the database has never been signed by Prisma ORM before
  • the database was signed earlier, but under an older contract hash

With the database signed, you can test the higher-level API first and confirm Prisma ORM is reading the existing schema correctly.

Create a script.ts file:

title="script.ts"
import { db } from "./src/prisma/db";

async function main() {

  const users = await db.orm.public.User

    .select("id", "email", "name")

    .limit(2)

    .all();

  console.log(users);

  await db.close();

}

main().catch((error) => {

  console.error(error);

  process.exit(1);

});

Run it:

title="bun"
bunx tsx script.ts
pnpm
pnpm dlx tsx script.ts
yarn
yarn dlx tsx script.ts
npm
npx tsx script.ts

After the ORM example, this step shows the lower-level SQL builder against the same existing schema.

The SQL builder names the table, where the ORM API names the model. This example reads a table named User. If your model has @@map("user"), write db.sql.public.user.

Replace script.ts with this version:

title="script.ts"
import { db } from "./src/prisma/db";

async function main() {

  const plan = db.sql.public.User

    .select("id", "email", "name")

    .limit(2)

    .build();

  const rows = await db.runtime().query(plan);

  console.log(rows);

  await db.close();

}

main().catch((error) => {

  console.error(error);

  process.exit(1);

});

Run it again:

title="bun"
bunx tsx script.ts
pnpm
pnpm dlx tsx script.ts
yarn
yarn dlx tsx script.ts
npm
npx tsx script.ts

When you change src/prisma/contract.prisma, emit the contract again:

bunx prisma contract emit
Bash
pnpm prisma contract emit
Bash
yarn prisma contract emit
Bash
npx prisma contract emit

Use db update for a direct development update, or migration plan when you want a checked-in migration.

Put together, adopting the database and making a first schema change is this sequence:

Use db update for a direct development update, or migration plan when you want a checked-in migration.

Put together, adopting the database and making a first schema change is this sequence:

bunx prisma contract infer --output ./src/prisma/contract.prisma

bunx prisma contract emit

bunx prisma db sign

# edit src/prisma/contract.prisma

bunx prisma contract emit

bunx prisma migration plan --name <slug>
Bash
pnpm prisma contract infer --output ./src/prisma/contract.prisma
pnpm prisma contract emit
pnpm prisma db sign
# edit src/prisma/contract.prisma
pnpm prisma contract emit
pnpm prisma migration plan --name <slug>
Bash
yarn prisma contract infer --output ./src/prisma/contract.prisma
yarn prisma contract emit
yarn prisma db sign
# edit src/prisma/contract.prisma
yarn prisma contract emit
yarn prisma migration plan --name <slug>
Bash
npx prisma contract infer --output ./src/prisma/contract.prisma
npx prisma contract emit
npx prisma db sign
# edit src/prisma/contract.prisma
npx prisma contract emit
npx prisma migration plan --name <slug>

The plan starts from the contract db sign recorded, so it contains only your change. Because migrations/app/ is still empty, that first plan also writes a baseline package recording the schema you adopted; see the automatic baseline.

The plan starts from the contract db sign recorded, so it contains only your change. Because migrations/app/ is still empty, that first plan also writes a baseline package recording the schema you adopted; see the automatic baseline.

Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu