---
title: Bitovi Academy - Connecting Apollo Server with MongoDB
description: "In this guide we will be working off of a starting point [project](https://github.com/bitovi/node-graphql-tutorial-2023) and will be removing the need for static data, while adding a connection to a live database! 
We’ll also go over some basic methods to perform CRUD operations, and how we will pull related entities from our database (what is known as joins in SQL) using the `populate` function in Mongoose. We’ll also create a small seed file in order to fill our database with some baseline information."
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us) 
    - [ Setting Up a GraphQL Server with Apollo ](https://www.bitovi.com/academy/learn-graphql/setting-up-apollo.html?hsLang=en-us)
    - [ Connecting Apollo Server with MongoDB ](https://www.bitovi.com/academy/learn-graphql/adding-mongodb.html?hsLang=en-us)
    - [ Converting Mongoose to Prisma ](https://www.bitovi.com/academy/learn-graphql/porting-to-prisma.html?hsLang=en-us)
    - [ Testing Node.js and GraphQL with Jest ](https://www.bitovi.com/academy/learn-graphql/adding-testing.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Connecting Apollo Server with MongoDB `page`

In this guide we will be working off of a starting point [project](https://github.com/bitovi/node-graphql-tutorial-2023) and will be removing the need for static data, while adding a connection to a live database! We’ll also go over some basic methods to perform CRUD operations, and how we will pull related entities from our database (what is known as joins in SQL) using the `populate` function in Mongoose. We’ll also create a small seed file in order to fill our database with some baseline information.

## Setting up MongoDB in the cloud

Rather than host a local instance of MongoDB, we can instead use **MongoDB Atlas** and leverage their free-tier option in order to set up a MongoDB instance. I would recommend to follow the first 5 minutes of the video to get your account and your [cluster setup](https://www.youtube.com/watch?v=xrc7dIO_tXk&ab_channel=MongoDB). After this video I would suggest to keep your connection uri so that we can begin to use it in our project.

### Dependencies

First things first, we’ll need to install some packages that we’ll be going to use:

```
npm i mongodb mongoose dotenv
```

We are deciding to use `dotenv` so that we can put our connection uri inside a file .env at the highest level of the directory. And this file won’t be committed to any repository so that we don’t share our precious connection uri! We’ll add the following line to the `.env` file:

`MONGODB_URL="mongodb+srv://<your_connection_uri_here>"`

## Mongoose vs MongoDB Native Driver:

In this tutorial we’ll be going with Mongoose as our package of choice, but let’s quickly go over our options on why we might use one or the other. For us, Mongoose provides a nice way to create a Model of any entity we want to refer to, and when we want to create a new document using that Model, we get inherent model validation out of the box. Rather than having to include document validation ourselves within our resolvers or dataSource files, Mongoose will throw an error if an incorrect field is placed during a `save` operation. You can also create and manage relationships data relatively quickly by making the schema strict (in terms of validation), and using the `populate` method. Some drawbacks are that Mongoose can hide some complexity of what it is doing away from developers making it really difficult to understand complex concepts.

The MongoDB native driver is definitely more performant than Mongoose and a lot of methods between the two are fairly similar in syntax. However you might have to create some boilerplate validation code since MongoDB will allow you store a document that doesn’t have to follow the same schema as other objects within the same collection. However depending on your use-case that may be better, for example where some sub-fields are not meant to be a consistent **type** between different documents. For this tutorial though, it keeps the GraphQL schema definition and the Mongoose definition fairly rigid to keep our entities consistent.

## Adding the Mongoose connection to our server

Add this line to the top of the `/index.js` file:

```
require('dotenv').config();
```

And the following lines to setup the mongoose connection added to the database:

```
mongoose.set('strictQuery', false);
mongoose.connect(process.env.MONGODB_URL);
```

**Note:** We are adding this `strictQuery` to `false` because Mongoose has a deprecation warning around this being set to false by default in Mongoose 7.

### Creating the Mongoose Models

In the `/src` folder we’ll create a `/models` folder and create three files: `property.js, propertyOwner.js, and renter.js`. We are keeping the model definitions separate from other parts of the code so we can keep our mongoose model definitions clean and concise.

In our `/src/models/renter.js` file we’ll add the following lines of code:

```
const { Schema, model } = require('mongoose');

const renterSchema = new Schema({
    name: {
        type: String,
        required: true,
        maxLength: 60
    },
    city: {
        type: String,
        required: true
    },
    rating: {
        type: Number,
        required: true
    },
    roommates: [{
        type: Schema.Types.ObjectId,
        ref: "Renter"
    }],
    deprecatedField: Boolean,
    nonDeprecatedField: Boolean
});

const Renter = model("Renter", renterSchema);

module.exports = {
    Renter
};
```

What is notable here is that with each field we are able to create specific types, add some level of verification and set whether the field is required or not. Another interesting portion is the **roommates** field having a `Schema.Types.ObjectId` type and a `ref: "Renter"`. This is Mongoose’s way to indicate that a field represents another object and will be referenced by the ID string we pass to that field. This is the first step in being able to take an ID, and eventually resolve a `Renter` object during the resolution process. Lastly we export our `Renter` so that we can use it in our resolvers going forward, this object will give us many helper functions around CRUD operations so that we can integrate MongoDB easily.

In our `/src/models/propertyOwner.js` file we’ll add the following lines of code:

```
const { Schema, model } = require('mongoose');

const propertyOwnerSchema = new Schema({
    name: {
        type: String,
        required: true,
        maxLength: 60
    },
    address: {
        type: String,
        required: true
    },
    rating: {
        type: Number,
        required: true
    },
    properties: [{
        type: Schema.Types.ObjectId,
        ref: "Property"
    }],
    photo: String
});

const PropertyOwner = model("PropertyOwner", propertyOwnerSchema);

module.exports = {
    PropertyOwner
};
```

Nothing here is notable aside from being able to add a list of `Schema.Types.ObjectId`, and remember that we are creating our Mongoose models to match the GraphQL schemas we created earlier.

In our `/src/models/property.js` file we’ll add the following lines of code:

```
const { Schema, model } = require('mongoose');

const propertySchema = new Schema({
    name: {
        type: String,
        required: true,
        maxLength: 60
    },
    city: {
        type: String,
        required: true
    },
    available: Boolean,
    description: {
        type: String,
        maxLength: 250
    },
    photos: [String],
    rating: {
        type: Number,
        required: true
    },
    renters: [{
        type: Schema.Types.ObjectId,
        ref: "Renter"
    }],
    propertyOwner: {
        type: Schema.Types.ObjectId,
        ref: "PropertyOwner"
    }
});

const Property = model("Property", propertySchema);

module.exports = {
    Property
};
```

In this model, the only notable fields are ensuring the relationship between `renters` and `propertyOwner` fields are created correctly and are referencing the same name that we passed into the `model` method for both renters and propertyOwners.

## Creating a seed script

At this point we have our models setup, but in order to start switching our resolvers to use Mongoose, we will need to create data within our database. That is where creating a seed file will come in handy, and we will learn some useful methods that we get from Mongoose. Create a `seed.js` file at the top-level of the project, we are going to use the static information we had before, and move it to the `seed.js` file with some adjustments:

```
require('dotenv').config();
const mongoose = require('mongoose');
const { Property, PropertyOwner, Renter } = require('./src/models');

const renters = [
    {
        name: 'renter 1',
        city: 'Toronto',
        rating: 4,
        roommates: []
    },
    {
        name: 'renter 2',
        city: 'Toronto',
        rating: 3.5,
        roommates: []
    }
];

const propertyOwners = [
    {
        name: 'owner 1',
        address: 'Toronto',
        rating: 4.0,
        properties: [],
        photo: 'something'
    },
    {
        name: 'owner 2',
        address: 'Toronto',
        rating: 4.0,
        properties: [],
        photo: 'something'
    }
];

const properties = [
    {
        name: 'Deluxe suite 1',
        city: 'Toronto',
        rating: 5.0,
        renters: [],
        available: true,
        description: 'amazing place 1',
        photos: [],
        propertyOwner: null
    },
    {
        name: 'Deluxe suite 2',
        city: 'Toronto',
        rating: 5.0,
        renters: [],
        available: true,
        description: 'amazing place 2',
        photos: [],
        propertyOwner: null
    }
];
```

**Note:** We are removing the need for `id` fields in our objects. We are also setting some initial reference values as null before we give them values later on.

Next we are going to run an `async runSeed()` function which will connect to our database, create the initial entities and then attach the relationships before creating the fields:

```
async function runSeed() {

    mongoose.set('strictQuery', false);
    mongoose.connect(process.env.MONGODB_URL);
    try {
        const newRenter = new Renter(renters[0]);
        const newRenterTwo = new Renter(renters[1]);

        // establish relationship
        newRenter.set('roommates', [newRenterTwo._id]);
        newRenterTwo.set('roommates', [newRenter._id]);
        
        await newRenter.save();
        await newRenterTwo.save();

        const newPropertyOwner = new PropertyOwner(propertyOwners[0]);
        const newPropertyOwnerTwo = new PropertyOwner(propertyOwners[1]);

        const newProperty = new Property(properties[0]);
        const newPropertyTwo = new Property(properties[1]);

        newProperty.set('renters', [newRenter._id]);
        newPropertyTwo.set('renters', [newRenterTwo._id]);
        newProperty.set('propertyOwner', [newPropertyOwner._id]);
        newPropertyTwo.set('propertyOwner', [newPropertyOwnerTwo._id]);

        newPropertyOwner.set('properties', [newProperty._id]);
        newPropertyOwnerTwo.set('properties', [newPropertyTwo._id]);

        await newPropertyOwner.save();
        await newPropertyOwnerTwo.save();

        await newProperty.save();
        await newPropertyTwo.save();

        console.log('done');
    } catch (err) {
        console.error('runSeed error', err);
    } finally {
        mongoose.connection.close();
    }
}

runSeed();
```

We can use the `new Entity()` method which will return a MongoDB document for us to save later on. We still want to set up our relationships, our models contain a useful method called `set` that we can set any field we want in the MongoDB document. Once our objects are now in the shape we want them to be, we then save them to the database.

We create our propertyOwners before our properties, so that we can set the `propertyOwner` field which is required by our model validation. Once we save our propertyOwners and our properties we have our console log print out `done` so we get a visual confirmation. We use the `finally` block during our `try/catch` in order to close our connection to Mongoose after our seeding is done.

Next we go to our `package.json` and we add a `seed` field to our scripts parameter:

```
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node --watch ./index.js",
    "seed": "node ./seed.js"
}
```

Your `package.json` should look like above for the `scripts` field.

## Adding Mongoose to our dataSources

Now that we have created the baseline models we want that match our entities, all we need to do to add values into the database is to run `npm run seed` from a console within the project directory. If the seed successfully ran, you will know that our models are correct by viewing them within the MongoDB Atlas webpage. The next step is to go into our dataSources and begin porting over our operations, go into `src/renters/dataSource.js`:

```
const { Renter } = require('../models');

async function getRenterById(renterId) {
    return Renter.findById(renterId)
        .populate('roommates');
}

async function createRenter(renter) {
    const newRenter = new Renter({
        city: renter.city,
        name: renter.name,
        rating: 0,
        roommates: renter.roommates || []
    });

    const savedRenter = await newRenter.save();
    return savedRenter.populate('roommates');
}

async function getAllRenters() {
    return Renter.find({})
        .populate('roommates');
}
```

Mongoose provides out-of-the-box methods for most operations we want to do here. Notice that at the top of the file we import our `Renter` model from our `src/models` directory. Another important thing to notice is the `populate` function. This is Mongoose’s way of performing a join operation on the id we attached to the field **roommates**. This populate method will fetch the data we need related to that id, removing the need to have a custom resolution strategy in our `src/renters/resolvers.js`. In fact we can remove the need for the entire `Renter` field in the resolvers object (we will be doing similar things for the other two entities properties, and propertyOwners).

When creating or updating an entity, we still want to have the `roommates` field populated. In this case we have to add a step where we `save` an entity first, and then return by calling the `.populate` function with the fields we want to return. If we return without calling `.populate` then it will return only the string value of those fields which will break our GraphQL query.

Now let’s move over to the `src/propertyOwners/dataSource.js` and add Mongoose to the methods:

```
const { PropertyOwner } = require('../models');

async function getPropertyOwnerById(propertyOwnerId) {
    return PropertyOwner.findById(propertyOwnerId)
        .populate('properties');
}

async function createPropertyOwner(propertyOwner) {
    const newPropertyOwner = new PropertyOwner({
        name: propertyOwner.name,
        address: propertyOwner.address,
        properties: propertyOwner.properties || [],
        photo: propertyOwner.photo,
        rating: 0
    });

    const savedPropertyOwner = await newPropertyOwner.save();

    return savedPropertyOwner
        .populate('properties');
}

async function getAllPropertyOwners() {
    return PropertyOwner.find({})
        .populate('properties');
}
```

**Note:** Do not forget to remove the PropertyOwner field from the resolvers in `src/propertyOwners/resolver.js`.

Finally we head to our `src/properties/dataSource.js` file and we add the following lines:

```
const { Property } = require('../models');
const { PropertyNotFoundError } = require('../../errors');

async function getPropertyById(propertyId) {
    return Property.findById(propertyId)
        .populate('renters propertyOwner');
}

async function createProperty(property) {
    const newProperty = new Property({
        available: property.available,
        city: property.city,
        description: property.description,
        name: property.name,
        photos: property.photos || [],
        propertyOwner: property.propertyOwnerId,
        rating: 0,
        renters: property.renters || []
    });

    const savedProperty = await newProperty.save();

    return savedProperty
        .populate('renters propertyOwner');
}

async function updateProperty(propertyId, updatedProperty) {
    const savedProperty = await Property.findByIdAndUpdate(
        propertyId,
        updatedProperty,
        { new: true }
    ).populate('renters propertyOwner');

    if (!savedProperty) {
        return PropertyNotFoundError(propertyId);
    }
    return {
        __typename: 'Property',
        id: savedProperty.id,
        available: savedProperty.available,
        city: savedProperty.city,
        description: savedProperty.description,
        name: savedProperty.name,
        photos: savedProperty.photos,
        propertyOwner: savedProperty.propertyOwnerId,
        rating: savedProperty.rating,
        renters: savedProperty.renters
    }
}

async function getAllProperties() {
    return Property.find({})
        .populate('renters propertyOwner');
}
```

Now an interesting part here is that if we need to `populate` multiple fields on one entity, we don’t have to chain multiple `populate` methods. We simply need to pass in one space-separated string of the fields we need to populate (see line 6 for an example). If the field we wish to populate is a field on a nested entity, then we can follow this notation `ENTITY_NAME.FIELD_NAME` and Mongoose will populate it for us.

The `updateProperty` function is also something to note since we are doing something a bit different, we are passing in a third input to `findIdAndUpdate`. By adding `{ new: true }`, it indicates that we should return a new document on update. If there is no returned document, then we can throw our `PropertyNotFoundError`. The other difference is how we return the updated document, because we cannot destructure the fields like so:

```
return {
  __typename: 'Property',
  ...savedProperty
}
```

Doing this will make the return object have other MongoDB-related fields and will break the return type for this GraphQL mutation.

## Conclusion

During this tutorial we covered how to get MongoDB setup in the cloud, how to use `dotenv` in order to hide our connection string, and had walked through some of the features of Mongoose’s Schema creation methods. Next we created a `seed.js` script in order to initialize data within our database. Finally we used those models defined to upgrade our dataSource methods. We talked about some of the features of populate and how it can make entity resolution easier.

At the end of this tutorial you should have a repository similar to [this](https://github.com/bitovi/node-graphql-tutorial-2023/tree/mongodb)

I hope you learned something new in this tutorial, and in the next tutorial in the series will we cover porting over our endpoints from Mongoose to Prisma!

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-graphql/adding-mongodb.html",
          "description": "In this guide we will be working off of a starting point [project](https://github.com/bitovi/node-graphql-tutorial-2023) and will be removing the need for static data, while adding a connection to a live database! 
We’ll also go over some basic methods to perform CRUD operations, and how we will pull related entities from our database (what is known as joins in SQL) using the `populate` function in Mongoose. We’ll also create a small seed file in order to fill our database with some baseline information.

"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```