# Interact with MongoDB in an AWS Lambda Function Using Go

If you're a Go developer and you're looking to go serverless, AWS Lambda is a solid choice that will get you up and running in no time. But what happens when you need to connect to your database? With serverless functions, also known as functions as a service (FaaS), you can never be sure about the uptime of your function or how it has chosen to scale automatically with demand. For this reason, concurrent connections to your database, which aren't infinite, happen a little differently. In other words, we want to be efficient in how connections and interactions to the database are made.

In this tutorial, we'll see how to create a serverless function using the Go programming language and that function will connect to and query MongoDB Atlas in an efficient manner.

## The prerequisites

To narrow the scope of this particular tutorial, there are a few prerequisites that must be met prior to starting:

- A MongoDB Atlas cluster with network access and user roles already configured.
Already have an AWS account? Atlas supports paying for usage via the AWS Marketplace (AWS MP) without any upfront commitment — simply sign up for MongoDB Atlas via AWS Marketplace.
- The sample MongoDB Atlas dataset loaded.
- Knowledge of the Go programming language.
- An Amazon Web Services (AWS) account with a basic understanding of AWS Lambda.

We won't go through the process of deploying a MongoDB Atlas cluster in this tutorial, including the configuration of network allow lists or users. As long as AWS has access through a VPC or global IP allow and a user that can read from the sample databases, you'll be fine.

If you need help getting started with MongoDB Atlas, check out this tutorial on the subject.

The point of this tutorial is not to explore the ins and outs of AWS Lambda, but instead see how to include MongoDB in our workflow. For this reason, you should have some knowledge of AWS Lambda and how to use it prior to proceeding.

## Build an AWS Lambda function with Golang and MongoDB

To kick things off, we need to create a new Go project on our local computer. Execute the following commands from your command line:

```bash
mkdir lambdaexample
cd lambdaexample
go mod init lambdaexample
```

The above commands will create a new project directory and initialize the use of Go Modules for our AWS Lambda and MongoDB dependencies.

Next, execute the following commands from within your project:

```bash
go get go.mongodb.org/mongo-driver/mongo
go get github.com/aws/aws-lambda-go/lambda
```

The above commands will download the Go driver for MongoDB and the AWS Lambda SDK.

Finally, create a **main.go** file in your project. The **main.go** file will be where we add all our project code.

Within the **main.go** file, add the following code:

```go
package main

import (
"context"
"os"

"github.com/aws/aws-lambda-go/lambda"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type EventInput struct {
Limit int64 `json:"limit"`
}

type Movie struct {
ID    primitive.ObjectID `bson:"_id" json:"_id"`
Title string             `bson:"title" json:"title"`
Year  int32              `bson:"year" json:"year"`
}

var client, err = mongo.Connect(context.Background(), options.Client().ApplyURI(os.Getenv("ATLAS_URI")))

func HandleRequest(ctx context.Context, input EventInput) (]Movie, error) {
if err != nil {
return nil, err
}

collection := client.Database("sample_mflix").Collection("movies")

opts := options.Find()

if input.Limit != 0 {
opts = opts.SetLimit(input.Limit)
}
cursor, err := collection.Find(context.Background(), bson.M{}, opts)
if err != nil {
return nil, err
}
var movies []Movie
if err = cursor.All(context.Background(), &movies); err != nil {
return nil, err
}

return movies, nil
}

func main() {
lambda.Start(HandleRequest)
}
```

Don't worry, we're going to break down what the above code does and how it relates to your serverless function.

First, you'll notice the following two data structures:

```go
type EventInput struct {
Limit int64 `json:"limit"`
}

type Movie struct {
ID    primitive.ObjectID `bson:"_id" json:"_id"`
Title string             `bson:"title" json:"title"`
Year  int32              `bson:"year" json:"year"`
}
```

In this example, `EventInput` represents any input that can be sent to our AWS Lambda function. The `Limit` field will represent how many documents the user wants to return with their request. The data structure can include whatever other fields you think would be helpful.

The `Movie` data structure represents the data that we plan to return back to the user. It has both BSON and JSON annotations on each of the fields. The BSON annotation maps the MongoDB document fields to the local variable and the JSON annotation maps the local field to data that AWS Lambda can understand.

We will be using the **sample_mflix** database in this example and that database has a **movies** collection. Our `Movie` data structure is meant to map documents in that collection. You can include as many or as few fields as you want, but only the fields included will be returned to the user.

Next, we want to handle a connection to the database:

```go
var client, err = mongo.Connect(context.Background(), options.Client().ApplyURI(os.Getenv("ATLAS_URI")))
```

The above line creates a database client for our application. It uses an `ATLAS_URI` environment variable with the connection information. We'll set that later in AWS Lambda.

We don't want to establish a database connection every time the function is executed. We only want to connect when the function starts. We don't have control over when a function starts, so the correct solution is to connect outside of the `HandleRequest` function and outside of the `main` function.

Most of our magic happens in the `HandleRequest` function:

```go
func HandleRequest(ctx context.Context, input EventInput) ([]Movie, error) {
if err != nil {
return nil, err
}

collection := client.Database("sample_mflix").Collection("movies")

opts := options.Find()

if input.Limit != 0 {
opts = opts.SetLimit(input.Limit)
}
cursor, err := collection.Find(context.Background(), bson.M{}, opts)
if err != nil {
return nil, err
}
var movies []Movie
if err = cursor.All(context.Background(), &movies); err != nil {
return nil, err
}

return movies, nil
}
```

Notice in the declaration of the function we are accepting the `EventInput` and we're returning a slice of `Movie` to the user.

When we first enter the function, we check to see if there was an error. Remember, the connection to the database could have failed, so we're catching it here.

Once again, for this example we're using the **sample_mflix** database and the **movies** collection. We're storing a reference to this in our `collection` variable.

Since we've chosen to accept user input and this input happens to be related to how queries are done, we are creating an options variable. One of our many possible options is the limit, so if we provide a limit, we should probably set it. Using the options, we execute a `Find` operation on the collection. To keep this example simple, our filter criteria is an empty map which will result in all documents from the collection being returned — of course, the maximum being whatever the limit was set to.

Rather than iterating through a cursor of the results in our function, we're choosing to do the `All` method to load the results into our `movies` slice.

Assuming there were no errors along the way, we return the result and AWS Lambda should present it as JSON.

We haven't uploaded our function yet!

## Building and packaging the AWS Lambda function with Golang

Since Go is a compiled programming language, you need to create a binary before uploading it to AWS Lambda. There are certain requirements that come with this job.

First, we need to worry about the compilation operating system and CPU architecture. AWS Lambda expects Linux and AMD64, so if you're using something else, you need to make use of the Go cross compiler.

For best results, execute the following command:

```bash
env GOOS=linux GOARCH=amd64 go build
```

The above command will build the project for the correct operating system and architecture regardless of what computer you're using.

Don't forget to add your binary file to a ZIP archive after it builds. In our example, the binary file should have a **lambdaexample** name unless you specify otherwise.

![AWS Lambda MongoDB Go Project

Within the AWS Lambda dashboard, upload your project and confirm that the handler and architecture are correct.

Before testing the function, don't forget to update your environment variables within AWS Lambda.

You can get your URI string from the MongoDB Atlas dashboard.

Once done, you can test everything using the "Test" tab of the AWS Lambda dashboard. Provide an optional "limit" for the "Event JSON" and check the results for your movies!

## Conclusion

You just saw how to use MongoDB with AWS Lambda and the Go runtime! AWS makes it very easy to use Go for serverless functions and the Go driver for MongoDB makes it even easier to use with MongoDB.

As a further reading exercise, it is worth checking out the MongoDB Go Quick Start as well as some documentation around connection pooling in serverless functions.