Journey from Mongoose to Prisma ORM for MongoDB!
Tags: mongodb, prisma, database, javascript
Published: 2021-07-29 16:51:25

[Prisma](https://www.prisma.io/?utm_source=Prisma%20Ambassador&utm_medium=Blog%20post&utm_campaign=Prisma%20AP%20Somsubhra%20Dass) is a next Generation ORM for NodeJS & TypeScript Environments. It has multiple databases support such as MySQL, SQLite, PostgreSQL, MSSQL and also MongoDB.

![Prisma Application Lifecycle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6asm3c1uhmis3ktx9sih.png)
[Source: Prisma](https://prisma.io)

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

```js
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.

```js
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:

```js
{
  "_id": { "$oid": "60d599cb001ef98000f2cad2" },
  "email": "somsubhra@email.com",
  "name": "Somsubhra",
}
```

So now how to define a model like this in Prisma? Inside **Prisma schema** file, we can define our models.

```js
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:

```js
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);
```

```js
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](https://www.prisma.io/?utm_source=Prisma%20Ambassador&utm_medium=Blog%20post&utm_campaign=Prisma%20AP%20Somsubhra%20Dass) we may also generate ObjectIds manually by using the `bson` package.

```js
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
```js
const user = await prisma.user.findUnique({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
})
```
#### Mongoose
```js
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
```js
const user = await prisma.user.findUnique({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
  select: {
    name: true,
  },
})
```

#### Mongoose
```js
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
```js
const posts = await prisma.user.findUnique({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
  include: {
    post: true,
  },
})
```

#### Mongoose
```js
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
```js
const posts = await prisma.post.findMany({
  where: {
    title: {
      contains: 'Hello World',
    },
  },
})
```

#### Mongoose
```js
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.

```js
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:
```js
const page = prisma.post.findMany({
  before: {
    id: ObjectId('6eb93546f4w5486a04e42401d8'),
  },
  last: 20,
})
```

Offset pagination:
```js
const cc = prisma.post.findMany({
  skip: 200,
  first: 20,
})
```

#### Mongoose

Mongoose also has similar implementation for pagination.

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

### Creating Records

#### Prisma
```js
const user = await prisma.user.create({
  data: {
    email: 'user@email.com',
  },
})
```

#### Mongoose
```js
const user = await User.create({
  name: 'John',
  email: 'user@email.com',
})
```

### 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
```js
const user = await prisma.user.update({
  data: {
    name: 'John',
  },
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
})
```

#### Mongoose
```js
const updatedUser = await User.findOneAndUpdate(
  { _id: id },
  {
    $set: {
      name: 'John',
    },
  }
)
```
### Deleting Single Record

#### Prisma
```js
const user = await prisma.user.delete({
  where: {
    id: ObjectId('5eb9354624286a04e42401d8'),
  },
})
```
#### Mongoose
```js
await User.findByIdAndDelete(id)
```
### Deleting Multiple Records

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

```js
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](https://www.prisma.io/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](https://github.com/prisma/prisma).