Setting Project ⚙️
STEP 0: ไฟล์ตั้งต้น
แนะนำให้เริ่มอ่านจาก ตอนที่1 มาก่อนนะครับ แต่หากใครที่พึ่งเข้ามาเริ่มอ่านในบทความนี้ ส่วนนี้คือไฟล์ตั้งต้นครับผม
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
func main() {
fmt.Println("hello world")
// fiber instance
app := fiber.New()
// routes
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("hello world 🌈")
})
// app listening at PORT: 3000
app.Listen(":3000")
}
STEP 1: ออกแบบ RESTful API
ก่อนจะลงมือสร้าง เราก็ต้องมาออกแบบ API กันก่อน โดยเราจะมาสร้าง API ของอาหารกันดังนี้เลย
GET "/foods" ค้นหาข้อมูลอาหารทั้งหมด
GET "/foods/:id" ค้นหาข้อมูลอาหารตามไอดี
POST "/foods" สร้างข้อมูลอาหาร
PUT "/foods/:id" แก้ไขข้อมูลอาหารตามไอดี
DELTE "/foods/:id" ลบข้อมูลอาหารตามไอดี
STEP 2: สร้าง Struct และจำลอง Data
ในตอนนี้เราจะยังไม่มีการเชื่อมต่อถึง Database ดังนั้นเราจะใช้โครงสร้างข้อมูลภายในไฟล์ main.go
แบบง่ายๆ ดังนี้
type Food struct {
ID uint `json:"id"`
Name string `json:"name"`
Price uint `json:"price"`
}
var foods = []Food{
{ID: 1, Name: "ต้มยำกุ้ง", Price: 140},
{ID: 2, Name: "ไก่ทอด", Price: 100},
{ID: 3, Name: "ก๋วยเตี๋ยว", Price: 30},
{ID: 4, Name: "เบอร์เกอร์", Price: 149},
}
stuct
คือโครงสร้างข้อมูลที่เรากำหนดได้เอง โดยจะประกอบไปด้วย fields ต่างๆ ในที่นี้จะเป็นโครงสร้างข้อมูลของ Food
สังเกตได้ว่าจะมี Tag `json:”id”`
ต่อท้ายอยู่ ซึ่ง tag เหล่านี้มีหน้าที่บอกให้ Go สามารถรู้ว่าควรจะเชื่อมเอา Field อันไหนใน JSON มาเชื่อมกับ Field ไหนใน Struct ของเรานั่นเอง
STEP 3: ติดตั้ง Postman
เนื่องจากมีการใช้หลาย Method ในบทความนี้ (Get, Post, Put, Delete, …) หากทดสอบตามบทความที่แล้วที่เราเข้าผ่าน URL จะสามารถทดสอบได้แค่เฉพาะ Method Get เราจึงใช้ Postman ในการทดสอบ API ของเรา หากใครยังไม่ได้ติดตั้งสามารถดาวน์โหลดได้ที่นี้เลย 👉 download
มาเริ่มสร้าง RESTful API กันเลย 🌈
STEP 1: สร้าง API สำหรับค้นหาข้อมูลทั้งหมด
Method GET
Path /foods
Description ค้นหาข้อมูลอาหารทั้งหมด
Return JSON
จากที่เราออกแบบไว้ข้างต้น เราสามารถ implement ได้โค้ดดังนี้
app.Get("/foods", func(c *fiber.Ctx) error {
return c.JSON(foods)
})
ทดสอบ 🧪
เราจะใช้ Postman โดยใส่ URL ของเราเป็น http://localhost:3000/foods และกด Send จะได้ผลลัพธ์หน้าตาแบบนี้ ซึ่งก็คือข้อมูลของ foods
นั้นเอง
STEP 2: สร้าง API สำหรับค้นหาข้อมูลตามไอดี
Method POST
Path /foods
Description สร้างข้อมูลอาหาร
Return JSON
เราจะมีการส่งเลข id
ผ่านลงบน path เช่น /foods/1
ซึ่งหมายถึงว่าค้นหาข้อมูลอาหารไอดีที่ 1
app.Get("/foods/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
for _, food := range foods {
if food.ID == id {
return c.JSON(food)
}
}
return nil
})
อธิบาย 💭
- เราสามารถใช้ Feature ของ Fiber ที่เป็น Path Parameters โดยแทรก
:id
เข้าไปใน path และเรียกใช้ผ่านคำสั่งid := c.Params(“id”)
เพื่อดึง Param เข้ามาใช้เป็นตัวแปรของเรา - หลังจากทำการวนลูปภายใน
foods
เพื่อหาfood
ที่มีเลขID
ตรงกับเลขid
ที่เราดึงมาจาก Params เมื่อเจอก็จะส่งข้อมูลfood
นั้นกลับเป็นJSON
ทดสอบ 🧪
ใช้ Postman โดยใส่ URL ของเราเป็น http://localhost:3000/foods/1 และกด Send จะได้ผลลัพธ์หน้าตาแบบนี้ ซึ่งก็คือข้อมูลของ food
นั้นเอง อย่าลืมเทสกับเลข id
ตัวอื่นๆด้วยนะครับ
STEP 3: สร้าง API สำหรับสร้างข้อมูลอาหาร
Method POST
Path /foods
Description สร้างข้อมูลอาหาร
Return JSON
เราจะมาสร้างอาหารกันบ้างดีกว่า ตามโค้ดด้านล่างนี้เลย
app.Post("/foods", func(c *fiber.Ctx) error {
var food Food
if err := c.BodyParser(&food); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
foods = append(foods, food)
return c.JSON(food)
})
อธิบาย 💭
- เนื่องจากเราจะทำการสร้างข้อมูลอาหาร พวกรายละเอียดของอาหารต่างๆนั้น จะถูกส่งมาผ่าน Body ของ Request เราจึงใช้คำสั่ง
c.BodyParser(&food)
เพื่อทำการ Bind ข้อมูลจาก Body มาเก็บไว้ในตัวแปร Struct ที่มีชื่อว่าfood
- เมื่อรับข้อมูลมาแล้วไม่เกิด Error ใดๆ เราจะใช้คำสั่ง
foods = append(foods, food)
เพื่อเพิ่มข้อมูลลงตัวแปรfoods
และคืนค่าfood
ที่เราพึ่งสร้างส่งคืนกลับ Client นั่นเอง
ทดสอบ 🧪
มาลองทดสอบกับ Postman กันต่อเลย แต่จะมีสิ่งที่เปลี่ยนไปเล็กน้อย
- เข้า POSTMAN
- ใช้ URL http://localhost:3000/foods
- เปลี่ยน Method เป็น
POST
- กดไปที่
Body -> raw -> JSON
เพื่อจะทำการส่งข้อมูลประเภท JSON ผ่าน Body - ใส่ข้อมูลอาหารที่เราต้องการจะสร้าง ในตอนนี้ผมรู้สึกอยากกินกะเพราะหมูกรอบดังนั้น Body ของผมจะหน้าตาแบบนี้
{
"id": "5",
"name": "กะเพราหมูกรอบ",
"price": 50
}
และเมื่อทดสอบผ่าน Postman จะได้ผลลัพธ์แบบนี้
STEP 4: สร้าง API สำหรับแก้ไขข้อมูลอาหารตามไอดี
Method PUT
Path /foods/:id
Description แก้ไขข้อมูลอาหารตามไอดี
Return JSON
เราจำเป็นต้องสร้าง struct ขึ้นมาใหม่โดยมีชนิดข้อมูลเป็นดังนี้
type EditFood struct {
Name string `json:"name"`
Price uint `json:"price"`
}
จากนั้นไปลุยต่อกันที่โค้ดด้านล่างนี้เลย
app.Put("/foods/:id", func(c *fiber.Ctx) error {
var editFood EditFood
if err := c.BodyParser(&editFood); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
id := c.Params("id")
for i := range foods {
if foods[i].ID == id {
foods[i].Name = editFood.Name
foods[i].Price = editFood.Price
return c.JSON(foods[i])
}
}
return nil
})
อธิบาย 💭
- สร้างข้อมูล Struct
editFood
ขึ้นมาเพื่อรับเจ้า Body จาก Request - สร้างตัวแปร
id
ที่ดึงข้อมูลมาจาก Param ของเรา ซึ่งเจ้าid
นี่แหละที่เราจะทำการแก้ไขข้อมูลกัน - ทำการวนลูปภายใน
foods
เพื่อหา food id นั้นจากนั้นทำการแก้ค่า และ return ค่ากลับเป็น JSON
ทดสอบ 🧪
- เข้า POSTMAN
- ใช้ URL http://localhost:3000/foods/1
- เปลี่ยน Method เป็น
PUT
- ใส่
Name
และPrice
ที่ต้องการจะเปลี่ยนดังนี้
{
"name": "ราเมน",
"price": 120
}
และเมื่อทดสอบจะได้ผลลัพธ์ดังนี้
STEP 5: สร้าง API สำหรับลบข้อมูลอาหารตามไอดี
Method DELETE
Path /foods/:id
Description ลบข้อมูลอาหารตามไอดี
Return String
มาถึง Method ตัวสุดท้ายกันแล้วครับ นั่นก็คือ Delete นั่นเอง โดยโค้ดจะเป็นดังนี้
app.Delete("/foods/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
for i, food := range foods {
if food.ID == id {
foods = append(foods[:i], foods[i+1:]...)
return c.sen("delete success")
}
}
return nil
})
อธิบาย 💭
- สร้างตัวแปร
id
ที่ดึงข้อมูลมาจาก Param ของเรา เพื่อนำไปค้นหาในfoods
- ทำการวนลูปเพื่อหา
food
ตัวที่เราต้องการจะลบ - เมื่อเจอจะใช้คำสั่ง
foods = append(foods[:i], foods[i+1:]...)
ซึ่งเป็นการเลือกเฉพาะ food ที่อยู่ก่อนหน้าและอยู่หลัง food ตัวที่ต้องการจะลบ เก็บเข้าสู่ตัวแปรfoods
ของเรา - return ค่ากลับเป็นคำว่า “delete success”
ทดสอบ 🧪
- เข้า POST
- ใช้ URL http://localhost:3000/foods/1
- เปลี่ยน Method เป็น
DELETE
Refactor Code กันดีกว่า
นี่คือโค้ดทั้งหมดของเรา หน้าตาจะเป็นแบบนี้
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
)
type Food struct {
ID string `json:"id"`
Name string `json:"name"`
Price uint `json:"price"`
}
type EditFood struct {
Name string `json:"name"`
Price uint `json:"price"`
}
var foods = []Food{
{ID: "1", Name: "ต้มยำกุ้ง", Price: 140},
{ID: "2", Name: "ไก่ทอด", Price: 100},
{ID: "3", Name: "ก๋วยเตี๋ยว", Price: 30},
{ID: "4", Name: "เบอร์เกอร์", Price: 149},
}
func main() {
fmt.Println("hello world")
// fiber instance
app := fiber.New()
// routes
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("hello world 🌈")
})
app.Get("/foods", func(c *fiber.Ctx) error {
return c.JSON(foods)
})
app.Get("/foods/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
for _, food := range foods {
if food.ID == id {
return c.JSON(food)
}
}
return nil
})
app.Post("/foods", func(c *fiber.Ctx) error {
var food Food
if err := c.BodyParser(&food); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
foods = append(foods, food)
return c.JSON(food)
})
app.Put("/foods/:id", func(c *fiber.Ctx) error {
var editFood EditFood
if err := c.BodyParser(&editFood); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
id := c.Params("id")
for i := range foods {
if foods[i].ID == id {
foods[i].Name = editFood.Name
foods[i].Price = editFood.Price
return c.JSON(foods[i])
}
}
return nil
})
app.Delete("/foods/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
for i, food := range foods {
if food.ID == id {
foods = append(foods[:i], foods[i+1:]...)
return c.SendString("delete success")
}
}
return nil
})
// app listening at PORT: 3000
app.Listen(":3000")
}
สังเกตได้ว่าโค้ดเราดูอ่านค่อนข้างยาก เพื่อความง่ายในการอ่านและการจัดการ เราจะทำ Handler แยกออกมาเป็น Function ของตัวมันเอง
func main() {
fmt.Println("hello world")
// fiber instance
app := fiber.New()
// routes
app.Get("/", helloWorld)
app.Get("/foods", getFoods)
app.Get("/foods/:id", getFoodByID)
app.Post("/foods", createFood)
app.Put("/foods/:id", updateFoodByID)
app.Delete("/foods/:id", deleteFoodByID)
// app listening at PORT: 3000
app.Listen(":3000")
}
func helloWorld(c *fiber.Ctx) error {
return c.SendString("hello world 🌈")
}
func getFoods(c *fiber.Ctx) error {
return c.JSON(foods)
}
func getFoodByID(c *fiber.Ctx) error {
id := c.Params("id")
for _, food := range foods {
if food.ID == id {
return c.JSON(food)
}
}
return nil
}
func createFood(c *fiber.Ctx) error {
var food Food
if err := c.BodyParser(&food); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
foods = append(foods, food)
return c.JSON(food)
}
func updateFoodByID(c *fiber.Ctx) error {
var editFood EditFood
if err := c.BodyParser(&editFood); err != nil {
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
}
id := c.Params("id")
for i := range foods {
if foods[i].ID == id {
foods[i].Name = editFood.Name
foods[i].Price = editFood.Price
return c.JSON(foods[i])
}
}
return nil
}
และนำพวก Struct มาไว้ด้านล่าง ที่นี้โค้ดของเราก็ดูดีขึ้นแล้วว 😍
type Food struct {
ID string `json:"id"`
Name string `json:"name"`
Price uint `json:"price"`
}
type EditFood struct {
Name string `json:"name"`
Price uint `json:"price"`
}
var foods = []Food{
{ID: "1", Name: "ต้มยำกุ้ง", Price: 140},
{ID: "2", Name: "ไก่ทอด", Price: 100},
{ID: "3", Name: "ก๋วยเตี๋ยว", Price: 30},
{ID: "4", Name: "เบอร์เกอร์", Price: 149},
}
Github
ลิ้ง Github สำหรับโค้ดในบทความนี้ครับ 👇🏻
จบกันไปแล้วในตอนที่ 2 เดี๋ยวในตอนต่อไปเราจะมีการเชื่อมต่อกับฐานข้อมูลกันครับขอบคุณที่แวะเข้ามาอ่านนะครับผม กราบๆๆๆ 🙏🏻🙏🏻🙏🏻