Blog Project with Go, Gin, MySQL and Docker - Part 1
Tags: showdev, docker, go, mysql
Published: 2021-05-25 07:13:22

## Contents
- [Target Audience](#target-audience)
- [Learning Objectives](#learning-objectives)
- [Requirements](#requirements)
- [Getting Started](#getting-started)
 - [Getting Up Hello World Server](#getting-up-hello-world-server)
 - [Setting up Docker](#setting-up-docker)
 - [Setting up the database](#setting-up-the-database)
 - [Environment Variables](#environment-variables)
 - [Docker Compose file](#docker-compose-file)
- [Wrap Up](#wrap-up)

## Target Audience
This Series doesn't focuses on fundamentals of Go. It focuses on how to build backend services. Indiviuals interested in learning web development with Gin Framework are ideal candidates.

## Learning Objectives
This Series teaches you the fundamental of building Rest APIs with Go. After completing this series you will know how to do the following : 
* Setup Golang with MySQL and Docker for Local Development
* Gin Framework for CRUD APIs.
* Domain Driven Development
* Dependency Injection with Go
* Authentication with Firebase and so. on


## Requirements 
- Go Installed ( version : go1.16.3 linux/amd64 )
- Docker ( version 20.10.6 ) & Docker-compose ( version 1.29.1). 
- Prefarrably Linux Machine ( As i will be using linux but any would be fine )
- General Understanding of Restful Services/APIs.

The above mentioned versions are installed on my machine.
You can find the full source of the series in this [repository](https://github.com/umschaudhary/hashnode-series), feel free to fork it:

## Getting Started
Let's start by creating a `blog` folder in your preferred location. Then initialize a new [Go module](https://blog.golang.org/using-go-modules) inside `blog` folder with following command.

```consloe
go mod init blog
``` 
The above command creates a `go.mod` file. It will allow you to manage project dependencies. Let's add some dependencies. 

```console
go get github.com/gin-gonic/gin
```
The above command will install [Gin Framework](https://github.com/gin-gonic/gin) and [Gorm](https://gorm.io/) and create `go.sum` file. This file stores info about the dependencies and their versions. </br>

**Gin**  is lightweight, well-documented and fast HTTP web framework. The creators claims that Gin is 40 times faster than other similar framework to Gin

### Getting Up Hello world Server
Create a `main.go` file in the root level; open up your favorite editor  and write up the following code

```go
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
)

func main() {
  router := gin.Default() //new gin router initialization
  router.GET("/", func(context *gin.Context) {
    context.JSON(http.StatusOK, gin.H{"data": "Hello World !"})    
  }) // first endpoint returns Hello World
  router.Run(":8000") //running application, Default port is 8080
}
``` 

Let's go through the code in `main.go`
- **package main** : Each go file is a package. The main package is always an entry point for gin project.

- ** import ** : Imports project dependenices.

- ** func main ** : Main function is triggered whenever you run the application. Inside the function, a new `Gin router` is initialized  to the `router` variable. A new endpoint `/` is set on the router.</br>
**Setting up a route/endpoint requires two things : **
 - **Endpoint** : It is the path from where data is fetched. For instance, if the visitor wants to get all posts, they’d fetch the `/posts` endpoint.
 - **Handler** : It determines how you provide the data to endpoint. Business logic, like getting/saving data from/to the database, validating the user input, and so on. JSON method of context object is used to send data in JSON format. This method takes an HTTP [status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) and a JSON response as the parameters.

Use the following command to start the server.
```bash
go run main.go
```
If you see output similar to the following that means you are good to go. 
```console
[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8000
```
visit http://localhost:8000/ on your browser. You should see `{"data":"Hello World !"}`.

### Setting Up Docker 
The above method of running the server is the Go way of running applications. Let's setup it via the Docker way.
Create a file `Dockerfile` at the project root with the following code
```Dockerfile
FROM golang:alpine

RUN mkdir /app

WORKDIR /app

ADD go.mod .
ADD go.sum .

RUN go mod download
ADD . .

RUN go get github.com/githubnemo/CompileDaemon

EXPOSE 8000

ENTRYPOINT CompileDaemon --build="go build main.go" --command=./main

```
To build the docker container from above `Dockerfile` use the below command
```console
docker build -t hello_world :1.0 .
```
containers can be listed out with `docker ps -a` on your terminal. You should see containers with other infomation.
```bash
CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS                      PORTS     NAMES
62cddf7c2659   hello_world:1.0      "/bin/sh -c 'Compile…"   23 minutes ago   Exited (0) 23 minutes ago             gifted_kilby
```
To spin up the container use the below command
```console
docker run -p 8000:8000 hello_world:1.0
```
visit http://localhost:8000/ on your browser. You should see `{"data":"Hello World !"}`.

#### Let’s Review Docker Commands mentioned above in Dockerfile :

- **FROM**  :  FROM refers which base image to use for the container. The golang:1.16  image is a Linux-based image which has golang installed and no other additional programs or software.

- **WORKDIR** :  WORKDIR Changes the working directory. In our case to /app. It sets a working directory for subsequent  commands. 

- **ADD** :  ADD instruction literally copies the file from one location to another. `ADD [SOURCE] [DESTINATION]` is the syntax of the command. Similary, there is also a COPY command for similar purpose. Here, we are copying the go.sum and go.mod file first so that we will have all the libraries installed before copying all the files.

- **RUN** :  RUN instruction will execute any commands in a new layer on top of the current image and commit the results. The resulting committed image will be used for the next step in the Dockerfile.

- **EXPOSE** : EXPOSE instructs that services running on Docker container is available in port 8000.

- **ENTRYPOINT** :  Entrypoint runs the command inside the container once a container is  created from an image. You can only have one Entrypoint instruction in a `Dockerfile`. If multiple Entrypoint instructions are used, the last one will be executed. Here, once the container is created, the Entrypoint command will run our golang project.

### Setting Up The Database
MySQL package is needed to connect the database with the application. Install it with following command.
```console
go get gorm.io/driver/mysql gorm.io/gorm
```
**Gorm**  is fantastic ORM library for Golang.
As connecting database is essential/building part of application. Let's create a folder `infrastructure` and `db.go` file inside `infrastructure`. Let's write some come inside `db.go`.
```go
package infrastructure

import (
	"fmt"
	"os"
	"time"
    "gorm.io/driver/mysql"
	"gorm.io/gorm"
)

//Database struct
type Database struct {
	DB *gorm.DB
}

//NewDatabase : intializes and returns mysql db
func NewDatabase() Database {
    USER := os.Getenv("DB_USER")
	PASS := os.Getenv("DB_PASSWORD")
	HOST := os.Getenv("DB_HOST")
	DBNAME := os.Getenv("DB_NAME")

    URL := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", USER, PASS, 
    HOST, DBNAME)
	fmt.Println(URL)
	db, err := gorm.Open(mysql.Open(URL))

	if err != nil {
		panic("Failed to connect to database!")

	}
	fmt.Println("Database connection established")
    return Database{
		DB: db,
	}

}
```
Inside `NewDatabase` function the following work is being done : 
1. database credentials USER, PASS, HOST and DBNAME is fetched from [environment variables](#environment-variables)
2. Database url is constructed utilizing environment variables and saving it to `URL` variable
3. A new `mysql` connection is opened with the `gorm.Open` method from `URL`.
4. Lastly, Database struct is returned with gorm database instance as parameter, which is used later on the application to access the database.

### Environment Variables
There are two type of variables in the project `program variables` and `environment variables`. Program variables are normal variables that stores values withing the code block or module, where as environment variables is available globally through the project. 
#### Setting Up Environment Variables
Environment variables can be set up using various ways. Here, we will be using `.env` file to set them. Create `.env` file on the project root and add below variables. 
```bash
MYSQL_ROOT_PASSWORD=root
DB_USER=user
DB_PASSWORD=Password@123
DB_HOST=db
DB_PORT=3306
DB_NAME=golang
SERVER_PORT=8000
ENVIRONMENT=local
```
#### Reading Environment Variables
Now, let's write code to read `.env` file. Create a file `env.go` in `infrastructure` folder and fill up with following code
```go
package infrastructure

import (
	"log"
    "github.com/joho/godotenv"
)

//LoadEnv loads environment variables from .env file
func LoadEnv() {
	err := godotenv.Load(".env")
	if err != nil {
		log.Fatalf("unable to load .env file")
	}
}

```
Install godotenv package by following command 
```console
go get github.com/joho/godotenv
```
**In order to load `.env` file and database we need to edit `main.go` as below **
```go
func main() {

	router := gin.Default()

	router.GET("/", func(context *gin.Context) {
		infrastructure.LoadEnv()  //loading env
		infrastructure.NewDatabase() //new database connection
		context.JSON(http.StatusOK, gin.H{"data": "Hello World !"})
	})
	router.Run(":8000")
}

```
### Docker Compose file
We need docker compose for defining and running multi-container Docker applications. In our case Database and our Gin application. Create a file `docker-compose.yml` at project level and fill up with following code 
```yml
version: '3'
services:
  db:
    image: mysql/mysql-server:5.7
    ports:
      - "3305:3306"
    environment:
      - "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}"
      - "MYSQL_USER=${DB_USER}"
      - "MYSQL_PASSWORD=${DB_PASSWORD}"
      - "MYSQL_DATABASE=${DB_NAME}"   
  web:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - ".:/app"
    depends_on:
      - db
    links:
      - "db:database"
``` 
Let’s review terms mentioned inside compose file.

- **version ‘3’**: Docker compose version, Latest is 3.7

- **services**: The services section defines all the different containers that are to be created. We have two services namely, `web` and `db`.

- **web**: This is the name of our Gin app service. It can be anything. Docker Compose will create containers with this name.

- **build**: This clause specifies the Dockerfile location. `.` represents the current directory where the docker-compose.yml file is located and Dockerfile is used to build an image and run the container from it. We can also enter a path to Dockerfile there.

- **ports**: The ports clause is used to map or expose the container ports to the host machine. Here mapping port `"8000:8000"` , so that we can access our services on `8000` port of host machine.

- **volumes**: Here, we are attaching our code files directory to the containers, ./app directory so that we don’t have to rebuild the images for every change in the files. This will also help in auto-reloading the server when running in debug mode.

- **links**: Links literally links one service to another. Here, we are linking the database container to the web container, so that our web container can reach the database in the bridge network. (Networking alert !!). Please if you want to learn about network in docker in detail do refer to : Network containers  

- **image**: If we don’t have a Dockerfile and want to run a service directly using an already built image, we can specify the image location using the ‘image’ clause. Compose will pull the image and fork a container from it. In our case We mentioned mysql/mysql-server:5.7 to our database service

- **environment**: Any environment variable that needs to be set up in the container can be created using the ‘environment’ clause. 
```
   environment:
      - "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}"
  ```
The value `${MYSQL_ROOT_PASSWORD}` and other variables are read from `.env`.
You are now all set to spin up the containers. Start build and run with the following command.
```
 docker-compose up --build
```
If you see something like below on your terminal that means, you have server running up: 
```console
db_1   | 2021-05-24T09:18:02.841094Z 0 [Note] Event Scheduler: Loaded 0 events
db_1   | 2021-05-24T09:18:02.841370Z 0 [Note] mysqld: ready for connections.
db_1   | Version: '5.7.34'  socket: '/var/lib/mysql/mysql.sock'  port: 3306  MySQL Community Server (GPL)
web_1  | 2021/05/24 09:18:06 Running build command!
web_1  | 2021/05/24 09:18:17 Build ok.
web_1  | 2021/05/24 09:18:17 Restarting the given command.
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
web_1  | 2021/05/24 09:18:17 stdout:
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
web_1  | 2021/05/24 09:18:17 stdout:  - using env:      export GIN_MODE=release
web_1  | 2021/05/24 09:18:17 stdout:  - using code:     gin.SetMode(gin.ReleaseMode)
web_1  | 2021/05/24 09:18:17 stdout:
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
web_1  | 2021/05/24 09:18:17 stdout: [GIN-debug] Listening and serving HTTP on :8000
```
Navigate to http://localhost:8000/ and check on terminal,  `Database connection established` should be printed on standard output.

The repository of this series can be found [here](https://github.com/umschaudhary/hashnode-series)

## Wrap Up
That's it for part 1 of the series.
In the next part we will be working on : 
- Adding models (structs) to the application
- Adopting Domain Driven Development pattern

Make sure to follow me to get the update for the next part or subscribe so that you never miss my upcoming articles. 

Thank you !!