Blog with Go, Gin, MySQL and Docker - Part 2
Tags: webdev, go, docker, mysql
Published: 2021-05-30 07:08:16

Welcome to Part 2 of the Blog Project with Go, Gin, MySQL and Docker. Do make sure you have followed part 1, [Here](https://nizu.tech/go-blog-1) is the link.

Contents : 
- [Architecture](#architecture)
- [Getting Started](#getting-started)
 - [Designing Models](#designing-models)
 - [Adding Repository Layer](#adding-repository-layer)
 - [Adding Service Layer](#adding-service-layer)
 - [Adding Controller  Layer](#adding-controller-layer)
  - [Adding Utils](#adding-utils)
 - [Adding Routes](#adding-routes)
 - [Main Router](#main-router)
 - [Gluing All Things Together](#gluing-all-things)
- [Test APIs](#test-apis)
- [Wrap Up](#wrap-up)

The respective repository can be found [here](https://github.com/umschaudhary/hashnode-series). Clone the project Check out to branch `part-2` by `git checkout part-2` .
## Architecture 
We will be following Clean Architecture for this Blog Project. Clean architecture is art of writing software applications in a layered fashion. Please do read [this article](#https://nizu.tech/go-clean-architecture) for more detailed information as all layers (repository, controller e.t.c ) are explained there. Following is the  overview of  the project that follows clean architecture and this is what you are going to follow. Isn't it awesome?

```console
├── api 
│   ├── controller
│   │   └── post.go
│   ├── repository
│   │   └── post.go
│   ├── routes
│   │   └── post.go
│   └── service
│       └── post.go
├── docker-compose.yml
├── Dockerfile
├── go.mod
├── go.sum
├── infrastructure
│   ├── db.go
│   ├── env.go
│   └── routes.go
├── main
├── main.go
├── models
│   └── post.go
└── util
    └── response.go
```

## Getting Started : 
### Designing Models
Create a folder `models` in project directory. Inside the `models` folder create a  `blog.go` file and add following code
```go
package models
import "time"

//Post Post Model
type Post struct {
	ID        int64     `gorm:"primary_key;auto_increment" json:"id"`
	Title     string    `gorm:"size:200" json:"title"`
	Body      string    `gorm:"size:3000" json:"body" `
	CreatedAt time.Time `json:"created_at,omitempty"`
	UpdatedAt time.Time `json:"updated_at,omitempty"`
}

// TableName method sets table name for Post model
func (post *Post) TableName() string {
	return "post"
}

//ResponseMap -> response map method of Post
func (post *Post) ResponseMap() map[string]interface{} {
	resp := make(map[string]interface{})
	resp["id"] = post.ID
	resp["title"] = post.Title
	resp["body"] = post.Body
	resp["created_at"] = post.CreatedAt
	resp["updated_at"] = post.UpdatedAt
	return resp
}

```
We are defining `Blog` model which later gets converted into database table ([gorm](#https://gorm.io) does this for us). `TableName` method sets a `blog` as a table name in the database for the `Blog` struct. `ResponseMap` is used to return response from Succesfull API calls. I assume you are familiar with Struct and methods in go. 

### Adding Repository Layer
This layer is the one that interacts and performs CRUD operations on the database. 
Create a folder `api` on the project directory. Inside `api` folder create `repository` folder. Inside the `repository` folder create a  `blog.go` file. The structure should look like this `api -> repository -> blog.go`. You can always refer to [architecture](#architecture) section for project structure reference.

```go
package repository
import (
	"blog/infrastructure"
	"blog/models"
)

//PostRepository -> PostRepository
type PostRepository struct {
	db infrastructure.Database
}

// NewPostRepository : fetching database
func NewPostRepository(db infrastructure.Database) PostRepository {
	return PostRepository{
		db: db,
	}
}

//Save -> Method for saving post to database
func (p PostRepository) Save(post models.Post) error {
	return p.db.DB.Create(&post).Error
}

//FindAll -> Method for fetching all posts from database
func (p PostRepository) FindAll(post models.Post, keyword string) (*[]models.Post, int64, error) {
	var posts []models.Post
	var totalRows int64 = 0

	queryBuider := p.db.DB.Order("created_at desc").Model(&models.Post{})

	// Search parameter
	if keyword != "" {
		queryKeyword := "%" + keyword + "%"
		queryBuider = queryBuider.Where(
			p.db.DB.Where("post.title LIKE ? ", queryKeyword))
	}

	err := queryBuider.
		Where(post).
		Find(&posts).
		Count(&totalRows).Error
	return &posts, totalRows, err
}

//Update -> Method for updating Post
func (p PostRepository) Update(post models.Post) error {
	return p.db.DB.Save(&post).Error
}

//Find -> Method for fetching post by id
func (p PostRepository) Find(post models.Post) (models.Post, error) {
	var posts models.Post
	err := p.db.DB.
		Debug().
		Model(&models.Post{}).
		Where(&post).
		Take(&posts).Error
	return posts, err
}

//Delete Deletes Post
func (p PostRepository) Delete(post models.Post) error {
	return p.db.DB.Delete(&post).Error
}
```
Let's explain above codes: 
- **PostRepository** : PostRepository struct has a `db` field which is a type of `infrastructure.Database`; which infact is a gorm database type. Infrastructure's Database part has been covered up in [Part 1](#https://nizu.tech/go-blog-1).
- **NewPostRepository** : NewPostRepository takes **database**  as argument and returns `PostRepository`. Database argument is provided while initializing the server on `main.go` file.
- ** Save/FindAll/Find/Update/Delete ** :  Perform CRUD operation to database using `gorm` ORM.

### Adding Service Layer
This layer manages the communication between the inner and outer layers (Repository and Controller layers ). More detail [here](#https://nizu.tech/go-clean-architecture).  Inside `api` folder create `service` folder. Inside the `service` folder create a  `blog.go` file. The structure should look like this `api -> service -> blog.go`.Refer to [architecture](#architecture) section for the structure.
```go
package service

import (
	"blog/api/repository"
	"blog/models"
)

//PostService PostService struct
type PostService struct {
	repository repository.PostRepository
}

//NewPostService : returns the PostService struct instance
func NewPostService(r repository.PostRepository) PostService {
	return PostService{
		repository: r,
	}
}

//Save -> calls post repository save method
func (p PostService) Save(post models.Post) error {
	return p.repository.Save(post)
}

//FindAll -> calls post repo find all method
func (p PostService) FindAll(post models.Post, keyword string) (*[]models.Post, int64, error) {
	return p.repository.FindAll(post, keyword)
}

// Update -> calls postrepo update method
func (p PostService) Update(post models.Post) error {
	return p.repository.Update(post)
}

// Delete -> calls post repo delete method
func (p PostService) Delete(id int64) error {
	var post models.Post
	post.ID = id
	return p.repository.Delete(post)
}

// Find -> calls post repo find method
func (p PostService) Find(post models.Post) (models.Post, error) {
	return p.repository.Find(post)
}
```
Let's explain above codes: 
- **PostService** : PostService struct has `repository` field which is a type to PostRepository allowing access to `PostRepository` methods. 
- **NewPostService** : NewPostService takes `PostRepository`  as argument and returns `PostService` allowing all `PostRepository` methods. 
- ** Save/FindAll/Find/Update/Delete ** :  Calls respective `repository` methods.

### Adding Controller Layer
This layer is used to grab the user input and process them or pass them to other layers. More about controllers [here](#https://nizu.tech/go-clean-architecture). But before adding code for the controller layer let's add some utilities which are used to return responses on sucessfull API calls. 

#### Adding Utils
Create a `util` folder on project directory and a file `response.go` inside it. The structure should look like `util -> response.go`.
```go
package util

import "github.com/gin-gonic/gin"

// Response struct
type Response struct {
	Success bool        `json:"success"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

// ErrorJSON : json error response function
func ErrorJSON(c *gin.Context, statusCode int, data interface{}) {
	c.JSON(statusCode, gin.H{"error": data})
}

// SuccessJSON : json error response function
func SuccessJSON(c *gin.Context, statusCode int, data interface{}) {
	c.JSON(statusCode, gin.H{"msg": data})
}

```
- **Response** :  Response is to return JSON Formatted success message with Struct data, here `Blog` data as of now.
- **ErrorJSON** : ErrorJSON is used to return JSON Formatted error response
- **SuccessJSON** : SuccessJSON is used to return JSON Formatted success message.

Create a `controller` folder inside `api` folder and `blog.go` file inside `controller` folder. Project structure folder should looks like `api -> controller -> blog.go`. 

```go
package controller

import (
	"blog/api/service"
	"blog/models"
	"blog/util"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
)

//PostController -> PostController
type PostController struct {
	service service.PostService
}

//NewPostController : NewPostController
func NewPostController(s service.PostService) PostController {
	return PostController{
		service: s,
	}
}

// GetPosts : GetPosts controller
func (p PostController) GetPosts(ctx *gin.Context) {
	var posts models.Post

	keyword := ctx.Query("keyword")

	data, total, err := p.service.FindAll(posts, keyword)

	if err != nil {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Failed to find questions")
		return
	}
	respArr := make([]map[string]interface{}, 0, 0)

	for _, n := range *data {
		resp := n.ResponseMap()
		respArr = append(respArr, resp)
	}

	ctx.JSON(http.StatusOK, &util.Response{
		Success: true,
		Message: "Post result set",
		Data: map[string]interface{}{
			"rows":       respArr,
			"total_rows": total,
		}})
}

// AddPost : AddPost controller
func (p *PostController) AddPost(ctx *gin.Context) {
	var post models.Post
	ctx.ShouldBindJSON(&post)

	if post.Title == "" {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Title is required")
		return
	}
	if post.Body == "" {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Body is required")
		return
	}
	err := p.service.Save(post)
	if err != nil {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Failed to create post")
		return
	}
	util.SuccessJSON(ctx, http.StatusCreated, "Successfully Created Post")
}

//GetPost : get post by id
func (p *PostController) GetPost(c *gin.Context) {
	idParam := c.Param("id")
	id, err := strconv.ParseInt(idParam, 10, 64) //type conversion string to int64
	if err != nil {
		util.ErrorJSON(c, http.StatusBadRequest, "id invalid")
		return
	}
	var post models.Post
	post.ID = id
	foundPost, err := p.service.Find(post)
	if err != nil {
		util.ErrorJSON(c, http.StatusBadRequest, "Error Finding Post")
		return
	}
    response := foundPost.ResponseMap()

	c.JSON(http.StatusOK, &util.Response{
		Success: true,
		Message: "Result set of Post",
		Data:    &response})

}

//DeletePost : Deletes Post
func (p *PostController) DeletePost(c *gin.Context) {
	idParam := c.Param("id")
	id, err := strconv.ParseInt(idParam, 10, 64) //type conversion string to uint64
	if err != nil {
		util.ErrorJSON(c, http.StatusBadRequest, "id invalid")
		return
	}
	err = p.service.Delete(id)

	if err != nil {
		util.ErrorJSON(c, http.StatusBadRequest, "Failed to delete Post")
		return
	}
	response := &util.Response{
		Success: true,
		Message: "Deleted Sucessfully"}
	c.JSON(http.StatusOK, response)
}

//UpdatePost : get update by id
func (p PostController) UpdatePost(ctx *gin.Context) {
	idParam := ctx.Param("id")

	id, err := strconv.ParseInt(idParam, 10, 64)

	if err != nil {
		util.ErrorJSON(ctx, http.StatusBadRequest, "id invalid")
		return
	}
	var post models.Post
	post.ID = id

	postRecord, err := p.service.Find(post)

	if err != nil {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Post with given id not found")
		return
	}
	ctx.ShouldBindJSON(&postRecord)

	if postRecord.Title == "" {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Title is required")
		return
	}
	if postRecord.Body == "" {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Body is required")
		return
	}

	if err := p.service.Update(postRecord); err != nil {
		util.ErrorJSON(ctx, http.StatusBadRequest, "Failed to store Post")
		return
	}
	response := postRecord.ResponseMap()

	ctx.JSON(http.StatusOK, &util.Response{
		Success: true,
		Message: "Successfully Updated Post",
		Data:    response,
	})
}
```
Let's explain above codes: 
- **PostController** : PostController struct has `service` field which is a type to PostService allowing access to `PostService` methods. 
- **NewPostController** : NewPostController takes `PostService` as argument and returns `PostController` allowing all `PostController` methods which are leveraged on controller.  
- ** GetPosts/AddPost/GetPost/DeletePost/UpdatePost ** :  User Input are grabbed/ validated / processed / Service layers are called (which calls Repository methods; performing database operations) / and responses are returned by utility response functions.

### Adding Routes
Till now we have created foundational part of the APIs. Let's create endpoints by adding routes.

```go
package routes

import (
	"blog/api/controller"
	"blog/infrastructure"
)

//PostRoute -> Route for question module
type PostRoute struct {
	Controller controller.PostController
	Handler    infrastructure.GinRouter
}

//NewPostRoute -> initializes new choice rouets
func NewPostRoute(
	controller controller.PostController,
	handler infrastructure.GinRouter,

) PostRoute {
	return PostRoute{
		Controller: controller,
		Handler:    handler,
	}
}

//Setup -> setups new choice Routes
func (p PostRoute) Setup() {
	post := p.Handler.Gin.Group("/posts") //Router group
	{
		post.GET("/", p.Controller.GetPosts)
		post.POST("/", p.Controller.AddPost)
		post.GET("/:id", p.Controller.GetPost)
		post.DELETE("/:id", p.Controller.DeletePost)
		post.PUT("/:id", p.Controller.UpdatePost)
	}
}
```
Let's explain above codes: 
- **PostRoute** : PostRoute struct has `Controller` and `Handler` fields. `Controller` is a type of `PostController` and `Handler` is of type Gin Router. Gin Router here is used to create router group which is used later to create endpoint.s
 - **NewPostRoute** : NewPostRoute takes `Controller` and `Handlre` as arguments and returns `PostRoute` struct allowing access to `PostController` and `Gin Router`.  
- ** Setup ** :  Setup method is used to configure endpoint for post APIs. 

### Main Router
Let's create a function to create and return `Gin Router`. 
Create a `routes.go` file inside `infrastructure` folder. It should look like `infrastructure -> routes.go`. 

```go
package infrastructure

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

//GinRouter -> Gin Router
type GinRouter struct {
	Gin *gin.Engine
}

//NewGinRouter all the routes are defined here
func NewGinRouter() GinRouter {

	httpRouter := gin.Default()

	httpRouter.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"data": "Up and Running..."})
	})
	return GinRouter{
		Gin: httpRouter,
	}

}
```
The above code configures and returns a ` Default Gin Router` instance. 

## Gluing All Things Together
The foundational part has been now completed. The only part left is to glue things together. Edit `main.go` file with following code 
```go
package main

import (
	"blog/api/controller"
	"blog/api/repository"
	"blog/api/routes"
	"blog/api/service"
	"blog/infrastructure"
	"blog/models"
)

func init() {
	infrastructure.LoadEnv()
}

func main() {

	router := infrastructure.NewGinRouter() //router has been initialized and configured
	db := infrastructure.NewDatabase() // databse has been initialized and configured
	postRepository := repository.NewPostRepository(db) // repository are being setup
	postService := service.NewPostService(postRepository) // service are being setup
	postController := controller.NewPostController(postService) // controller are being set up
	postRoute := routes.NewPostRoute(postController, router) // post routes are initialized
	postRoute.Setup() // post routes are being setup

	db.DB.AutoMigrate(&models.Post{}) // migrating Post model to datbase table
	router.Gin.Run(":8000") //server started on 8000 port
}
```
That's all for the `main.go`. 

## Test APIs
It's time to spin the server and testing the APIs.
Fire up the server via Docker Compose via following command
```console
docker-compose up --build
```
Now, Bring up your favorite API Tester application. I will be using [Insomnia](#https://insomnia.rest/)
Testing Create API endpoint -> /posts/
![Post Create API Test](https://cdn.hashnode.com/res/hashnode/image/upload/v1622317086796/kcT44yRig.png)
Testing Get All Post endpoint -> /posts/
![Post Get All Test](https://cdn.hashnode.com/res/hashnode/image/upload/v1622317498032/cRc7tjhgZ.png)
Testing Get Post endpoint -> /posts/2
![Post Get Test](https://cdn.hashnode.com/res/hashnode/image/upload/v1622317525742/8ZUW3eaNo.png)
Testing Update Post endpoint -> /posts/2
![Post Update Test](https://cdn.hashnode.com/res/hashnode/image/upload/v1622317548008/AyyXTx1yd.png)
Testing Delete Post endpoint -> /posts/2
![Post Delete Test](https://cdn.hashnode.com/res/hashnode/image/upload/v1622317569434/t__tK90le.png)

## Wrap Up
Link to Part 1 : [Blog with Go, Gin, MySQL and Docker](#https://nizu.tech/go-blog-1)

**Next Up**
Upcoming Part 3 will cover the following 

- Adding User Struct & APIs
- Related User Posts
- Middleware Layer
and many more..

That's a wrap. Hope you enjoyed the article. Do not hesitate to share your feedback. I am on [Linkedin ](#https://linkedin/com/in/umschaudhary). Let's Connect !

Next Part will be published on next weekend. Please stay tuned.

Thank You for reading !