Integrate MongoDB into Vercel Functions for the Serverless Experience
Rate this tutorial
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.
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.
- A recent version of Node.js and NPM must be available.
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 project is easy when working with the CLI. From a command line, assuming Node.js is installed, execute the following command:
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 devto 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:
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: . Instead, we'll look at doing things manually to get an idea of what's happening at each stage of the development cycle.
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.0rule as per the .
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:
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:
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
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.
So what does this look like exactly?
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
connectToDatabasefunction 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:
Ignoring the boilerplate Next.js code, we added a
useEffectlike the following:
The above code will consume the API when the component loads. We can then render it in the following section:
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.
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.