ยินดีต้อนรับเข้าสู่ EP ที่ 2 ของ Go Tutorial Series ของเรากันครับ ใน EP ที่แล้ว เราได้ทำการสร้าง API โดยใช้ Gin Framework ขึ้นมากัน โดยเราจะเก็บข้อมูลต่างๆ ของ API ไว้ใน Memory ของ Server ของเรา

ซึ่งใน EP นี้เราจะมาเรียนรู้วิธีการเก็บข้อมูลเหล่านี้ลงใน Database ด้วย Gorm Library กันครับ โดยเราจะทำการเก็บข้อมูลลงใน SQLite Database ที่เป็น Database ประเภทที่น้ำหนักเบา และใช้งานได้ง่าย เพราะมันจะเก็บข้อมูลเป็นไฟล์ปกติในเครื่องเราเลย (ซึ่งจริงๆ แล้ว Gorm สามารถเอาไปใช้กับ SQL Database ได้เหมือนกันครับไม่ว่าจะเป็น MySQL หรือ PostgresSQL ก็สามารถทำได้เช่นกัน)

สำหรับคนที่ยังไม่เคยสร้าง REST API ด้วยภาษา Go หรือ Gin Framework มาก่อน ผมแนะนำให้ลองอ่านบทความ EP. 1 ของเราที่นี่ก่อนได้เลยครับ

เริ่มต้นจากการ Implement แบบง่ายๆ

1. Import Gorm และ SQLite Driver

Import Gorm และ SQLite driver ในไฟล์ main.go ของเรา

ติดตั้ง dependencies ต่างๆ โดยใช้คำสั่ง

go get
go mod tidy

2. ทำการ Initialize Gorm ใน main function

เราเริ่มต้นจากการสร้าง global variable ที่ชื่อdb เพื่อทำการเก็บ instance จากการสร้าง instance ด้วยคำสั่ง gorm.Open ประกอบกับการใช้ driver ที่สร้างโดยคำสั่ง sqlite.Open ซึ่งเราทำการตั้งชื่อไฟล์ของฐานข้อมูลเราว่า test.db ซึ่งจริงๆ สามารถที่จะตั้งชื่อเป็นอะไรก็ได้ตามต้องการ แค่ให้มีนามสกุลเป็น .db ก็เพียงพอ

หลังจากนั้นเราใช้คำสั่ง db.AutoMigrate ในการสร้าง Table ต่างๆ ของเราเข้ามา ซึ่งเราจะต้องทำการใส่ pointer ของ Struct Book ของเราเข้าไปเพื่อทำให้ Gorm รู้ได้ว่า Table ต่างๆ นั้นต้องสร้าง Table ชื่ออะไร และมีหน้าตาแบบไหนบ้าง อย่างเช่นในกรณีนี้ ชื่อ Table ก็จะเป็น books ตามชื่อของ Struct Book ของเรานั่นเอง

3. ปรับ List Handler ให้มาใช้ Gorm

เรามาเริ่มต้นจากการเปลี่ยนใน GET handler ของเรา จากเดิมที่เป็น Logic ในการเอาข้อมูล Slice มาเป็น Response เปลี่ยนเป็นการดึงข้อมูลจาก Database แทนโดยใช้คำสั่ง db.Find(&books)

สังเกตว่าข้อมูลที่เรา query ออกมาจาก database นั้นจะไม่ได้ออกมาจาก return value ของคำสั่ง db.Find แต่ว่ามันจะอยู่ในตัวแปร books ที่เราใส่เป็น argument เข้าไปแทน และในส่วนของ return value จะเป็นข้อมูลต่างๆ เกี่ยวกับการ query แทน เช่น error ต่างๆ ที่อาจจะเกิดขึ้นในตอนที่ query ข้อมูลเป็นต้น

ซึ่งเมื่อการ query จาก database นั้น อาจจะมีโอกาสที่เกิด error ขึ้นได้ ดังนั้นเราก็จำเป็นที่จะต้องทำการ handle error เหล่านี้ และคืน response เป็น 500 Internal Server Error พร้อมกับ error message ไปยัง client ของเรา

4. ปรับ Create Handler ให้มาใช้ Gorm

ต่อมาเรามาทำการเปลี่ยน Logic เดิมใน POST handler เป็นการ insert ข้อมูลของเราเข้าไปใน Database แทนด้วยคำสั่ง db.Create(&book) ซึ่งสังเกตว่าเราจะทำการส่งเป็น pointer เข้าไป เนื่องจากว่าอาจจะมี field บางอย่างที่ตัว database จะสร้างให้เราแบบอัตโนมัติ เช่น auto-increment id หรือ timestamps ต่างๆ

5. ปรับ Delete Handler ให้มาใช้ Gorm

และสุดท้าย เรามาปรับ DELETE handler ของเราให้มาใช้คำสั่ง db.Delete ในการลบข้อมูลจาก database แทน ซึ่งเราจะต้องใส่ argument เข้าไป 2 อัน นั่นก็คือ Pointer ของ Struct Book ของเรา และ ID ของ row ที่เราต้องการจะลบเข้าไป

สาเหตุที่เราต้องใส่ Pointer ของ Struct Book เข้าไปนั้นก็เพราะว่า Gorm จะได้รู้ว่าเรากำลังต้องการจะ Delete ข้อมูลใน Table ไหน ซึ่งในทีนี้ก็คือ Table books ของเรานั่นเอง

6. ตอนนี้ไฟล์ main.go จะมีหน้าตาเป็นแบบนี้

วิธีการทดสอบ API

ตอนนี้เราก็สามารถทดสอบ API ของเราเหมือนกับที่เราเคยทำก่อนหน้านี้ด้วย REST API client extension บน VSCode สามารถดูวิธีการเพิ่มเติมได้ที่ Tutorial EP.1

ปัญหาของการใช้ Global DB Variables

ตอนนี้เรากำลังใน global variable ชื่อ db ภายในโค้ดของเรา ซึ่งมันสามารถทำให้เกิดปัญหาในอนาคตได้ เพราะมีโอกาสที่บาง function ที่สามารถเข้ามาเปลี่ยนแปลงตัวแปร db ของเราได้ ซึ่งเราจะมาดูวิธีการแก้ไขปัญหานี้กัน

ใช้วิธีการ Dependency Injection

หลักการ Dependency Injection เราอาจจะคุ้นเคยกับภาษาในรูปแบบที่เป็น OOP อย่าง Java และมันอาจจะฟังดูยากและซับซ้อน แต่ในความเป็นจริงแล้ว มันเป็นเพียงแค่การเอา dependencies ต่างๆ ที่เราต้องการใช้ ตัวอย่างเช่น ตัวแปร db ของเรานั้น ให้คนอื่นเป็นคนสร้าง และส่งต่อให้กับ class ที่จะใช้ db ผ่าน constructor ได้

ซึ่งในภาษา Go จะไม่มี class เหมือนกับภาษาอื่นๆ ที่เป็น OOP แต่ว่าเรามีซึ่งที่ใกล้เคียงกันนั่นก็คือ Struct นั่นเอง ซึ่งเราจะทำการสร้าง Handler struct ขึ้นมาเพื่อทำการเก็บตัวแปร dependencies ต่างๆ ของเรา ซึ่งในที่นี้ก็คือ *gorm.DB นั่นเอง และเราก็ทำการเปลี่ยน handler function ของเราเป็น method แทน และสุดท้ายก็สร้าง method สำหรับการสร้าง Handler struct นี้ก็เป็นอันเสร็จ

พูดแล้วก็ไม่ค่อยเห็นภาพ เรามาดูตัวอย่างการเขียนโค้ดกันเลยดีกว่า!

1. สร้าง Handler Struct และฟังก์ชัน newHandler

เราจะทำการสร้าง struct ที่ชื่อว่า Handler ขึ้นมาพร้อมกับ function newHandler ของเราที่ทำหน้าที่คล้ายๆ กับ constructor ในภาษา OOP อื่นๆ โดยการจะสร้าง Handler ด้วยnewHandler นั้นจะต้องใช้ dependency อย่าง *gorm.DB

2. Make handler function to be method and use the db from Handler

ต่อมาเราจะทำการเปลี่ยน Handler Function ต่างๆ ก่อนหน้านี่ให้กลายเป็น Method ของ Struct Handler ซึ่งเราสามารถทำได้อย่างง่ายดายโดยการใส่ Receiver (h *Handler) เข้าไปในแต่ละ method แค่นี้เราก็ได้ method มาใช้งานแล้ว

และเราก็เปลี่ยนจากการที่เราใช้ Global Variable db มาเป็นใช้ Field h.db ใน Struct Handler แทน

3. เปลี่ยน main function ให้มาใช้ Handler struct แทน

สุดท้าย เราก็จะมาสร้าง db instance ใน main function และทำการส่ง instance นี้ไปใน newHandler function เพื่อทำการสร้าง Handler ขึ้นมา และเราก็สามารถใช้ method ของ Handler มาแทน function ของเราก่อนหน้านี้ได้เลยตามโค้ดด้านบนนี้

สุดท้ายหน้าตาของ Code เราจะเป็นแบบนี้

เสร็จแล้ว! ที่นี้เราก็สามารถสร้าง REST API โดยเก็บข้อมูลลง Database ด้วย Gorm ได้เรียบร้อยแล้ว

ตอนต่อไปใน Series นี้

ใน EP ถัดไป เราจะมาลองดูวิธีการทำ Authorization กับ API ของเราโดยการใช้ JSON Web Token (JWT) และ Middleware ของ Gin

More in:Technology

Comments are closed.