18
Journey from Mongoose to Prisma ORM for MongoDB!
So in this post let's talk about the all new Prisma MongoDB Connector, it's operations and what made me switch from Mongoose to Prisma for MongoDB.
Let's start by establishing the connection to our MongoDB Server. In your Prisma schema file we need to change the provider
.
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
The prisma.schema
file allows us to specify how we want Prisma to connect to our database. We need to tell it what kind of provider we would like to use - in this case mongodb
- and a url
to connect to - this is pointing to an environment variable as we want to keep it secret. We will use a MongoDB connection string as the DATABASE_URL
which can be found in the /prisma/.env
file.
Next we need to setup the generator block like below.
generator client {
provider = "prisma-client-js"
previewFeatures = ["mongoDb"]
}
Since Prisma MongoDB Connector is still in preview we need to explicitly specify the previewFeatures
key.
So now that connection has been successfully established to MongoDB Server, let's now create models for our database collection.
A typical MongoDB document looks like this:
{
"_id": { "$oid": "60d599cb001ef98000f2cad2" },
"email": "[email protected]",
"name": "Somsubhra",
}
So now how to define a model like this in Prisma? Inside Prisma schema file, we can define our models.
model User {
id String @id @default(dbgenerated()) @map("_id") @db.ObjectId
email String @unique
name String?
posts Post[]
}
model Post {
id String @id @default(dbgenerated()) @map("_id")
published Boolean @default(false)
title String
user User? @relation(fields: [userId], references: [id])
userId String?
}
Comparing it with mongoose models, in Mongoose ODM we would have written something like:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: String,
email: {
type: String,
unique: true,
},
});
module.exports = User = mongoose.model("user", UserSchema);
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const PostSchema = new Schema({
title: String,
user: {
type: mongoose.Types.ObjectId,
ref: "user",
},
published: {
type: Boolean
default: false,
},
});
module.exports = Post = mongoose.model("post", PostSchema);
In Prisma we may also generate ObjectIds manually by using the bson
package.
import { ObjectId } from "bson";
const id = new ObjectId();
Now let's understand how to write queries and operations to MongoDB using Prisma in comparison to Mongoose.
Fetching a single record in prisma is done using the where
property but in mongoose it has findById
method.
const user = await prisma.user.findUnique({
where: {
id: ObjectId('5eb9354624286a04e42401d8'),
},
})
const result = await User.findById('5eb9354624286a04e42401d8')
Fetching selected values for single record is easier than ever in Prisma ORM by using just a single query function to do the select operation whereas in Mongoose after finding the record we need to chain the output with select()
. This increases the time complexity and also slows down the process.
const user = await prisma.user.findUnique({
where: {
id: ObjectId('5eb9354624286a04e42401d8'),
},
select: {
name: true,
},
})
const user = await User.findById('5eb9354624286a04e42401d8').select(['name', 'email'])
In Prisma, we use the include
property but in Mongoose we would have to use the populate()
method.
const posts = await prisma.user.findUnique({
where: {
id: ObjectId('5eb9354624286a04e42401d8'),
},
include: {
post: true,
},
})
const userWithPosts = await User.findById(id).populate('posts')
In Prisma we filter records using the where
property whereas in Mongoose we use the find()
.
const posts = await prisma.post.findMany({
where: {
title: {
contains: 'Hello World',
},
},
})
const post = await Post.find({
title: 'Hello World',
})
Prisma can filter a list based on a criteria that applies not only to the models of the list being retrieved, but to a relation of that model.
const posts = await prisma.user.findMany({
where: {
posts: {
some: {
title: {
contains: 'Hello',
},
},
},
},
})
Mongoose doesn't offer this feature for relation filters. We may achieve similar functionality by adding an additional step to filter the results returned by the query but that would result in increased query times and load for large databases.
Cursor-style pagination:
const page = prisma.post.findMany({
before: {
id: ObjectId('6eb93546f4w5486a04e42401d8'),
},
last: 20,
})
Offset pagination:
const cc = prisma.post.findMany({
skip: 200,
first: 20,
})
Mongoose also has similar implementation for pagination.
const posts = await Post.find({
skip: 5,
limit: 10,
})
const user = await prisma.user.create({
data: {
email: '[email protected]',
},
})
const user = await User.create({
name: 'John',
email: '[email protected]',
})
Prisma updates the record directly with the values passed in data property in comparison with mongoose where we need to use $set
operator.
const user = await prisma.user.update({
data: {
name: 'John',
},
where: {
id: ObjectId('5eb9354624286a04e42401d8'),
},
})
const updatedUser = await User.findOneAndUpdate(
{ _id: id },
{
$set: {
name: 'John',
},
}
)
const user = await prisma.user.delete({
where: {
id: ObjectId('5eb9354624286a04e42401d8'),
},
})
await User.findByIdAndDelete(id)
const users = await prisma.user.deleteMany({
where: {
id: {
in: [1, 2, 6, 34],
},
},
})
await User.deleteMany({ _id: { $in: [1, 2, 6, 34] } })
So now that we know the differences in operations between Prisma & Mongoose, let's now focus on the advantages Prisma provides over Mongoose.
- Prisma allows TypeSafe Database access.
- Prisma has an auto generated query builder
- Support for multiple databases. This is a huge advantage to developers when moving between SQL and NoSQL Databases, since only the Schema file needs to be changed. All other operations/queries remain same.
- Supports multiple RDBMS
- Prisma lets you filter a list based on a criteria that applies not only to the models of the list being retrieved, but to a relation of that model. Mongoose doesn't offer a dedicated API for relation filters. You can get similar functionality by adding an additional step to filter the results returned by the query.
- Prisma Studio tool that helps to manage data easily.
On the other side, Prisma has a few disadvantages over Mongoose as well.
- Support for multiple model files not available. All models need to be written in
schema.prisma
file which makes the file cluttered and hard to debug and read. - Prisma MongoDB support is currently in preview
- Currently no embedded collection support.
- Error handling is incomplete.
- The Migrate and Introspection workflows are currently not supported.
-
@@id
and auto-increment are not currently supported.
Prisma is a modern ORM which has its own tradeoffs. If you are building a server side application with REST APIs and GraphQL, Prisma would be a great choice. It also makes lives of developers easier. Prisma gives a significant productivity boost for the most common database workflows.
If these factors don't matter much to you and you want to have more control over database operations then my suggestion will be to go with Mongoose "for the time being".
Prisma MongoDB Connector is still in preview and development. If you want to contribute to the Prisma check out their GitHub Repository by clicking here.
18