Skip to content

akmamun/go-fication-examples

Repository files navigation

Go Fication Examples

An Example repos of go-fication

Table of Contents

Motivation

Write restful API with fast development and developer friendly

Configuration Manage

ENV Manage

  • Default ENV Configuration Manage from .env. sample file .env.example
# Server Configuration
SECRET=h9wt*pasj6796j##w(w8=xaje8tpi6h*r&hzgrz065u&ed+k2)
DEBUG=True # `False` in Production
ALLOWED_HOSTS=0.0.0.0
SERVER_HOST=0.0.0.0
SERVER_PORT=8000

# Database Configuration
MASTER_DB_NAME=test_pg_go
MASTER_DB_USER=mamun
MASTER_DB_PASSWORD=123
MASTER_DB_HOST=postgres_db
MASTER_DB_PORT=5432
MASTER_DB_LOG_MODE=True # `False` in Production
MASTER_SSL_MODE=disable

REPLICA_DB_NAME=test_pg_go
REPLICA_DB_USER=mamun
REPLICA_DB_PASSWORD=123
REPLICA_DB_HOST=localhost
REPLICA_DB_PORT=5432
REPLICA_DB_LOG_MODE=True # `False` in Production
REPLICA_SSL_MODE=disable
  • Server DEBUG set False in Production
  • Database Logger MASTER_DB_LOG_MODE and REPLICA_DB_LOG_MODE set False in production
  • If ENV Manage from YAML file add a config.yml file and configuration db.go and server.go. See More ENV YAML Configure

Server Configuration

  • Use chi Route

Database Configuration

  • Use GORM as an ORM
  • Use database MASTER_DB_HOST value set as localhost for local development, and use postgres_db for docker development

PG Admin

  • Check PG Admin on http://0.0.0.0:5050/browser/
  • Login with Credential Email admin@admin.com Password root
  • Connect Database Host as postgres_db, DB Username and Password as per .env set
  • Note: if not configure .env, default Username mamun and password 123

Installation

Local Setup Instruction

Follow these steps:

  • Copy .env.example as .env and configure necessary values
  • To add all dependencies for a package in your module go get . in the current directory
  • Locally run go run main.go or go build main.go and run ./main
  • Check Application health available on 0.0.0.0:8000/health

Develop Application in Docker with Live Reload

Follow these steps:

Boilerplate Structure

├── config
│   ├── config.go
│   ├── db.go
│   └── server.go
├── controllers
│   └── example_controller.go
├── docker-compose-dev.yml
├── docker-compose-prod.yml
├── Dockerfile
├── Dockerfile-dev
├── go.mod
├── go.sum
├── helpers
│   ├── json_helper.go
│   ├── pagination
│   │   └── pagination.go
│   └── response.go
├── infra
│   ├── database
│   │   ├── migrate.go
│   │   └── sql_client.go
│   └── logger
│       └── zero_log.go
├── LICENSE
├── main.go
├── Makefile
├── migrations
│   └── 20120_example.sql
├── models
│   └── example_model.go
├── README.md
├── repository
│   ├── example_repo.go
│   └── gorm_repo.go
├── routers
│   ├── examples.go
│   ├── index.go
│   ├── middlewares
│   │   └── cors.go
│   └── router.go

Let's Build an API

  1. models folder add a new file name example_model.go
package models

import (
	"time"
)

type Example struct {
	Id        int        `json:"id"`
	Data      string     `json:"data" binding:"required"`
	CreatedAt *time.Time `json:"created_at,string,omitempty"`
	UpdatedAt *time.Time `json:"updated_at_at,string,omitempty"`
}
// TableName is Database Table Name of this model
func (e *Example) TableName() string {
	return "examples"
}
  1. Add Model to migration
package migrations

import (
	"go-fication/models"
	"go-fication/infra/database"
)

func Migrate() {
	var migrationModels = []interface{}{&models.Example{}}
	err := database.GetDB().AutoMigrate(migrationModels...)
	if err != nil {
		return
	}
}
  1. repository folder add a file example_repo.go
package repository

import (
	"go-fication/helpers/pagination"
	"go-fication/models"
)

type ExampleRepo interface {
	GetExamples(limit, offset int64) (res interface{}, err error)
	CreateExample(exp *models.Example) error
}

func (r *GormRepository) GetExamples(limit, offset int64) (res interface{}, err error) {
	var example []*models.Example
	res = pagination.Paginate(&pagination.Param{
		DB:      r.db,
		Limit:   limit,
		Offset:  offset,
		OrderBy: "id ASC",
	}, &example)
	return
}
func (r *GormRepository) GetExamplesList() (exp []*models.Example, err error) {
	err = r.db.Database.Find(&exp).Error
	return
}

func (r *GormRepository) CreateExample(exp *models.Example) (err error) {
	err = r.db.Database.Create(exp).Error
	return
}
  1. controller folder add a file example_controller.go
  • Create API Endpoint
  • Use any syntax of GORM after base.DB, this is wrapper of *gorm.DB
package controllers

import (
  "encoding/json"
  "go-fication/models"
  "go-fication/repository"
  "net/http"
  "strconv"
)

type ExampleHandler struct {
  repo repository.ExampleRepo
}

func NewExampleHandler(repo repository.ExampleRepo) *ExampleHandler {
  return &ExampleHandler{
    repo: repo,
  }
}
func (h *ExampleHandler) GetData(w http.ResponseWriter, request *http.Request) {
  q := request.URL.Query()
  limit, _ := strconv.Atoi(q.Get("limit"))
  offset, _ := strconv.Atoi(q.Get("offset"))

  data, err := h.repo.GetExamples(int64(limit), int64(offset))
  if err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
  }
  w.Header().Set("Content-Type", "application/json")
  json.NewEncoder(w).Encode(&data)
}

func (h *ExampleHandler) CreateData(w http.ResponseWriter, request *http.Request) {
  example := new(models.Example)
  err := json.NewDecoder(request.Body).Decode(&example)
  if err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
  }
  err = h.repo.CreateExample(example)
  if err != nil {
    return
  }
  w.Header().Set("Content-Type", "application/json")
  json.NewEncoder(w).Encode(example)
}
  1. routers folder add a file example.go
package routers

import (
  "github.com/go-chi/chi/v5"
  "go-fication/controllers"
  "go-fication/infra/database"
  "go-fication/repository"
)

func ExamplesRoutes(router *chi.Mux, db *database.DB) {
  repo := repository.NewGormRepository(db)
  exampleCtrl := controllers.NewExampleHandler(repo)
  router.Group(func(r chi.Router) {
    r.Get("/test", exampleCtrl.GetData)
    r.Post("/test", exampleCtrl.CreateData)

  })
}
  1. Finally, register routes to index.go
package routers

import (
  "github.com/go-chi/chi/v5"
  "net/http"
)

//RegisterRoutes add all routing list here automatically get main router
func RegisterRoutes(router *chi.Mux) {
  router.Get("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("\"live\": \"ok\""))
  })
  //Add All route
  ExamplesRoutes(router)
}
  • Congratulation, your new endpoint 0.0.0.0:8000/v1/example/

Deployment

Container Development Build

  • Run make build

Container Production Build and Up

  • Run make production

ENV Yaml Configure

database:
  driver: "postgres"
  dbname: "test_pg_go"
  username: "mamun"
  password: "123"
  host: "postgres_db" # use `localhost` for local development
  port: "5432"
  ssl_mode: disable
  log_mode: false

server:
  host: "0.0.0.0"
  port: "8000"
  secret: "secret"
  allow_hosts: "localhost"
  debug: false #use `false` in production
  request:
    timeout: 100
func ServerConfig() string {
viper.SetDefault("server.host", "0.0.0.0")
viper.SetDefault("server.port", "8000")
appServer := fmt.Sprintf("%s:%s", viper.GetString("server.host"), viper.GetString("server.port"))
return appServer
}
func DbConfiguration() string {
	
dbname := viper.GetString("database.dbname")
username := viper.GetString("database.username")
password := viper.GetString("database.password")
host := viper.GetString("database.host")
port := viper.GetString("database.port")
sslMode := viper.GetString("database.ssl_mode")

dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s",
host, username, password, dbname, port, sslMode)
return dsn
}

Useful Commands

  • make dev: make dev for development work
  • make build: make build container
  • make production: docker production build and up
  • clean: clean for all clear docker images

Use Packages

  • Viper - Go configuration with fangs.
  • Gorm - The fantastic ORM library for Golang
  • Logger - Zero Allocation JSON Logger
  • Air - Live reload for Go apps (Docker Development)