GO: Database Restful API (GIN+GORM)
Potchara Pruksasri
Posted on September 4, 2022
สร้าง Project
เริ่มต้นสร้าง Go project ชื่อ tripbooking (project ตัวอย่าง) กันก่อนเลย โดยสร้าง folder ชื่อ tripbooking แล้วเข้าไป folder รันคำสั่งสร้าง project และเปิด vscode ที่ folder นั้น
mkdir tripbooking
cd tripbooking
go mod init tripbooking
code .
สร้าง 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() เริ่มต้น ตามรูป
เพิ่ม 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
สร้าง 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!!!",
})
}
ใน main.go สั่งให้เรียก func StartServer()
package main
func main() {
StartServer()
}
รันโปรแกรมด้วย plugin air เพื่อให้ auto reload เมื่อเราแก้ไข code โดยต้องกำหนดค่าเริ่มต้นให้ก่อน
air init
air
ทดสอบเรียก Api อาจใช้ Thunder Client, Talend หรือ Postman ก็ได้
ติดต่อ Database ด้วย GORM
ตั้งค่า dsn ที่จะเชื่อมไปยังฐานข้อมูล MySQL ในไฟล์ config.yaml (อย่าลืมเปิด Server MySQL ตัวอย่างล่ะ หรือ ถ้าเชื่อมไปยัง Server ที่ Online อยู่ก็ได้เลย)
mysql:
dsn: "username:password@tcp(localhost:3306)/tripbooking?collation=utf8mb4_unicode_ci&parseTime=true"
จากนั้น สร้างไฟล์ 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
}
ซึ่งได้จะ 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"
}
ในกรณีที่ไม่อยากเขียน struct ของ model เอง ให้ใช้วิธีนี้คือ
- เข้า phpmyadmin แล้วรันคำสั่ง (destination คือชื่อ table ที่ต้องการ) โดยเลือกการแสดงผลตรง option ให้เป็น แบบ Full Text
SHOW CREATE TABLE destination
- จะได้คำสั่งสร้าง Table ออกมา ให้ copy คำสั่ง Create นั้นไว้ แล้วเข้าที่เวบ https://sql2gorm.mccode.info
- วางคำสั่ง sql ลงไป เวบจะสร้าง struct ของ model ให้ตามรูป (เลือก json tag เป็น include ด้วยนะ ถ้าจะใช้เป็น DTO รับข้อมูลรูปแบบ json)
สร้าง 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
}
เมื่อสร้าง 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
}
เมื่อได้ส่วนประมวลผลแล้ว ต่อไปจะทำการสร้าง 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)
}
เมื่อได้ 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)
}
จากนั้นทดลองเรียก api โดยใช้ /destination จะได้ผลลัพธ์ตามตัวอย่าง
เสร็จสิ้นการเริ่มต้น GO Project ในการสร้าง Restful api เชื่อมต่อไปยัง MySQL ด้วย GIN และ GORM
Posted on September 4, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 27, 2024