# Multiple databases (/docs/guides/database/multiple-databases) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Connect one Next.js app to two PostgreSQL databases with Prisma ORM: one contract, config, and client per database, selected with the --config flag. Location: Guides > Database > Multiple databases ## Introduction [#introduction] This guide shows you how to use two databases from one [Next.js](https://nextjs.org/) app with Prisma ORM. You give each database its own data contract, its own `prisma.config.ts`, and its own Prisma ORM client, then render data from both on one page. The same layout works for any number of databases and is a good fit for multi-tenant apps or for keeping unrelated data in separate databases. Every command and code block below was run against two local PostgreSQL databases, `users` and `posts`. > [!NOTE] > Using Prisma ORM 7? > > Prisma ORM 8 is the current release. Prisma ORM 7 remains fully supported; the Prisma ORM 7 version of this guide is at [/guides/v7/database/multiple-databases](https://www.prisma.io/docs/guides/v7/database/multiple-databases). ## Prerequisites [#prerequisites] * [Node.js](https://nodejs.org) 24 or later * Two PostgreSQL connection strings, or nothing at all: `npx create-db@latest` can create a [Prisma Postgres](https://www.prisma.io/docs/postgres) database for you, run it twice for two * A [Vercel](https://vercel.com/) account if you want to deploy at the end ## Use with your agent [#use-with-your-agent] To delegate this guide to your coding agent, copy the prompt below and hand it over: ```text Create a Next.js app that reads from two PostgreSQL databases with Prisma ORM, following https://www.prisma.io/docs/guides/database/multiple-databases.md. 1. Scaffold with `npx create-next-app@latest my-multi-db-app --yes`, then in the project run `npx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma`. Run `npx prisma@latest init` so the Prisma agent skills are installed, and use them. 2. Rename `prisma.config.ts` to `prisma.users.config.ts`, point it at `USERS_DATABASE_URL` and `./prisma/users/migrations`, and reduce `prisma/users/contract.prisma` to a `User` model. Create the same three files for the posts database: `prisma.posts.config.ts` reading `POSTS_DATABASE_URL`, `prisma/posts/contract.prisma` with a `Post` model that stores `authorId Int` (no relation across databases), and `prisma/posts/db.ts`. Export `usersDb` and `postsDb` from the two `db.ts` files. 3. Put both connection strings in `.env` (use the ones I give you, or create two Prisma Postgres databases with `npx create-db@latest` and show me the claim URLs). Add package scripts that run `prisma contract emit`, `prisma db init`, and `prisma db verify` once per config with `--config`, then run them. 4. Write a seed script that inserts two users and two posts, run it with `node prisma/seed.ts`, and rewrite `app/page.tsx` as a server component that lists users from `usersDb` and posts from `postsDb`, joining authors in application code. 5. Start `npm run dev` in the background, verify http://localhost:3000 renders both lists, stop the server, and confirm `npm run build` passes. ``` ## 1. Set up a Next.js project [#1-set-up-a-nextjs-project] Create a new Next.js app and accept the defaults (TypeScript, Tailwind CSS, App Router, no `src` directory): #### bun ```bash bunx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` #### pnpm ```bash pnpm dlx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` #### yarn ```bash yarn dlx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` #### npm ```bash npx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` This guide starts from `create-next-app` and adds Prisma ORM with `orm init` instead of using the `next` template of `create-prisma`. That template wires a single database and declares it for Prisma Compute; here you want exactly the Prisma files, once per database, and nothing else. ## 2. Add Prisma ORM for the users database [#2-add-prisma-orm-for-the-users-database] Run `orm init` from the project root. The `--schema-path` flag puts the contract in a folder named after the database, and `orm init` places the client file next to it: #### bun ```bash bunx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` #### pnpm ```bash pnpm dlx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` #### yarn ```bash yarn dlx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` #### npm ```bash npx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` The command installs `@prisma/orm-postgres` and `dotenv`, adds `prisma` as a dev dependency, sets `"type": "module"` in `package.json`, updates `tsconfig.json`, and writes these files: ```text no-copy prisma/users/contract.prisma prisma/users/db.ts prisma.config.ts prisma-8.md .env.example ``` There is no `prisma generate` and no generated client package. The emitted `contract.json` and `contract.d.ts` next to the contract are what the runtime and the type checker read. ### 2.1. Give the config a database-specific name [#21-give-the-config-a-database-specific-name] Prisma ORM commands read `./prisma.config.ts` by default and take a different file with the global [`--config`](https://www.prisma.io/docs/cli/global-flags) flag. Rename the generated config so each database has one: ```bash mv prisma.config.ts prisma.users.config.ts ``` Point it at the users contract, a users-only migrations folder, and a `USERS_DATABASE_URL` variable: ```typescript title="prisma.users.config.ts" import 'dotenv/config'; import { definePrismaConfig } from '@prisma/cli-engine'; import { defineConfig as ormConfig } from '@prisma/orm-postgres/config'; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/users/contract.prisma", migrations: { // [!code ++] dir: "./prisma/users/migrations", // [!code ++] }, // [!code ++] db: { connection: process.env.USERS_DATABASE_URL!, // [!code ++] }, }), }); ``` `migrations.dir` matters once you use checked-in migrations: without it both configs would write to the same `./migrations` folder. ### 2.2. Define the users contract [#22-define-the-users-contract] Replace the starter contract with a single `User` model: ```prisma title="prisma/users/contract.prisma" // use prisma-8 model User { id Int @id @default(autoincrement()) email String @unique name String? createdAt TimestamptzString @default(now()) } ``` ### 2.3. Name the client after the database [#23-name-the-client-after-the-database] Change `prisma/users/db.ts` to read `USERS_DATABASE_URL` and export a name you can tell apart from the second client: ```typescript title="prisma/users/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 usersDb = postgres({ // [!code ++] contractJson, url: process.env.USERS_DATABASE_URL!, // [!code ++] }); ``` `contract.d.ts` and `contract.json` do not exist yet; you emit them in step 4. ## 3. Add the posts database [#3-add-the-posts-database] The second database needs the same three files. Create them by hand; running `orm init` again would replace the files from step 2. ```bash mkdir -p prisma/posts ``` ```typescript title="prisma.posts.config.ts" import 'dotenv/config'; import { definePrismaConfig } from '@prisma/cli-engine'; import { defineConfig as ormConfig } from '@prisma/orm-postgres/config'; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/posts/contract.prisma", migrations: { dir: "./prisma/posts/migrations", }, db: { connection: process.env.POSTS_DATABASE_URL!, }, }), }); ``` ```prisma title="prisma/posts/contract.prisma" // use prisma-8 model Post { id Int @id @default(autoincrement()) title String content String? authorId Int createdAt TimestamptzString @default(now()) } ``` `authorId` is a plain integer, not a relation. A contract describes one database, and PostgreSQL cannot enforce a foreign key into another database, so the link between a post and its author is resolved in application code in step 5. ```typescript title="prisma/posts/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 postsDb = postgres({ contractJson, url: process.env.POSTS_DATABASE_URL!, }); ``` Now set both connection strings. Both config files and both clients load `.env` through `dotenv/config`, and Next.js loads it as well: ```bash title=".env" USERS_DATABASE_URL="postgres://user:password@localhost:5432/users" POSTS_DATABASE_URL="postgres://user:password@localhost:5432/posts" ``` `orm init` already added `.env` to `.gitignore`. Update `.env.example` with the two variable names so collaborators know what to set. ## 4. Emit the contracts and initialize both databases [#4-emit-the-contracts-and-initialize-both-databases] Each Prisma ORM command works on one config. Add scripts that run every command once per database so you do not have to type `--config` twice: ```json title="package.json" "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "contract:emit": "prisma contract emit --config ./prisma.users.config.ts && prisma contract emit --config ./prisma.posts.config.ts", // [!code ++] "db:init": "prisma db init --config ./prisma.users.config.ts && prisma db init --config ./prisma.posts.config.ts", // [!code ++] "db:verify": "prisma db verify --config ./prisma.users.config.ts && prisma db verify --config ./prisma.posts.config.ts", // [!code ++] "db:update": "prisma db update --config ./prisma.users.config.ts && prisma db update --config ./prisma.posts.config.ts", // [!code ++] "db:seed": "node prisma/seed.ts", // [!code ++] "prebuild": "npm run contract:emit" // [!code ++] } ``` `prebuild` re-emits both contracts before every `next build`, locally and on Vercel, so the build never runs against stale artifacts. Emit the contract artifacts for both databases. This step is offline: #### bun ```bash bun run contract:emit ``` #### pnpm ```bash pnpm run contract:emit ``` #### yarn ```bash yarn contract:emit ``` #### npm ```bash npm run contract:emit ``` ```text no-copy ✔ Emitted contract.json and contract.d.ts │ contract: prisma/users/contract.json │ types: prisma/users/contract.d.ts ✔ Emitted contract.json and contract.d.ts │ contract: prisma/posts/contract.json │ types: prisma/posts/contract.d.ts ``` Create the tables in both databases and sign each one with its contract: #### bun ```bash bun run db:init ``` #### pnpm ```bash pnpm run db:init ``` #### yarn ```bash yarn db:init ``` #### npm ```bash npm run db:init ``` ```text no-copy │ contract: prisma/users/contract.json │ database: postgres://****@localhost:5432/users ✔ Applied 2 operation(s) across 1 contract space App space ├─ Create table "User" ├─ Add unique constraint on "User" (email) └─ marker f340195186d0acf8e9914097e58af0375650e7251358382aa9ad3ed5b724daf7 │ contract: prisma/posts/contract.json │ database: postgres://****@localhost:5432/posts ✔ Applied 1 operation(s) across 1 contract space App space ├─ Create table "Post" └─ marker a0c012793cc392569087d82d7ea41686c8e60586b63c7a131391b99717157804 ``` Each database now carries a marker with the hash of the contract it was initialized from. Confirm both match: #### bun ```bash bun run db:verify ``` #### pnpm ```bash pnpm run db:verify ``` #### yarn ```bash yarn db:verify ``` #### npm ```bash npm run db:verify ``` ```text no-copy │ contract: prisma/users/contract.json │ database: postgres://****@localhost:5432/users ✔ Database marker and schema match contract │ contract: prisma/posts/contract.json │ database: postgres://****@localhost:5432/posts ✔ Database marker and schema match contract ``` The marker is also what protects you from crossing the wires. Verifying the users contract against the posts database stops immediately: #### bun ```bash bunx prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` #### yarn ```bash yarn prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` #### npm ```bash npx prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` ```text no-copy ✘ [CONTRACT.MARKER_MISMATCH] Hash mismatch why: Contract storageHash does not match database marker ``` ## 5. Query both databases from the app [#5-query-both-databases-from-the-app] ### 5.1. Seed both databases [#51-seed-both-databases] Create a script that writes through both clients. Node.js 24 runs TypeScript directly, so no extra tooling is needed: ```typescript title="prisma/seed.ts" import { usersDb } from "./users/db.ts"; import { postsDb } from "./posts/db.ts"; async function main() { const alice = await usersDb.orm.public.User.create({ email: "alice@prisma.io", name: "Alice", }); const bob = await usersDb.orm.public.User.create({ email: "bob@prisma.io", name: "Bob", }); await postsDb.orm.public.Post.create({ title: "Hello from the posts database", content: "This row lives in the posts database.", authorId: alice.id, }); await postsDb.orm.public.Post.create({ title: "Two databases, one app", content: null, authorId: bob.id, }); console.log("Seeded", alice.email, "and", bob.email, "with one post each."); await usersDb.close(); await postsDb.close(); } main().catch((error) => { console.error(error); process.exit(1); }); ``` The `.ts` extensions in the imports are what Node.js needs to resolve the files. Allow them in `tsconfig.json`, otherwise `next build` fails its type check with `TS5097`: ```json title="tsconfig.json" { "compilerOptions": { "allowImportingTsExtensions": true // [!code ++] } } ``` Run the seed: #### bun ```bash bun run db:seed ``` #### pnpm ```bash pnpm run db:seed ``` #### yarn ```bash yarn db:seed ``` #### npm ```bash npm run db:seed ``` ```text no-copy Seeded alice@prisma.io and bob@prisma.io with one post each. ``` Both clients open their connection pool on the first query. The script closes them at the end so the process can exit. ### 5.2. Render data from both databases [#52-render-data-from-both-databases] Replace `app/page.tsx` with a server component that queries each client and joins posts to their authors in memory: ```tsx title="app/page.tsx" import { usersDb } from "@/prisma/users/db"; import { postsDb } from "@/prisma/posts/db"; export const dynamic = "force-dynamic"; export default async function Home() { const users = await usersDb.orm.public.User.select("id", "email", "name").all(); const posts = await postsDb.orm.public.Post .select("id", "title", "authorId") .orderBy((p) => p.id.asc()) .all(); const authorById = new Map(users.map((u) => [u.id, u])); return (

Multi-database showcase

Users come from one PostgreSQL database, posts from another.

Users

    {users.map((user) => (
  • {user.name} ({user.email})
  • ))}

Posts

    {posts.map((post) => (
  • {post.title}, by {authorById.get(post.authorId)?.name ?? "unknown"}
  • ))}
); } ``` Each client is typed by its own `contract.d.ts`: `usersDb.orm.public` only knows `User`, `postsDb.orm.public` only knows `Post`, and mixing them up is a type error. `force-dynamic` keeps Next.js from trying to render the page at build time, when no database is reachable. ### 5.3. Run the development server [#53-run-the-development-server] #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` ```text no-copy ▲ Next.js 16.3.4 (Turbopack) - Local: http://localhost:3000 - Environments: .env ✓ Ready in 894ms ``` Open [http://localhost:3000](http://localhost:3000). The page lists Alice and Bob under **Users** and the two posts under **Posts**, each attributed to its author. One request, two databases, no adapter or extra client package in between. Finally, confirm the production build passes. `prebuild` emits both contracts first: #### bun ```bash bun run build ``` #### pnpm ```bash pnpm run build ``` #### yarn ```bash yarn build ``` #### npm ```bash npm run build ``` ```text no-copy > prisma contract emit --config ./prisma.users.config.ts && prisma contract emit --config ./prisma.posts.config.ts ... ✓ Compiled successfully ``` ## 6. Deploy to Vercel [#6-deploy-to-vercel] The app needs two environment variables in production and nothing else that is Prisma-specific: `prebuild` runs on Vercel, and there is no client to generate and no engine binary to ship. 1. Push the project to a GitHub repository. If you do not have one yet, [create one on GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository), then run: ```bash git add . git commit -m "Next.js app with two Prisma 8 databases" git branch -M main git remote add origin https://github.com//.git git push -u origin main ``` 2. In the [Vercel dashboard](https://vercel.com/), follow [Import an existing project](https://vercel.com/docs/getting-started-with-vercel/import) and stop at the step where you configure the project, before clicking **Deploy**. 3. Expand **Environment variables** and add both connection strings: * **Key**: `USERS_DATABASE_URL`, **Value**: the users database connection string from your `.env` * **Key**: `POSTS_DATABASE_URL`, **Value**: the posts database connection string from your `.env` 4. Click **Deploy**. Vercel runs `npm run build`, which emits both contracts and builds the app. > [!WARNING] > Set both variables before the first deploy. Each config file reads its variable with a non-null assertion, and a missing one fails the first request to the page. Open the live URL. The page renders the same two lists from the same two databases. The Vercel deploy was not run while validating this guide; the `npm run build` it executes is the one from step 5. ## Common gotchas [#common-gotchas] > [!WARNING] > Commands without `--config` fail once the default config is gone. Running `npx prisma contract emit` with no `./prisma.config.ts` (or with one that has no `orm` section) stops with `[CONFIG.FILE_NOT_FOUND]` and a hint that the `orm` config section is absent. Use the package scripts from step 4, or pass `--config` explicitly. * **No relations across databases.** A contract covers one database. Keep a plain id column such as `authorId` on the side that references the other database, and join in application code. * **`npx prisma@latest init` writes a third config.** It creates a `prisma.config.ts` that holds only the `skills` section for agent skills. That is expected; the database commands keep using `--config` with the two database configs. * **Do not close a client per request.** In `page.tsx` and route handlers, never call `usersDb.close()`. The pools are shared across requests and close when the process exits. Only scripts such as `prisma/seed.ts` close them. * **`orm init` picks the package manager from your lockfile.** Run it after `create-next-app` has written `package-lock.json`, `pnpm-lock.yaml`, or `bun.lock`, or it may install with a different package manager than the one you use. ## Prompt your coding agent [#prompt-your-coding-agent] Run [`npx prisma@latest init`](https://www.prisma.io/docs/cli/init) once to install the [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) for your coding agent and keep them matching your installed packages. Prompts that map to this guide: * "Using the prisma-8 skill, add a `Comment` model to `prisma/posts/contract.prisma`, emit with `--config ./prisma.posts.config.ts`, and update the posts database." * "Add a third database for billing following the same layout: `prisma.billing.config.ts`, `prisma/billing/contract.prisma`, `prisma/billing/db.ts`, and extend the package scripts." * "Write a route handler that creates a post for the signed-in user, validating that the `authorId` exists in the users database first." ## Next steps [#next-steps] * Change a contract, then run `npm run contract:emit` and `npm run db:update` to apply the change to both databases in development. For checked-in migrations, run [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) and [`db migrate`](https://www.prisma.io/docs/cli/db-migrate) with the `--config` of the database you changed; each writes to its own `migrations.dir`. * [Learn the fundamentals](https://www.prisma.io/docs/orm/fundamentals/reading-data): filtering, sorting, pagination, and writes. * [CLI configuration](https://www.prisma.io/docs/cli/configuration) covers everything `prisma.config.ts` can hold. * Splitting the app itself into packages? See [pnpm workspaces](https://www.prisma.io/docs/guides/deployment/pnpm-workspaces) and [Turborepo](https://www.prisma.io/docs/guides/deployment/turborepo). ## Related pages - [`Expand-and-contract migrations`](https://www.prisma.io/docs/guides/database/data-migration): Replace a column without downtime using the expand and contract pattern, with the data backfill inside a Prisma ORM migration. - [`Schema management in teams`](https://www.prisma.io/docs/guides/database/schema-changes): Plan, merge, and apply Prisma ORM migrations when several developers change the schema at the same time.