This repository is a structured golang project that is used to generate a new backend/API service application by using the golang gin framework.
- Required for this local run in gcc https://jmeubank.github.io/tdm-gcc/
- Install golang ^v18 here https://go.dev/dl/ and follow the installation instructions
- Install postgresql for database transaction (skip if you want to use docker)
- Install docker (if you want to run this locally)
/
(root dir)
this folder contain .env
file that store environment data from host, _public_key.pem
for authorize data token JWT (if have jwt Authorization) and file main.go
of The main entrance of the API for setup environment settings, systems,port, etc .
Example .env
PUBLIC_KEY_PATH="_public_key.pem"
API_PATH_VERSION="/v1"
DB_HOST=${DB_HOST}
DB_PORT=5432
DB_NAME='app_db'
DB_USER=${DB_USER}
DB_PASSWORD=${DB_PASSWORD}
PORT=${APP_PORT}
Example main.go
package main
import (
"fmt"
"net/http"
"gopkg.in/gin-gonic/gin.v1"
"articles/services/mysql"
"articles/routers/v1"
"articles/core/models"
)
var router *gin.Engine;
func init() {
mysql.CheckDB()
router = gin.New();
router.NoRoute(noRouteHandler())
version1:=router.Group("/v1")
v1.InitRoutes(version1)
}
func main() {
fmt.Println("Server Running on Port: ", 9090)
http.ListenAndServe(":9090",router)
}
/routers
This package will store every routes in your REST API.
The reason separate the handler is, to easy us to manage each routers. So we can create comments about the API , that with apidoc will generate this into structured documentation. Then we will call the function in index.go in current package.
Example:
Example code ExampleRoute.go
package route
import (
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"golang-example/app/controller"
"golang-example/app/repository"
"golang-example/app/service"
"golang-example/config/database"
)
func ExampleRoute(router *gin.RouterGroup) {
validate := validator.New()
PostgreDB := database.PostgreDB
exampleRepository := repository.NewExampleRepository(PostgreDB)
exampleService := service.NewExampleService(exampleRepository)
authService := service.NewAuthService()
exampleController := controller.NewExampleController(exampleService, authService, validate)
//ExampleData
router.POST("/example-data", exampleController.CreateExampleData)
router.GET("/example-data", exampleController.GetExampleDatas)
router.GET("/example-data/:example_data_id", exampleController.GetExampleDataById)
router.PUT("/example-data/:example_data_id", exampleController.EditExampleData)
router.DELETE("/example-data/:example_data_id", exampleController.DeleteExampleData)
}
Example code routers.go
package router
import (
"github.com/gin-gonic/gin"
"golang-example/app/middleware"
route "golang-example/router/v1"
)
// InitRoutesJWT function route that use JWT midlleware
func InitRoutesJWT(g *gin.RouterGroup) {
// Initialize Midlleware
g.Use(middleware.ErrorHandler())
g.Use(middleware.JSONMiddleware())
g.Use(middleware.AuthorizeJWT())
// Initialize route
route.ExampleRoute(g)
}
// InitRoutes function route for home or some url not using a JWT Auth
func InitRoutes(g *gin.RouterGroup) {
g.Use(middleware.ErrorHandler())
g.Use(middleware.JSONMiddleware())
// Initialize route
route.SetHomeRoutes(g)
}
/app/controllers
this package will store every controllers in your REST API, and will be used in the routers.
the controller will be used to handle the request and response to the client.
Example Code
package controller
import (
"errors"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"strconv"
"golang-example/app/helper"
"golang-example/app/models/request"
"golang-example/app/service"
"golang-example/libraries/httpResponse"
)
type exampleController struct {
exampleService service.ExampleService
authService service.AuthService
validator *validator.Validate
}
func NewExampleController(
exampleService service.ExampleService,
authService service.AuthService,
validator *validator.Validate,
) *exampleController {
return &exampleController{
exampleService: exampleService,
authService: authService,
validator: validator,
}
}
func (global *exampleController) CreateExampleData(c *gin.Context) {
/*-- Check user permission with decoded JWT Token --*/
checkUserRoles := global.authService.UserHasRoles(c, "backend_services")
if !checkUserRoles {
httpResponse.Forbidden()
return
}
/*-- Validating project id params from segment --*/
Request := request.CreateExampleData{}
Request.BindRequestField(c)
errorValidation := helper.ValidateFormData(c.Request, Request.Rules, Request.Message)
if errorValidation != nil {
httpResponse.BadRequestFormData(errorValidation)
}
requestData := Request.RequestCreateExampleData
/*-- Print data request with Example Service --*/
ExampleData := global.exampleService.CreateExampleData(requestData)
httpResponse.HttpCreated(c, "Success create example data", ExampleData)
return
}
/app/service
this package will store every services in your REST API, and will be used in the controllers.
the service will create a logic for handling the request and response to the client and pass the data to the controller.
Example Code
package service
import (
"errors"
"gorm.io/gorm"
"golang-example/app/models/entity"
"golang-example/app/models/request"
"golang-example/app/repository"
"golang-example/libraries/httpResponse"
)
type exampleService struct {
exampleRepository repository.ExampleRepository
}
func NewExampleService(exampleRepository repository.ExampleRepository) ExampleService {
return &exampleService{
exampleRepository: exampleRepository,
}
}
type ExampleService interface {
CreateExampleData(ExampleData request.RequestCreateExampleData) entity.ExampleData
GetExampleDatas() []entity.ExampleData
GetExampleDataById(ExampleDataId int) entity.ExampleData
EditExampleData(ExampleDataId int, ExampleDataRequest request.ExampleRequest) entity.ExampleData
DeleteExampleData(ExampleDataId int)
}
func (s *exampleService) CreateExampleData(ExampleDataRequest request.RequestCreateExampleData) (ExampleData entity.ExampleData) {
//Create Example Data
ExampleData, err := s.exampleRepository.CreateExampleData(ExampleDataRequest)
if err != nil {
httpResponse.InternalServerError(err)
}
return ExampleData
}
/app/repository
this package will store every repositories in your REST API, and will be used in the services.
the repository will handling data from services and do the CRUD operation to the database.
Example Code
package repository
import (
"golang-example/app/models/entity"
"golang-example/app/models/request"
"gorm.io/gorm"
)
type exampleRepository struct {
PostgreDB *gorm.DB
}
func NewExampleRepository(PostgreDB *gorm.DB) ExampleRepository {
return &exampleRepository{PostgreDB: PostgreDB}
}
type ExampleRepository interface {
CreateExampleData(ExampleDataRequest request.RequestCreateExampleData) (ExampleData entity.ExampleData, err error)
GetExampleDatas() (ExampleDatas []entity.ExampleData, err error)
GetExampleDataById(ExampleDataId int) (ExampleData entity.ExampleData, err error)
EditExampleData(ExampleData entity.ExampleData) (err error)
DeleteExampleData(ExampleDataId int) (err error)
}
func (r *exampleRepository) CreateExampleData(ExampleDataRequest request.RequestCreateExampleData) (ExampleData entity.ExampleData, err error) {
ExampleData = entity.ExampleData{
Name: ExampleDataRequest.Name,
Age: ExampleDataRequest.Age,
Address: ExampleDataRequest.Address,
}
if err := r.PostgreDB.Create(ExampleData).Error; err != nil {
return ExampleData, err
}
return ExampleData, nil
}
/app/models/entity
This package will store all created model struct for using as data transcaction in database or just as transactional data. we use gorm as ORM library for handling data in database.
Example Code
package entity
import "gorm.io/gorm"
type ExampleData struct {
gorm.Model
Name string `gorm:"type:varchar(255);not null"`
Age int `gorm:"type:int;not null"`
Address string `gorm:"type:varchar(255);not null"`
}
/app/models/request
This package will store all request as struct data for validate data from API body request.
We use package validator as validation library for handling data from request.
- Reference for validator library is https://pkg.go.dev/github.com/go-playground/validator/v10
- the reference for validating form-data type is https://github.com/thedevsaddam/govalidator.
Example Code
package request
import (
"github.com/gin-gonic/gin"
"strconv"
)
// ExampleRequest is a struct for example using validator v10
type ExampleRequest struct {
Name string `json:"name" validate:"required,min=3"`
Age int `json:"age" validate:"required,gte=0,lte=130"`
Address string `json:"address" validate:"required,min=3"`
}
type RequestCreateExampleData struct {
Name string
Age int
Address string
}
// CreateExampleData is a struct for example using thedevsaddam/govalidator
type CreateExampleData struct {
Rules map[string][]string
Message map[string][]string
RequestCreateExampleData RequestCreateExampleData
}
func (std *CreateExampleData) BindRequestField(c *gin.Context) {
std.Rules = make(map[string][]string)
std.Rules["name"] = []string{"required"}
std.Rules["age"] = []string{"required"}
std.Rules["address"] = []string{"required"}
std.RequestCreateExampleData.Name = c.PostForm("name")
std.RequestCreateExampleData.Age, _ = strconv.Atoi(c.PostForm("age"))
std.RequestCreateExampleData.Address = c.PostForm("address")
}
/app/models/response
This package will store all response as struct data for giving API body response.
/app/helper
This pacakge will store every function that will reusuable in any function in controller.
included helper:
- `ChiperHelper.go` helper for encrypting or decrypting data.
- `ErrorHelper.go` is helper for error handling, reference for error handling is https://go.dev/blog/error-handling-and-go
- `JWTHelper.go` is helper for JWT token, reference for JWT token is github.com/dgrijalva/jwt-go
- `SimplifyError.go` helper for convert error response package validator v10 to human readable.
- `validate.go` helper for validate data from request.
/app/middlewares
This package will store every middeleware that will use in routes.
included helper:
- `errorHandler.go` middleware for error handling that use in routes(gin routes).
- `JWTMiddleware.go` middleware for JWT authorization that use in routes(gin routes).
- `JSONMiddleware.go` middleware for giving JSON response that use in routes(gin routes).
/libraries
This package will store any library that used in projects. But only for manually created/imported library, that not available when using go get package_name commands. Could be your own hashing algorithm, graph, tree etc.
include library:
- `httpResponse.go` library for giving response to client based on status code and message. The library will give response in JSON format using gin context default like: `c.JSON(202, SuccessResp{ Status: "Progress", Message: message }) `` and will return panic if some status code error occur. the panic will be handled by errorHandler middleware.
- `looger.go` library for logging data to file. this package require logrus library.
/config
This package will store any configuration and setting to used in project from any used service, could be mongodb,redis,mysql, elasticsearch, etc.
/config/database
This package for database configuration. File migrations
is for database migration that use package gorm.io/gorm. the migration will be run when project start.
/test/mockDatabase
This package will store any mockDatabase repository.
/test/tools/
This package for any tools that used for unit testing, included tools:
- `tools.go` a tools for setting driver mock, run test any manymore.
Database migration is a process of creating and updating database tables to match the current model definitions.
- for using database migration, you need to import package gorm.io/gorm.
- Create entity struct and use gorm to create table in
/models/entity
folder. for example:thepackage entity import "gorm.io/gorm" type ExampleData struct { gorm.Model Name string `gorm:"type:varchar(255);not null"` Age int `gorm:"type:int;not null"` Address string `gorm:"type:varchar(255);not null"` }
gorm.Model
is a for defining table primary key,created_at,updated_at,deleted_at. - Add this line code to
/config/database/migrations
file functionMigrate()
:package database import "golang-example/app/models/entity" func Migrate() { db := PostgreDB err := db.AutoMigrate(&entity.ExampleData{}) if err != nil { return } }
- Start project it will run migration automatically.
AutoMigrate will create tables, missing foreign keys, constraints, columns and indexes. It will change existing column’s type if its size, precision, nullable changed. It WON’T delete unused columns to protect your data. for more detail about gorm, please refer to https://gorm.io/docs/migration.html
- Create database in postgresql.
- Edit
.env
file and set database configuration based on your database configuration. example:DB_HOST=localhost DB_PORT=5432 DB_USER=postgres DB_PASSWORD=postgres DB_NAME=postgres
- in terminal go to project folder and run command:
go mod download
- then run the project:
go run main.go
- after application run, go to your POSTMAN application, in workspace, click on
import
button and selectFile
option. - Browser the collection file (
Example Data.postman_collection.json
) in project directory and select it. - click on
import
button and all API will show in POSTMAN. - Then you can test your API.
Maintainer : Muhammad Kasfi
If you are interested in becoming a maintainer please reach out to me https://github.com/ontakaspi