Add Prisma ORM to an existing MongoDB database
Add Prisma ORM to an app whose MongoDB database already has collections.
This page uses MongoDB. Use PostgreSQL instead.
On this page you add Prisma ORM to an app whose MongoDB database already has collections. You run orm init, write a contract that describes your collections, and run two queries. The contract is the file that holds your models. In earlier Prisma ORM versions, it was schema.prisma.
Your app must reach its MongoDB database and run on Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer. A single mongod server is enough. You need a replica set only for transactions and change streams, and MongoDB Atlas already runs one.
If your database has no collections yet, follow Add Prisma ORM and MongoDB to an existing app instead, which creates the collections for you. If you want Prisma ORM to create a new app for you, follow Create a new app with MongoDB.
Prisma ORM 8 is 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.
1. Install tsx
The scripts on this page run with tsx. If your project does not have it, install it:
bun add --dev tsx typescript2. Initialize Prisma ORM
The orm init command below changes your tsconfig.json and your package.json. In tsconfig.json, it sets module to preserve and moduleResolution to bundler. If your package.json has no "type" field, it adds "type": "module". If it has "type": "commonjs", the command keeps it and prints a warning. If your app runs as CommonJS, for example because it loads files with require or because tsc compiles it to require calls, follow In a CommonJS project before you start your app again. tsx runs the scripts on this page in both kinds of app, so you can finish this page first.
From the root of your project, run:
bunx prisma@latest orm init --yes --target mongodb --authoring psl --write-envprisma@latest runs Prisma ORM 8, and --target mongodb picks MongoDB. --authoring psl picks PSL, the Prisma Schema Language, which is the .prisma file format you know from earlier Prisma ORM versions. --write-env writes a .env file, unless you already have one. --yes accepts the default answer to every question, so the command asks nothing.
The command installs the Prisma ORM packages and writes its files. You use three of them on this page:
src/prisma/contract.prisma, an example contract. In step 4 you change it to describe your collections.src/prisma/db.ts, the file your code imports to run queries..env, which holds the connection string.
The command also writes prisma-8.md, a short reference for writing queries. You do not need it for this page.
From now on, every prisma command ends with the line Prisma agent skills are out of date. You can ignore it, because it does not change what the command does. Agent skills are instruction files that AI coding tools read, and the line appears because your project has none yet. To add them and stop the line, run npx prisma@latest init. In Prisma ORM 8, init adds the skills and a postinstall script that keeps them up to date, and it leaves your contract and your .env alone. orm init is the command that sets up Prisma ORM.
3. Set your database connection string
If you had no .env, orm init wrote one with a placeholder, DATABASE_URL="mongodb://user:password@localhost:27017/mydb". Set DATABASE_URL in .env to the connection string of the database your app already uses:
DATABASE_URL="mongodb://username:password@host:27017/database"The part after the last /, and before any ?, is the name of the database.
orm init wrote src/prisma/db.ts, and you do not need to change it:
import 'dotenv/config';
import mongo from '@prisma/orm-mongo/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };
export const db = mongo<Contract>({
contractJson,
url: process.env['DATABASE_URL']!,
});The first line loads .env, so every file that imports db reads DATABASE_URL from there. The two contract files it imports are written by contract emit in step 5.
4. Describe your collections in the contract
Prisma ORM cannot read a MongoDB database to write the contract for you, so you write it yourself. Describe only the collections that your code reads and writes through Prisma ORM. The other collections stay as they are.
The example contract in src/prisma/contract.prisma describes a users collection and a posts collection:
// use prisma-8
model User {
id ObjectId @id @map("_id")
email String @unique
username String?
name String?
posts Post[]
@@map("users")
}
model Post {
id ObjectId @id @map("_id")
title String
content String?
author User @relation(fields: [authorId], references: [id])
authorId ObjectId
@@map("posts")
}Keep the first line, because contract emit reads only .prisma files that start with it. @@map("users") names the collection that stores the model. MongoDB stores the identifier of every document in _id. On MongoDB, contract emit names each field in contract.json by the name in its @map, so in queries and in results you use _id, not id. The same goes for any other field with @map.
Change the models to match your documents: one model for each collection, with one field for each field of its documents. Data modeling lists the field types.
5. Generate the files your code imports
contract emit takes the place of prisma generate, and you run it after every change to the contract:
bunx prisma contract emitThe command writes src/prisma/contract.json and src/prisma/contract.d.ts, and db.ts imports both of them. Commit them, because your app cannot run without them.
6. Query a collection with db.orm
db.orm queries your models, the way Prisma Client did in earlier Prisma ORM versions. On MongoDB you reach a model by the name of its collection, which is the name in @@map. So the User model is db.orm.users.
Create script.ts with the code below, and put your own names in it: replace users with the name of one of your collections, and replace email and existing@example.com with a field and a value from its documents.
import { db } from "./src/prisma/db";
async function main() {
const user = await db.orm.users.where({ email: "existing@example.com" }).first();
console.log(user);
await db.close();
}
main().catch((error) => {
console.error(error);
process.exit(1);
});Run it:
bunx tsx script.ts{
username: null,
_id: '6abca6a1e7a9bb8d8c716f65',
email: 'existing@example.com',
name: 'Existing User'
}The document has no username field, and username is optional in the contract, so the result shows it as null. If no document matches, the script prints null.
7. Query a collection with a pipeline
db.query builds a MongoDB aggregation pipeline on a collection, which it names as it is stored in MongoDB. .build() returns the pipeline, and runtime.query() runs it, where runtime is the object that await db.runtime() returns. Replace users, email, and the value with your own again.
Replace script.ts with this version:
import { db } from "./src/prisma/db";
async function main() {
const runtime = await db.runtime();
const query = db.query
.from("users")
.match((fields) => fields.email.eq("existing@example.com"))
.project("email", "name")
.build();
const rows = await runtime.query(query);
console.log(rows);
await db.close();
}
main().catch((error) => {
console.error(error);
process.exit(1);
});Run it again:
bunx tsx script.ts[
{
email: 'existing@example.com',
name: 'Existing User',
_id: '6abca6a1e7a9bb8d8c716f65'
}
]8. Next steps
When you change src/prisma/contract.prisma, run npx prisma contract emit again.
You can read and write documents in collections that already exist without a migration. Use migration plan when you want Prisma ORM to create or change collections and indexes.
