How to Set Up a Local Development Workflow with Docker for your Go Apps (with MongoDB and Mongo Express)
Tags: docker, go, mongodb
Published: 2020-05-15 14:59:42

---
title: How to Set Up a Local Development Workflow with Docker for your Go Apps (with MongoDB and Mongo Express)
published: true
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/3j1s46t1zs3n51p09oux.jpg
description: This is how I set up my local development flow using for Go and Mongo Apps with Hot Reloead and Mongo Express Client
tags: Docker,Go,Mongo
---

Cover Photo by Etienne Girardet on Unsplash

Setting up a development workflow can be time consuming and frustrating especially when there is a need to install a lot of tools. But with the advent of containers this became easy, way easy. In this article I'll explain how I set up my dev workflow for Go Apps being my primary working tool. 

So in setting up a new project I start out by listing out the tooling I need for maximum productivity along the way. In this guide, I'll build an extremely easy Go api with just two endpoints, one that receives a `GET Request` to the base endpoint `/` and returns a welcome message and another one that simply receives a book as `json` object from a `POST Request` to the api endpoint `/books` then saves this book in a MongoDB collection. So to work on this project I need the following tools:
- Golang and the apps dependencies
- Mongo DB as the database
- Mongo Express (optional) for my database admin interface

To start things of, open your `terminal` or `cmd` window and navigate to your desired working directory. In my case I have a folder named `projects` in my home directory where I organize my projects.

1. Clone this repository https://github.com/ItsCosmas/docker-go-dev
    ```sh
    git clone https://github.com/ItsCosmas/docker-go-dev
    ```
2. Navigate to the newly created directory
    ```sh
    cd docker-go-dev
    ```
3. Once Inside the `docker-go-dev` directory, open with your favorite editor. In my case VS Code.
    ```sh
    code .
    ```
4. Make Sure your directory tree looks like this.
    ![Tree directory Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/9udt0ry62rwer7is8260.png)
5. Open the `go-app` folder and Look at the `Dockerfile`.

    ```
    # Pull base image
    FROM golang:1.18-alpine

    # Install git
    RUN apk update && apk add --no-cache git

    # Where our files will be in the docker container 
    WORKDIR /opt/go-app

    # Copy the source from the current directory to the working Directory inside the container 
    # Source also contains go.mod and go.sum which are dependency files
    COPY . .

    # Get Dependency
    RUN go mod download

    # Install Air for hot reload
    RUN go install github.com/cosmtrek/air@latest

    # The ENTRYPOINT defines the command that will be ran when the container starts up
    # In this case air command for hot reload go apps on file changes
    ENTRYPOINT air
        
    ```
I have well commented the Dockerfile but I will explain a few bits.
>A Dockerfile is a text document that contains all the commands a 
user could call on the command line to assemble an image.

- We start by pulling the base go image from the docker hub. The alpine tag indicates the alpine version which is bit lightweight and should work well in development, I won't go to much optimization for the moment. 
- Then install git which is a dependency to get some packages.
- Then we set the work directory inside the container in my case it is `/opt/go-app` that is where our files will live inside the container.
- Then we copy the source from the current directory to the working Directory set above. Our Source Code also contains `go.mod` and `go.sum` which are dependency files
- After that we get the app service dependencies using `go mod download`
- We will need Live Reload for our app so we install `air` https://github.com/cosmtrek/air which is a live reload utility for Go apps.
- Finally we pass an `ENTRYPOINT` which defines the command that will be ran when the container starts up, in our case its `air` the Live reload utility.

That's it for the `go-app` service now we move a directory up to the `docker-compose.yml` file.

This is the content of the compose file:

```
version: '3.7'
services:
    # The Go App
    go-app:
        build: ./go-app
        container_name: go_app
        depends_on:
            - 'mongo'
        environment:
            - PORT=8000
        ports:
            - '8000:8000'
        volumes:
            - './go-app:/opt/go-app:cached'
    # MongoDB
    mongo:
        image: 'mongo:4.4'
        container_name: mongo
        ports:
            - '27017:27017'
        volumes:
            - ./mongodata:/data/db
        restart: always
    # Mongo Express Web-based MongoDB admin interface
    mongo_express:
        image: 'mongo-express:0.54.0'
        container_name: mongo_express
        depends_on:
            - 'mongo'
        ports:
            - '8081:8081'
        restart: always

```
I'll explain the core parts of this file.
>Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. 

To learn more about `docker-compose.yml` file head over to the official docs at https://docs.docker.com/compose/. 
- First we define our version tag for the compose file in our case `3.7` as at the time of writing. 

#### go-app service
- Next We will define the services needed for our application. We start with the `go-app` service which we just created above, remember that `Dockerfile`? 
- The go-app service uses an image build from the `Dockerfile` inside the `go-app` directory. 
- We give it a custom name in our case `go_app`.
- Then We indicate that the service is dependent on another service called `mongo` which will be our database service.
- Finally We pass in some environment variables for use inside the container. In our case `PORT`.
- Last We bind the container and the host machine to the exposed port `8000`.
- Finally we define a volume which maps our source directory to the work directory inside the container, that way changes in our source code from our host machine will reflect inside the container.

#### Mongo Service

- First we define the image that shall be used in our `mongo` service, in our case an official image with the bionic tag.
- Then we give it a custom name `mongo` which is the default, could be anything.
- Then We bind the container and the host machine to the exposed port `27017`.
- Then We define a volume which maps a local directory `mongodata` to the data directory inside the container, that way we shall have persistent storage even if our container fails.

- Lastly we add a `restart:always` flag to always restart the container if it stops.

#### Mongo Express (Optional)
- A Web-based MongoDB admin interface that will allows to manage our mongo database in the browser.

- First We define the image , in our case the official `mongo-express` image from docker hub.
- We give it a custom container name
- We add a `depends_on` flag so that the container only runs if `mongo` service is running.
- Then We bind the container and the host machine to the exposed port `8081`.
- Lastly we add a `restart:always` flag to always restart the container if it stops.

# Action
Now that everything is set up, it's time to see our containers in action.To start our containers we run:

```sh
docker-compose up
```

If everything is correct docker will build the containers and you should see this output in your terminal indicating your containers were built and started successfully:

![Containers Created](https://dev-to-uploads.s3.amazonaws.com/i/0st1ju76w1xn6at05f7k.png)

There's obviously more output but I'm focusing on this. Once that's done you can hit the base endpoint `http://localhost:8000` to verify its working. You can use a tool like postman https://www.postman.com/ or just `curl` on your terminal.

Open a new Terminal and input:
```sh
curl http://localhost:8000; echo
```
If your app worked, you should see similar output:

```sh
{"message":"Welcome to your App on Docker"}
```
The `GET` endpoint actually works.
Now let us test the `POST` Endpoint. On your terminal enter this command:
```sh
curl -d '{
	"name":"My Best Book Ever",
	"pages": 12
}' -H "Content-Type: application/json" -X POST http://localhost:8000/books; echo 
```
If it works you should see this Response:
```sh
{"code":200,"msg":"Book Published","data":{"name":"My Best Book Ever","pages":12}}
```

`POST` Endpoint on Postman:

![Postman Post](https://dev-to-uploads.s3.amazonaws.com/i/4v7spcs27xrvvcvaalpu.png)

If success, you should see this output:

![A successful Request on Postman](https://dev-to-uploads.s3.amazonaws.com/i/tj66ey01n225cjwf8rv8.png)

##### To Check Live Reload, Just Edit a file e.g. The `app/app.go` :

```
	// API Home
	r.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "Welcome to your App on Docker",
		})
	})
```
You can change the message to whatever you want and once you save your file you should see a similar output on your terminal:

![Live Reload](https://dev-to-uploads.s3.amazonaws.com/i/w5wxrdkeja5r5k25ewos.png)

##### To check whether the data was saved to Mongo We'll use Mongo-Express:

1. Open http://localhost:8081/ in your browser. You should see the database `books-db` and if not just refresh your browser:

    ![Mongo Client](https://dev-to-uploads.s3.amazonaws.com/i/gwlm2o1eko2q3l7jgg6k.png)

2. Click on `books-db` and it should redirect to the Database Page as seen below::

    ![Collections Page](https://dev-to-uploads.s3.amazonaws.com/i/xx7d5ibnlbqr99znscpr.png)


3. Click on the Collection you wish, in this case `book` collection and the records from the collection should be displayed as below:

    ![Records](https://dev-to-uploads.s3.amazonaws.com/i/gapl629yvo3njcgh4z4k.png)

4. You can also view a Single Document if you wish by just clicking on it and it should redirect to a document page similar to this:
    ![Single Document Page](https://dev-to-uploads.s3.amazonaws.com/i/3h1ae3v5wkelep065d0s.png)

So In this article I have shown you how I set up my local dev environment and workflow for go apps, Mongo and Mongo Express. The process is almost similar for other stacks, the key is to have a standardized dev environment for maximum productivity. By the way shipping to production is very easy with `docker` you require just a few optimizations and that's all, no OS dependency issues ever.

I have updated the article and the github repository so that this tutorial can be helpful even now as at 2022.

And that's it for the article. PS: Trying to do some regular writing 🙂.