Explore Developer Center's New Chatbot! MongoDB AI Chatbot can be accessed at the top of your navigation to answer all your MongoDB questions.

MongoDB Developer
Atlas
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Productschevron-right
Atlaschevron-right

Serverless Development with AWS Lambda and MongoDB Atlas Using Java

Nic Raboy6 min read • Published Jul 20, 2023 • Updated Jul 20, 2023
ServerlessJavaAtlas
Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
So you need to build an application that will scale with demand and a database to scale with it? It might make sense to explore serverless functions, like those offered by AWS Lambda, and a cloud database like MongoDB Atlas.
Serverless functions are great because you can implement very specific logic in the form of a function and the infrastructure will scale automatically to meet the demand of your users. This will spare you from having to spend potentially large amounts of money on always on, but not always needed, infrastructure. Pair this with an elastically scalable database like MongoDB Atlas, and you've got an amazing thing in the works.
In this tutorial, we're going to explore how to create a serverless function with AWS Lambda and MongoDB, but we're going to focus on using Java, one of the available AWS Lambda runtimes.

The requirements

To be successful with this tutorial, there are a few requirements that must be met prior to continuing.
  • Must have an AWS Lambda compatible version of Java installed and configured on your local computer.
  • Must have a MongoDB Atlas instance deployed and configured.
  • Must have an Amazon Web Services (AWS) account.
  • Must have Gradle or Maven, but Gradle will be the focus for dependency management.
For the sake of this tutorial, the instance size or tier of MongoDB Atlas is not too important. In fact, an M0 instance, which is free, will work fine. You could also use a serverless instance which pairs nicely with the serverless architecture of AWS Lambda. Since the Atlas configuration is out of the scope of this tutorial, you'll need to have your user rules and network access rules in place already. If you need help configuring MongoDB Atlas, consider checking out the getting started guide.
Going into this tutorial, you might start with the following boilerplate AWS Lambda code for Java:
1package example;
2
3import com.amazonaws.services.lambda.runtime.Context;
4import com.amazonaws.services.lambda.runtime.RequestHandler;
5
6public class Handler implements RequestHandler<Map<String,String>, Void>{
7
8 @Override
9 public void handleRequest(Map<String,String> event, Context context) {
10 // Code will be in here...
11 return null;
12 }
13}
You can use a popular development IDE like IntelliJ, but it doesn't matter, as long as you have access to Gradle or Maven for building your project.
Speaking of Gradle, the following can be used as boilerplate for our tasks and dependencies:
1plugins {
2 id 'java'
3}
4
5group = 'org.example'
6version = '1.0-SNAPSHOT'
7
8repositories {
9 mavenCentral()
10}
11
12dependencies {
13 testImplementation platform('org.junit:junit-bom:5.9.1')
14 testImplementation 'org.junit.jupiter:junit-jupiter'
15 implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'
16 implementation 'com.amazonaws:aws-lambda-java-events:3.11.1'
17 implementation 'org.slf4j:slf4j-log4j12:1.7.36'
18 runtimeOnly 'com.amazonaws:aws-lambda-java-log4j2:1.5.1'
19}
20
21test {
22 useJUnitPlatform()
23}
24
25task buildZip(type: Zip) {
26 into('lib') {
27 from(jar)
28 from(configurations.runtimeClasspath)
29 }
30}
31
32build.dependsOn buildZip
Take note that we do have our AWS Lambda dependencies included as well as a task for bundling everything into a ZIP archive when we build.
With the baseline AWS Lambda function in place, we can focus on the MongoDB development side of things.

Installing, configuring, and connecting to MongoDB Atlas with the MongoDB driver for Java

To get started, we're going to need the MongoDB driver for Java available to us. This dependency can be added to our project's build.gradle file:
1dependencies {
2 // Previous boilerplate dependencies ...
3 implementation 'org.mongodb:bson:4.10.2'
4 implementation 'org.mongodb:mongodb-driver-sync:4.10.2'
5}
The above two lines indicate that we want to use the driver for interacting with MongoDB and we also want to be able to interact with BSON.
With the driver and related components available to us, let's revisit the Java code we saw earlier. In this particular example, the Java code will be found in a src/main/java/example/Handler.java file.
1package example;
2
3import com.amazonaws.services.lambda.runtime.Context;
4import com.amazonaws.services.lambda.runtime.RequestHandler;
5import com.mongodb.client.MongoClient;
6import com.mongodb.client.MongoClients;
7import com.mongodb.client.MongoCollection;
8import com.mongodb.client.MongoDatabase;
9import com.mongodb.client.model.Filters;
10import org.bson.BsonDocument;
11import org.bson.Document;
12import org.bson.conversions.Bson;
13
14import java.util.ArrayList;
15import java.util.List;
16import java.util.Map;
17
18public class Handler implements RequestHandler<Map<String,String>, Void>{
19
20 private final MongoClient mongoClient;
21
22 public Handler() {
23 mongoClient = MongoClients.create(System.getenv("MONGODB_ATLAS_URI"));
24 }
25
26 @Override
27 public void handleRequest(Map<String,String> event, Context context) {
28 MongoDatabase database = mongoClient.getDatabase("sample_mflix");
29 MongoCollection<Document> collection = database.getCollection("movies");
30
31 // More logic here ...
32
33 return null;
34 }
35}
In the above code, we've imported a few classes, but we've also made some changes pertaining to how we plan to interact with MongoDB.
The first thing you'll notice is our use of the Handler constructor method:
1public Handler() {
2 mongoClient = MongoClients.create(System.getenv("MONGODB_ATLAS_URI"));
3}
We're establishing our client, not our connection, outside of the handler function itself. We're doing this so our connections can be reused and not established on every invocation, which would potentially overload us with too many concurrent connections. We're also referencing an environment variable for our MongoDB Atlas URI string. This will be set later within the AWS Lambda portal.
It's bad practice to hard-code your URI string into your application. Use a configuration file or environment variable whenever possible.
Next up, we have the function logic where we grab a reference to our database and collection:
1@Override
2public void handleRequest(Map<String,String> event, Context context) {
3 MongoDatabase database = mongoClient.getDatabase("sample_mflix");
4 MongoCollection<Document> collection = database.getCollection("movies");
5
6 // More logic here ...
7
8 return null;
9}
Because this example was meant to only be enough to get you going, we're using the sample datasets that are available for MongoDB Atlas users. It doesn't really matter what you use for this example as long as you've got a collection with some data.
We're on our way to being successful with MongoDB and AWS Lambda!

Querying data from MongoDB when the serverless function is invoked

With the client configuration in place, we can focus on interacting with MongoDB. Before we do that, a few things need to change to the design of our function:
1public class Handler implements RequestHandler<Map<String,String>, List<Document>>{
2
3 private final MongoClient mongoClient;
4
5 public Handler() {
6 mongoClient = MongoClients.create(System.getenv("MONGODB_ATLAS_URI"));
7 }
8
9 @Override
10 public List<Document> handleRequest(Map<String,String> event, Context context) {
11 MongoDatabase database = mongoClient.getDatabase("sample_mflix");
12 MongoCollection<Document> collection = database.getCollection("movies");
13
14 // More logic here ...
15
16 return null;
17 }
18}
Notice that the implemented RequestHandler now uses List<Document> instead of Void. The return type of the handleRequest function has also been changed from void to List<Document> to support us returning an array of documents back to the requesting client.
While you could do a POJO approach in your function, we're going to use Document instead.
If we want to query MongoDB and return the results, we could do something like this:
1@Override
2public List<Document> handleRequest(Map<String,String> event, Context context) {
3 MongoDatabase database = mongoClient.getDatabase("sample_mflix");
4 MongoCollection<Document> collection = database.getCollection("movies");
5
6 Bson filter = new BsonDocument();
7
8 if(event.containsKey("title") && !event.get("title").isEmpty()) {
9 filter = Filters.eq("title", event.get("title"));
10 }
11
12 List<Document> results = new ArrayList<>();
13 collection.find(filter).limit(5).into(results);
14
15 return results;
16}
In the above example, we are checking to see if the user input data event contains a property "title" and if it does, use it as part of our filter. Otherwise, we're just going to return everything in the specified collection.
Speaking of returning everything, the sample data set is rather large, so we're actually going to limit the results to five documents or less. Also, instead of using a cursor, we're going to dump all the results from the find operation into a List<Document> which we're going to return back to the requesting client.
We didn't do much in terms of data validation, and our query was rather simple, but it is a starting point for bigger and better things.

Deploy the Java application to AWS Lambda

The project for this example is complete, so it is time to get it bundled and ready to go for deployment within the AWS cloud.
Since we're using Gradle for this project and we have a task defined for bundling, execute the build script doing something like the following:
1./gradlew build
If everything built properly, you should have a build/distributions/*.zip file. The name of that file will depend on all the naming you've used throughout your project.
With that file in hand, go to the AWS dashboard for Lambda and create a new function.
There are three things you're going to want to do for a successful deployment:
  1. Add the environment variable for the MongoDB Atlas URI.
  2. Upload the ZIP archive.
  3. Rename the "Handler" information to reflect your actual project.
Within the AWS Lambda dashboard for your new function, click the "Configuration" tab followed by the "Environment Variables" navigation item. Add your environment variable information and make sure the key name matches the name you used in your code.
We used MONGODB_ATLAS_URI in the code, and the actual value would look something like this:
1mongodb+srv://<username>:<password>@examples.170lwj0.mongodb.net/?retryWrites=true&w=majority
Just remember to use your actual username, password, and instance URL.
Next, you can upload your ZIP archive from the "Code" tab of the dashboard.
When the upload completes, on the "Code" tab, look for "Runtime Settings" section and choose to edit it. In our example, the package name was example, the Java file was named Handler, and the function with the logic was named handleRequest. With this in mind, our "Handler" should be example.Handler::handleRequest. If you're using something else for your naming, make sure it reflects appropriately, otherwise Lambda won't know what to do when invoked.
Take the function for a spin!
Using the "Test" tab, try invoking the function with no user input and then invoke it using the following:
1{
2 "title": "Batman"
3}
You should see different results reflecting what was added in the code.

Conclusion

You just saw how to create a serverless function with AWS Lambda that interacts with MongoDB. In this particular example, Java was the star of the show, but similar logic and steps can be applied for any of the other supported AWS Lambda runtimes or MongoDB drivers.
If you have questions or want to see how others are using MongoDB Atlas with AWS Lambda, check out the MongoDB Community Forums.

Facebook Icontwitter iconlinkedin icon
Rate this tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Code Example

Final Space API


Jul 07, 2022 | 1 min read
Tutorial

UDF Announcement for MongoDB to BigQuery Dataflow Templates


Apr 02, 2024 | 4 min read
Video

The Atlas Search 'cene: Season 1


Sep 11, 2024 | 2 min
Article

Keeping Your Costs Down With MongoDB Atlas Serverless Instances


Oct 01, 2024 | 3 min read
Table of Contents