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.

Connection

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.

Defining Models

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();

Queries & Database Operations

Now let's understand how to write queries and operations to MongoDB using Prisma in comparison to Mongoose.

Fetching Single record

Fetching a single record in prisma is done using the where property but in mongoose it has findById method.

Prisma

const user = await prisma.user.findUnique({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
})

Mongoose

const result = await User.findById('5eb9354624286a04e42401d8')

Fetching selected values for single record

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.

Prisma

const user = await prisma.user.findUnique({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
  select: {
    name: true,
  },
})

Mongoose

const user = await User.findById('5eb9354624286a04e42401d8').select(['name', 'email'])

Fetching relations

In Prisma, we use the include property but in Mongoose we would have to use the populate() method.

Prisma

const posts = await prisma.user.findUnique({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
  include: {
    post: true,
  },
})

Mongoose

const userWithPosts = await User.findById(id).populate('posts')

Filtering with values

In Prisma we filter records using the where property whereas in Mongoose we use the find().

Prisma

const posts = await prisma.post.findMany({
  where: {
    title: {
      contains: 'Hello World',
    },
  },
})

Mongoose

const post = await Post.find({
  title: 'Hello World',
})

Relation Filtering

Prisma

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

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.

Pagination

Prisma

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

Mongoose also has similar implementation for pagination.

const posts = await Post.find({
  skip: 5,
  limit: 10,
})

Creating Records

Prisma

const user = await prisma.user.create({
  data: {
    email: '[email protected]',
  },
})

Mongoose

const user = await User.create({
  name: 'John',
  email: '[email protected]',
})

Updating Records

Prisma updates the record directly with the values passed in data property in comparison with mongoose where we need to use $set operator.

Prisma

const user = await prisma.user.update({
  data: {
    name: 'John',
  },
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
})

Mongoose

const updatedUser = await User.findOneAndUpdate(
  { _id: id },
  {
    $set: {
      name: 'John',
    },
  }
)

Deleting Single Record

Prisma

const user = await prisma.user.delete({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
})

Mongoose

await User.findByIdAndDelete(id)

Deleting Multiple Records

Prisma

const users = await prisma.user.deleteMany({
  where: {
    id: {
      in: [1, 2, 6, 34],
    },
  },
})

Mongoose

await User.deleteMany({ _id: { $in: [1, 2, 6, 34] } })

Advantages of Prisma over Mongoose

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.

Disadvantages of Prisma

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.

Should you be using Prisma over Mongoose?

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".

Contribute to Prisma

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