PostgreSQL
Add Prisma ORM to an existing PostgreSQL project.
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.
Prisma ORM 8 is the current release, as a release candidate. Prisma ORM 7 remains fully supported; its docs live at /orm/v7 and its setup paths at /v7/getting-started.
For what release candidate means, when the final release is expected, and how to stay on version 7, see Release status. For the Prisma ORM 8 name of every Prisma ORM 7 API, see Coming from Prisma ORM 7.
1. Make sure you can run the example script
If your project already runs TypeScript scripts, you can skip this step.
Otherwise, install the script tooling:
bun add --dev tsx typescriptLater, orm init will also add the Node.js types it needs and make sure the generated Prisma ORM files can run as ES modules. If your project already declares "type": "commonjs", Prisma ORM leaves that choice alone and prints a warning so you can decide how to wire the generated helper into your app.
2. Initialize Prisma ORM
From the root of your existing project, run:
bunx prisma@latest orm init --target postgresThis is the existing-project path. It preselects PostgreSQL, adds Prisma ORM files and package scripts to the app you already have, and does not scaffold a new framework 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-pathif 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-envskips the prompt and writes the file.
3. Set your database connection string
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:
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:
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.
4. Infer a starter contract from the live database
This step gives you a starting contract by reading the schema that already exists in PostgreSQL.
Run:
bunx prisma@latest contract infer --output ./src/prisma/contract.prismaThe 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.
contract infer maps timestamp columns to Timestamp and timestamptz columns to Timestamptz. Both read and write their values through the global Temporal API. Node.js 26 ships it enabled by default (check with node -p "typeof Temporal", which prints object when it is there); on Node.js 24 and earlier, reading such a column fails with RUNTIME.TEMPORAL_UNAVAILABLE unless you install temporal-polyfill and add import "temporal-polyfill/full/global"; before the first query. If you would rather not add the polyfill, change the field's type in the contract to TimestamptzString and read and write PostgreSQL's own text instead.
5. Emit the generated artifacts
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@latest contract emitThis refreshes src/prisma/contract.json and src/prisma/contract.d.ts so the runtime and query APIs are aligned with the contract you just reviewed.
6. Sign the database
Record that the live database matches the emitted contract:
bunx prisma@latest db signThis 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
7. Run a simple high-level query
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:
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:
bunx tsx script.ts8. Run a simple low-level query
After the ORM example, this step shows the lower-level SQL builder against the same existing schema.
Replace script.ts with this version:
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:
bunx tsx script.ts9. Next steps
When you change src/prisma/contract.prisma, emit the contract again:
bunx prisma@latest contract emitUse db update for a direct development update, or migration plan when you want a checked-in migration.
