# How to Build a Go Web Application with Gin, MongoDB, and with the Help of AI

Building applications with Go provides many advantages. The language is fast, simple, and lightweight while supporting powerful features like concurrency, strong typing, and a robust standard library. In this tutorial, we’ll use the popular Gin web framework along with MongoDB to build a Go-based web application.

Gin is a minimalist web framework for Golang that provides an easy way to build web servers and APIs. It is fast, lightweight, and modular, making it ideal for building microservices and APIs, but can be easily extended to build full-blown applications.

We'll use Gin to build a web application with three endpoints that connect to a MongoDB database. MongoDB is a popular document-oriented NoSQL database that stores data in JSON-like documents. MongoDB is a great fit for building modern applications.

Rather than building the entire application by hand, we’ll leverage a coding AI assistant by Sourcegraph called Cody to help us build our Go application. Cody is the only AI assistant that knows your entire codebase and can help you write, debug, test, and document your code. We’ll use many of these features as we build our application today.

## Prerequisites

Before you begin, you’ll need:

- Go installed on your development machine. Download it on their website.
- A MongoDB Atlas account. Sign up for free.
- Basic familiarity with Go and MongoDB syntax.
- Sourcegraph Cody installed in your favorite IDE. (For this tutorial, we'll be using VS Code). Get it for free.

Once you meet the prerequisites, you’re ready to build. Let’s go.

## Getting started

We'll start by creating a new Go project for our application. For this example, we’ll name the project **mflix**, so let’s go ahead and create the project directory and navigate into it:

```bash
mkdir mflix
cd mflix
```

Next, initialize a new Go module, which will manage dependencies for our project:

```bash
go mod init mflix
```

Now that we have our Go module created, let’s install the dependencies for our project. We’ll keep it really simple and just install the `gin` and `mongodb` libraries.

```bash
go get github.com/gin-gonic/gin
go get go.mongodb.org/mongo-driver/mongo
```

With our dependencies fetched and installed, we’re ready to start building our application.

## Gin application setup with Cody

To start building our application, let’s go ahead and create our entry point into the app by creating a **main.go** file. Next, while we can set up our application manually, we’ll instead leverage Cody to build out our starting point. In the Cody chat window, we can ask Cody to create a basic Go Gin application.

 guide. The database that we will work with is called `sample_mflix` and the collection in that database we’ll use is called `movies`. This dataset contains a list of movies with various information like the plot, genre, year of release, and much more.

 on the movies collection. Aggregation operations process multiple documents and return computed results. So with this endpoint, the end user could pass in any valid MongoDB aggregation pipeline to run various analyses on the `movies` collection.

Note that aggregations are very powerful and in a production environment, you probably wouldn’t want to enable this level of access through HTTP request payloads. But for the sake of the tutorial, we opted to keep it in. As a homework assignment for further learning, try using Cody to limit the number of stages or the types of operations that the end user can perform on this endpoint.

```go
// POST /movies/aggregations - Run aggregations on movies
func aggregateMovies(c *gin.Context) {
    // Get aggregation pipeline from request body
    var pipeline interface{}
    if err := c.ShouldBindJSON(&pipeline); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // Run aggregations
    cursor, err := mongoClient.Database("sample_mflix").Collection("movies").Aggregate(context.TODO(), pipeline)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    // Map results
    var result ]bson.M
    if err = cursor.All(context.TODO(), &result); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    // Return result
    c.JSON(http.StatusOK, result)
}
```

Now that we have our endpoints implemented, let’s add them to our router so that we can call them. Here again, we can use another feature of Cody, called autocomplete, to intelligently give us statement completions so that we don’t have to write all the code ourselves. 

![Cody AI Autocomplete with Go][6]

Our `main` function should now look like:

```go
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello World",
})
})
r.GET("/movies", getMovies)
r.GET("/movies/:id", getMovieByID)
r.POST("/movies/aggregations", aggregateMovies)

r.Run()
}
```

Now that we have our routes set up, let’s test our application to make sure everything is working well. Restart the server and navigate to **localhost:8080/movies**. If all goes well, you should see a large list of movies returned in JSON format in your browser window. If you do not see this, check your IDE console to see what errors are shown.

![Sample Output for the Movies Endpoint][7]

Let’s test the second endpoint. Pick any `id` from the movies collection and navigate to **localhost:8080/movies/{id}** — so for example, **localhost:8080/movies/573a1390f29313caabcd42e8**. If everything goes well, you should see that single movie listed. But if you’ve been following this tutorial, you actually won’t see the movie.

![String to Object ID Results Error][8]

The issue is that in our `getMovie` function implementation, we are accepting the `id` value as a `string`, while the data type in our MongoDB database is an `ObjectID`. So when we run the `FindOne` method and try to match the string value of `id` to the `ObjectID` value, we don’t get a match. 

Let’s ask Cody to help us fix this by converting the string input we get to an `ObjectID`.

![Cody AI MongoDB String to ObjectID][9]

Our updated `getMovieByID` function is as follows:

```go
func getMovieByID(c *gin.Context) {

// Get movie ID from URL
idStr := c.Param("id")

// Convert id string to ObjectId
id, err := primitive.ObjectIDFromHex(idStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// Find movie by ObjectId
var movie bson.M
err = mongoClient.Database("sample_mflix").Collection("movies").FindOne(context.TODO(), bson.D{{"_id", id}}).Decode(&movie)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

// Return movie
c.JSON(http.StatusOK, movie)
}
```

Depending on your IDE, you may need to add the `primitive` dependency in your import statement. The final import statement looks like:

```go
import (
"context"
"log"
"net/http"

"github.com/gin-gonic/gin"
"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"
)
```

If we examine the new code that Cody provided, we can see that we are now getting the value from our `id` parameter and storing it into a variable named `idStr`. We then use the primitive package to try and convert the string to an `ObjectID`. If the `idStr` is a valid string that can be converted to an `ObjectID`, then we are good to go and we use the new `id` variable when doing our `FindOne` operation. If not, then we get an error message back.

Restart your server and now try to get a single movie result by navigating to **localhost:8080/movies/{id}**.

![Single Movie Response Endpoint][10]

For our final endpoint, we are allowing the end user to provide an aggregation pipeline that we will execute on the `mflix` collection. The user can provide any aggregation they want. To test this endpoint, we’ll make a POST request to **localhost:8080/movies/aggregations**. In the body of the request, we’ll include our aggregation pipeline.

![Postman Aggregation Endpoint in MongoDB][11]

Let’s run an aggregation to return a count of comedy movies, grouped by year, in descending order. Again, remember aggregations are very powerful and can be abused. You normally would not want to give direct access to the end user to write and run their own aggregations ad hoc within an HTTP request, unless it was for something like an internal tool. Our aggregation pipeline will look like the following:

```json
[
    {"$match": {"genres": "Comedy"}},
    {"$group": {
        "_id": "$year", 
        "count": {"$sum": 1}
    }},
    {"$sort": {"count": -1}}
]
```

Running this aggregation, we’ll get a result set that looks like this:

```json
[
    {
        "_id": 2014,
        "count": 287
    },
    {
        "_id": 2013,
        "count": 286
    },
    {
        "_id": 2009,
        "count": 268
    },
    {
        "_id": 2011,
        "count": 263
    },
    {
        "_id": 2006,
        "count": 260
    },
    ...
]
```

It seems 2014 was a big year for comedy. If you are not familiar with how aggregations work, you can check out the following resources:

- [Introduction to the MongoDB Aggregation Framework
- MongoDB Aggregation Pipeline Queries vs SQL Queries
- A Better MongoDB Aggregation Experience via Compass

Additionally, you can ask Cody for a specific explanation about how our `aggregateMovies` function works to help you further understand how the code is implemented using the Cody `/explain` command.

. 

And if you have any questions or comments, let’s continue the conversation in our developer forums!

The entire code for our application is above, so there is no GitHub repo for this simple application. Happy coding.

  [1]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt123181346af4c7e6/65148770b25810649e804636/eVB87PA.gif
  [2]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt3df7c0149a4824ac/6514820f4f2fa85e60699bf8/image4.png
  [3]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt6a72c368f716c7c2/65148238a5f15d7388fc754a/image2.png
  [4]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blta325fcc27ed55546/651482786fefa7183fc43138/image7.png
  [5]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltc8029e22c4381027/6514880ecf50bf3147fff13f/A7n71ej.gif
  [6]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt438f1d659d2f1043/6514887b27287d9b63bf9215/6O8d6cR.gif
  [7]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltd6759b52be548308/651482b2d45f2927c800b583/image3.png
  [8]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltfc8ea470eb6585bd/651482da69060a5af7fc2c40/image5.png
  [9]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blte5d9fb517f22f08f/651488d82a06d70de3f4faf9/Y2HuNHe.gif
  [10]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/bltc2467265b39e7d2b/651483038f0457d9df12aceb/image6.png
  [11]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt972b959f5918c282/651483244f2fa81286699c09/image1.png
  [12]: https://images.contentstack.io/v3/assets/blt39790b633ee0d5a7/blt9c888329868b60b6/6514892c2a06d7d0a6f4fafd/g4xtxUg.gif