# Integrate MongoDB into Vercel Functions for the Serverless Experience

Working with Functions as a Service (FaaS), often referred to as serverless, but you're stuck when it comes to trying to get your database working? Given the nature of these serverless functions, interacting with a database is a slightly different experience than if you were to create your own fully hosted back end.

Why is it a different experience, though?

Databases in general, not just MongoDB, can have a finite amount of concurrent connections. When you host your own web application, that web application is typically connecting to your database one time and for as long as that application is running, so is that same connection to the database. Functions offer a different experience, though. Instead of an always-available application, you are now working with an application that may or may not be available at request time to save resources. If you try to connect to your database in your function logic, you'll risk too many connections. If the function shuts down or hibernates or similar, you risk your database connection no longer being available.

In this tutorial, we're going to see how to use the MongoDB Node.js driver with Vercel functions, something quite common when developing Next.js applications.

## The requirements

There are a few requirements that should be met prior to getting started with this tutorial, depending on how far you want to take it.

- You must have a MongoDB Atlas cluster deployed, free tier (M0) or better.
- You should have a Vercel account if you want to deploy to production.
- A recent version of Node.js and NPM must be available.

In this tutorial, we're not going to deploy to production. Everything we plan to do can be tested locally, but if you want to deploy, you'll need a Vercel account and either the CLI installed and configured or your Git host. Both are out of the scope of this tutorial.

While we'll get into the finer details of MongoDB Atlas later in this tutorial, you should already have a MongoDB Atlas account and a cluster deployed. If you need help with either, consider checking out this tutorial.

The big thing you'll need is Node.js. We'll be using it for developing our Next.js application and testing it.

## Creating a new Next.js application with the CLI

Creating a new Next.js project is easy when working with the CLI. From a command line, assuming Node.js is installed, execute the following command:

```bash
npx create-next-app@latest
```

You'll be prompted for some information which will result in your project being created. At any point in this tutorial, you can execute `npm run dev` to build and serve your application locally. You'll be able to test your Vercel functions too!

Before we move forward, let’s add the MongoDB Node.js driver dependency:

```bash 
yarn add mongodb
```

We won't explore it in this tutorial, but Vercel offers a starter template with the MongoDB Atlas integration already configured. If you'd like to learn more, check out the tutorial by Jesse Hall: How to Connect MongoDB Atlas to Vercel Using the New Integration. Instead, we'll look at doing things manually to get an idea of what's happening at each stage of the development cycle.

## Configuring a database cluster in MongoDB Atlas

At this point, you should already have a MongoDB Atlas account with a project and cluster created. The free tier is fine for this tutorial.

Rather than using our imagination to come up with a new set of data for this example, we're going to make use of one of the sample databases available to MongoDB users.

From the MongoDB Atlas dashboard, click the ellipsis menu for one of your clusters and then choose to load the sample datasets. It may take a few minutes, so give it some time.

For this tutorial, we're going to make use of the **sample_restaurants** database, but in reality, it doesn't really matter as the focus of this tutorial is around setup and configuration rather than the actual data.

With the sample dataset loaded, go ahead and create a new user in the "Database Access" tab of the dashboard followed by adding your IP address to the "Network Access" rules. You'll need to do this in order to connect to MongoDB Atlas from your Next.js application. If you choose to deploy your application, you'll need to add a `0.0.0.0` rule as per the Vercel documentation.

## Connect to MongoDB and cache connections for a performance optimized experience

Next.js is one of those technologies where there are a few ways to solve the problem. We could interact with MongoDB at build time, creating a 100% static generated website, but there are plenty of reasons why we might want to keep things adhoc in a serverless function. We could also use the Atlas Data API in the function, but you'll get a richer experience using the Node.js driver.

Within your Next.js project, create a **.env.local** file with the following variables:

```
NEXT_ATLAS_URI=YOUR_ATLAS_URI_HERE
NEXT_ATLAS_DATABASE=sample_restaurants
NEXT_ATLAS_COLLECTION=restaurants
```

Remember, we're using the **sample_restaurants** database in this example, but you can be adventurous and use whatever you'd like. Don't forget to swap the connection information in the **.env.local** file with your own.

Next, create a **lib/mongodb.js** file within your project. This is where we'll handle the actual connection steps. Populate the file with the following code:

```javascript
import { MongoClient } from "mongodb";

const uri = process.env.NEXT_ATLAS_URI;
const options = {
    useUnifiedTopology: true,
    useNewUrlParser: true,
};

let mongoClient = null;
let database = null;

if (!process.env.NEXT_ATLAS_URI) {
    throw new Error('Please add your Mongo URI to .env.local')
}

export async function connectToDatabase() {
    try {
        if (mongoClient && database) {
            return { mongoClient, database };
        }
        if (process.env.NODE_ENV === "development") {
            if (!global._mongoClient) {
                mongoClient = await (new MongoClient(uri, options)).connect();
                global._mongoClient = mongoClient;
            } else {
                mongoClient = global._mongoClient;
            }
        } else {
            mongoClient = await (new MongoClient(uri, options)).connect();
        }
        database = await mongoClient.db(process.env.NEXT_ATLAS_DATABASE);
        return { mongoClient, database };
    } catch (e) {
        console.error(e);
    }
}
```

It might not look like much, but quite a bit of important things are happening in the above file, specific to Next.js and serverless functions. Specifically, take a look at the `connectToDatabase` function:

```javascript
export async function connectToDatabase() {
    try {
        if (mongoClient && database) {
            return { mongoClient, database };
        }
        if (process.env.NODE_ENV === "development") {
            if (!global._mongoClient) {
                mongoClient = await (new MongoClient(uri, options)).connect();
                global._mongoClient = mongoClient;
            } else {
                mongoClient = global._mongoClient;
            }
        } else {
            mongoClient = await (new MongoClient(uri, options)).connect();
        }
        database = await mongoClient.db(process.env.NEXT_ATLAS_DATABASE);
        return { mongoClient, database };
    } catch (e) {
        console.error(e);
    }
}
```

The goal of the above function is to give us a client connection to work with as well as a database. However, the finer details suggest that we need to only establish a new connection if one doesn't exist and to not spam our database with connections if we're in development mode for Next.js. The local development server behaves differently than what you'd get in production, hence the need to check.

Remember, connection quantities are finite, and we should only connect if we aren't already connected.

So what we're doing in the function is we're first checking to see if that connection exists. If it does, return it and let whatever is calling the function use that connection. If the connection doesn't exist and we're in development mode, we check to see if we have a cached session and use that if we do. Otherwise, we need to create a connection and either cache it for development mode or production.

If you understand anything from the above code, understand that we're just creating connections if connections don't already exist.

## Querying MongoDB from a Vercel function in the Next.js application

We've done the difficult part already. We have a connection management system in place for MongoDB to be used throughout our Vercel application. The next part involves creating API endpoints, in a near identical way to Express Framework, and consuming them from within the Next.js front end.

So what does this look like exactly?

Within your project, create a **pages/api/list.js** file with the following JavaScript code:

```javascript
import { connectToDatabase } from "../../lib/mongodb";

export default async function handler(request, response) {
    
    const { database } = await connectToDatabase();
    const collection = database.collection(process.env.NEXT_ATLAS_COLLECTION);

    const results = await collection.find({})
    .project({
        "grades": 0,
        "borough": 0,
        "restaurant_id": 0
    })
    .limit(10).toArray();

    response.status(200).json(results);

}
```

Vercel functions exist within the **pages/api** directory. In this case, we're building a function with the goal of listing out data. Specifically, we're going to list out restaurant data.

In our code above, we are leveraging the `connectToDatabase` function from our connection management code. When we execute the function, we're getting a connection without worrying whether we need to create one or reuse one. The underlying function code handles that for us.

With a connection, we can find all documents within a collection. Not all the fields are important to us, so we're using a projection to exclude what we don't want. Rather than returning all documents from this large collection, we're limiting the results to just a few.

The results get returned to whatever code or external client is requesting it.

If we wanted to consume the endpoint from within the Next.js application, we might do something like the following in the **pages/index.js** file:

```react
import { useEffect, useState } from "react";
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'

export default function Home() {

    const restaurants, setRestaurants] = useState([]);

    useEffect(() => {
        (async () => {
            const results = await fetch("/api/list").then(response => response.json());
            setRestaurants(results);
        })();
    }, []);

    return (
        
            
                Create Next App
                
                
            

            
                
                    MongoDB with Next.js! Example
                
                
                
                    {restaurants.map(restaurant => (
                        
                            

{RESTAURANT.NAME}

                            

{restaurant.address.street}

                        
                    ))}
                
            
        
    )
}
```

Ignoring the boilerplate Next.js code, we added a `useState` and `useEffect` like the following:

```javascript
const [restaurants, setRestaurants] = useState([]);

useEffect(() => {
    (async () => {
        const results = await fetch("/api/list").then(response => response.json());
        setRestaurants(results);
    })();
}, []);
```

The above code will consume the API when the component loads. We can then render it in the following section:

```react

    {restaurants.map(restaurant => (
        
            

{RESTAURANT.NAME}

            

{restaurant.address.street}

        
    ))}

```

There isn't anything out of the ordinary happening in the process of consuming or rendering. The heavy lifting that was important was in the function itself as well as our connection management file.

## Conclusion

You just saw how to use MongoDB Atlas with Vercel functions, which is a serverless solution that requires a different kind of approach. Remember, when dealing with serverless, the availability of your functions is up in the air. You don't want to spawn too many connections and you don't want to attempt to use connections that don't exist. We resolved this by caching our connections and using the cached connection if available. Otherwise, spin up a new connection.

Got a question or think you can improve upon this solution? Share it in the [MongoDB Community Forums!