DEV Community

Potchara Pruksasri
Potchara Pruksasri

Posted on • Edited on

GO: Database Restful API (GIN+GORM)

สร้าง Project

เริ่มต้นสร้าง Go project ชื่อ tripbooking (project ตัวอย่าง) กันก่อนเลย โดยสร้าง folder ชื่อ tripbooking แล้วเข้าไป folder รันคำสั่งสร้าง project และเปิด vscode ที่ folder นั้น

mkdir tripbooking 
cd tripbooking 
go mod init tripbooking 
code .
Enter fullscreen mode Exit fullscreen mode

สร้าง folder ที่จะเอาไว้เก็บไฟล์ต่างๆให้เป็นระเบียบ โดยในที่นี่ขึ้นอยู่กับความเหมาะสม สำหรับที่จะแนะนำให้สร้าง folder ทั้งหมด 4 folder คือ

  • controller = เอาไว้เก็บ restful api controller (path) ต่างๆ
  • service = ส่วนประมวลผลต่างๆของ Api
  • model = struct ของ database model
  • repository = ส่วนที่เชื่อมต่อไปยัง database

และสร้างไฟล์อีก 3 ไฟล์คือ

  • config.yaml = ไฟล์การตั้งค่าต่างๆของ project
  • server.go = ไฟล์เริ่มต้นการทำงานของ Server โดย GIN
  • main.go = ไฟล์เริ่มต้นการทำงานของ project

โดยใน main.go ก็จะมี code ของ func main() เริ่มต้น ตามรูป

Image description


เพิ่ม libraries ที่เกี่ยวข้องกับ project

go get github.com/gin-gonic/gin
go get gorm.io/gorm
go get gorm.io/driver/mysql
go get github.com/spf13/viper
go install github.com/cosmtrek/air@latest
Enter fullscreen mode Exit fullscreen mode

สร้าง Server ด้วย GIN

ที่ไฟล์ server.go สร้าง server ของ restful api ด้วย GIN

package main

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

func StartServer() {
    router := gin.Default()
    router.SetTrustedProxies(nil)
    router.GET("/", hello)
    port := os.Getenv("PORT")
    if port == "" {
        port = "8888"
    }
    router.Run(":" + port)
}

func hello(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "message": "Hello World!!!",
    })
}

Enter fullscreen mode Exit fullscreen mode

ใน main.go สั่งให้เรียก func StartServer()

package main

func main() {
    StartServer()
}

Enter fullscreen mode Exit fullscreen mode

รันโปรแกรมด้วย plugin air เพื่อให้ auto reload เมื่อเราแก้ไข code โดยต้องกำหนดค่าเริ่มต้นให้ก่อน

air init
air
Enter fullscreen mode Exit fullscreen mode

ทดสอบเรียก Api อาจใช้ Thunder Client, Talend หรือ Postman ก็ได้

Image description


ติดต่อ Database ด้วย GORM

ตั้งค่า dsn ที่จะเชื่อมไปยังฐานข้อมูล MySQL ในไฟล์ config.yaml (อย่าลืมเปิด Server MySQL ตัวอย่างล่ะ หรือ ถ้าเชื่อมไปยัง Server ที่ Online อยู่ก็ได้เลย)

mysql:
  dsn: "username:password@tcp(localhost:3306)/tripbooking?collation=utf8mb4_unicode_ci&parseTime=true"
Enter fullscreen mode Exit fullscreen mode

จากนั้น สร้างไฟล์ database.go ใน folder repository แล้วเขียนคำสั่งใช้การติดต่อไปยังฐานข้อมูล

package repository


import (
    "github.com/spf13/viper"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

var db *gorm.DB

func NewDatabaseConnection() (*gorm.DB, error) {
    if db == nil {
        viper.SetConfigName("config")
        viper.AddConfigPath(".")
        err := viper.ReadInConfig()
        if err != nil {
            panic(err)
        }
        dsn := viper.GetString("mysql.dsn")
        dialector := mysql.Open(dsn)
        db, err = gorm.Open(dialector)
        if err != nil {
            return nil, err
        }
    }
    return db, nil
}
Enter fullscreen mode Exit fullscreen mode

ซึ่งได้จะ func NewDatabaseConnection() ไว้ใช้ในการติดต่อไปยังฐานข้อมูลที่เราได้ตั้งค่าไว้


ติดต่อไปดึงข้อมูลจากฐานข้อมูล (Table) ต้องมี Model (Entity) ที่เป็นตัวแทนของ Table นั้นๆ ตัวอย่างเช่น จะดึงข้อมูลจาก Table destination ก็ต้องสร้าง Model Destination ขี้นมาก่อน

สร้างไฟล์ destinationModel.go ใน folder model และเขียน model ตามโครงสร้างของ table destination

package model

type Destination struct {
    ID   uint   `gorm:"column:idx;primaryKey"`
    Zone string `gorm:"column:zone;size:255"`
}

func (Destination) TableName() string {
    return "destination"
}
Enter fullscreen mode Exit fullscreen mode

ในกรณีที่ไม่อยากเขียน struct ของ model เอง ให้ใช้วิธีนี้คือ

  • เข้า phpmyadmin แล้วรันคำสั่ง (destination คือชื่อ table ที่ต้องการ) โดยเลือกการแสดงผลตรง option ให้เป็น แบบ Full Text
SHOW CREATE TABLE destination
Enter fullscreen mode Exit fullscreen mode

Image description

  • จะได้คำสั่งสร้าง Table ออกมา ให้ copy คำสั่ง Create นั้นไว้ แล้วเข้าที่เวบ https://sql2gorm.mccode.info
  • วางคำสั่ง sql ลงไป เวบจะสร้าง struct ของ model ให้ตามรูป (เลือก json tag เป็น include ด้วยนะ ถ้าจะใช้เป็น DTO รับข้อมูลรูปแบบ json)

Image description


สร้าง Repositoy ที่เป็นตัวเชื่อมไปยังฐานข้อมูล โดยในการเขียนนี้ เราจะสร้างให้อยู่ในรูปแบบ Interface (ถ้ายังไม่รู้จักจะงงๆหน่อยนะ) เพื่อให้สะดวกต่อการใช้งาน อาจดูยุ่งยาก แต่มันดีในอนาคตเวลาโปรแกรมมันขยายและซับซ้อนขึ้น

สร้างไฟล์ destinationRepository.go ไว้ใน folder repository และเขียนให้ไปดึงข้อมูลของ table destination มา (FindAll) โดยมี pattern ของการเขียนดังตัวอย่าง

package repository

import (
    "tripbooking/model"

    "gorm.io/gorm"
)

// repository struct (<name>DB)
type destinationRepo struct {
    db *gorm.DB
}

// repository contructor (New<name>Repository)
func NewDestinationRepository() DestinationRepository {
    db, err := NewDatabaseConnection()
    if err != nil {
        return nil
    }
    return destinationRepo{db: db}
}

// repository interface (<name>Repository)
type DestinationRepository interface {
    FindAll() (*[]model.Destination, error)
}

// implement functions conformed interface
func (d destinationRepo) FindAll() (*[]model.Destination, error) {
    destinations := []model.Destination{}
    result := d.db.Find(&destinations)
    if result.Error != nil {
        return nil, result.Error
    }
    return &destinations, nil
}
Enter fullscreen mode Exit fullscreen mode

เมื่อสร้าง Repository เสร็จแล้ว ขี้นต่อไปคือการสร้าง Service เพื่อใช้ประมวลผลต่างๆ

เราจะไม่ประมวลใน repository และ controller ถ้าไม่จำเป็น

สร้างไฟล์ destinationService.go ใน folder service เพื่อใช้เขียนการประมวลผลในรูปแบบของ service ซึ่งจะมี pattern การเขียนแบบ interface เหมือน repository

package service

import (
    "tripbooking/model"
    "tripbooking/repository"
)

// service struct (<name>Serv)
type destinationServ struct{}

// service contructor (New<name>Service)
func NewDestinationService() DestinationService {
    return destinationServ{}
}

// create repositories
var destinationRepo = repository.NewDestinationRepository()

// service interface (<name>Service)
type DestinationService interface {
    GetAllDestinations() (*[]model.Destination, error)
}

func (d destinationServ) GetAllDestinations() (*[]model.Destination, error) {

    destinations, err := destinationRepo.FindAll()
    if err != nil {
        return nil, err
    }
    return destinations, nil
}
Enter fullscreen mode Exit fullscreen mode

เมื่อได้ส่วนประมวลผลแล้ว ต่อไปจะทำการสร้าง controller เพื่อให้ user สามารถเรียกใช้งาน api เราได้ โดยสร้างไฟล์ destinationController.go ใน folder controller แล้วเขียนเรียกใช้งาน service ดังตัวอย่าง

package controller

import (
    "net/http"
    "tripbooking/service"

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

// create services
var destinationServ = service.NewDestinationService()

// Initiate Controller
func NewDestinationController(router *gin.Engine) {
    r := router.Group("/destination")
    {
        r.GET("", findAll)
    }
}

// Implement Controller
func findAll(c *gin.Context) {
    destinations, err := destinationServ.GetAllDestinations()
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{
            "error": err,
        })
    }
    c.JSON(http.StatusOK, destinations)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อได้ controller แล้ว ต่อไป จะเป็นการผูก Controller เข้าไปยัง Server ที่เราสร้าง

มาที่ไฟล์ server.go และเพิ่ม (Register) Controller ของเราเข้าไป ตาม code ตัวอย่าง

func StartServer() {
    router := gin.Default()
    router.SetTrustedProxies(nil)
    router.GET("/", hello)
    port := os.Getenv("PORT")
    if port == "" {
        port = "8888"
    }

    // **** Register controllers
    controller.NewDestinationController(router)

    router.Run(":" + port)
}
Enter fullscreen mode Exit fullscreen mode

จากนั้นทดลองเรียก api โดยใช้ /destination จะได้ผลลัพธ์ตามตัวอย่าง

Image description


เสร็จสิ้นการเริ่มต้น GO Project ในการสร้าง Restful api เชื่อมต่อไปยัง MySQL ด้วย GIN และ GORM

Top comments (0)