keyboard_tabSkip to content
listGoAPI Essential

แนะนำ CRUD / Request

สารบัญ

เริ่มต้น CRUD

Ref: https://docs.gofiber.io/guide/routing

มาเริ่มต้นด้วยการเพิ่ม HTTP Method ของ API กัน โดยปกติ Fiber ได้เตรียม method ไว้ให้ใช้ตามนี้

go
// HTTP methods
func (app *App) Get(path string, handlers ...Handler) Router
func (app *App) Head(path string, handlers ...Handler) Router
func (app *App) Post(path string, handlers ...Handler) Router
func (app *App) Put(path string, handlers ...Handler) Router
func (app *App) Delete(path string, handlers ...Handler) Router
func (app *App) Connect(path string, handlers ...Handler) Router
func (app *App) Options(path string, handlers ...Handler) Router
func (app *App) Trace(path string, handlers ...Handler) Router
func (app *App) Patch(path string, handlers ...Handler) Router

// Add allows you to specifiy a method as value
func (app *App) Add(method, path string, handlers ...Handler) Router

// All will register the route on all HTTP methods
// Almost the same as app.Use but not bound to prefixes
func (app *App) All(path string, handlers ...Handler) Router

ในเริ่มต้นนี้เราจะ

  • ทำ CRUD ซึ่งประกอบด้วย method ทั้งหมด 4 อันคือ GET, POST, PUT, DELETE
  • ทำการเพิ่มข้อมูลหนังสือเข้า struct เก็บไว้ก่อน
  • และทำการเรียกใช้ผ่าน struct ออกมาได้

** เนื่องจากเรายังไม่มี database = ตอนเราปิด process ไป ข้อมูลทั้งหมดจะหายไป

ซึ่ง function จะมีด้วยกันคือ

  • getBooks = ดึงหนังสือทั้งหมด
  • getBook = ดึงหนังสือตาม id
  • createBook = สร้างหนังสือใหม่
  • updateBook = แก้ไขข้อมูลหนังสือเดิมผ่าน id
  • deleteBook = ลบหนังสือ

ซึ่งเราจะนำทั้งหมดไปประกอบกับ REST API ออกมา

go
package main

import (
	"github.com/gofiber/fiber/v2"
	"strconv"
)

// Book struct to hold book data
type Book struct {
	ID     int    `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

var books []Book // Slice to store books

func main() {
	app := fiber.New()

	// Initialize in-memory data
	books = append(books, Book{ID: 1, Title: "1984", Author: "George Orwell"})
	books = append(books, Book{ID: 2, Title: "The Great Gatsby", Author: "F. Scott Fitzgerald"})

	// CRUD routes
	app.Get("/book", getBooks)
	app.Get("/book/:id", getBook)
	app.Post("/book", createBook)
	app.Put("/book/:id", updateBook)
	app.Delete("/book/:id", deleteBook)

	app.Listen(":8080")
}

// Handlers
func getBooks(c *fiber.Ctx) error {
	return c.JSON(books)
}

func getBook(c *fiber.Ctx) error {
	id, err := strconv.Atoi(c.Params("id"))
	if err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	for _, book := range books {
		if book.ID == id {
			return c.JSON(book)
		}
	}

	return c.SendStatus(fiber.StatusNotFound)
}

func createBook(c *fiber.Ctx) error {
	book := new(Book)

	if err := c.BodyParser(book); err != nil {
		return c.Status(fiber.StatusBadRequest).SendString(err.Error())
	}

	book.ID = len(books) + 1
	books = append(books, *book)

	return c.JSON(book)
}

func updateBook(c *fiber.Ctx) error {
	id, err := strconv.Atoi(c.Params("id"))
	if err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	bookUpdate := new(Book)
	if err := c.BodyParser(bookUpdate); err != nil {
		return c.Status(fiber.StatusBadRequest).SendString(err.Error())
	}

	for i, book := range books {
		if book.ID == id {
			book.Title = bookUpdate.Title
			book.Author = bookUpdate.Author
			books[i] = book
			return c.JSON(book)
		}
	}

	return c.SendStatus(fiber.StatusNotFound)
}

func deleteBook(c *fiber.Ctx) error {
	id, err := strconv.Atoi(c.Params("id"))
	if err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	for i, book := range books {
		if book.ID == id {
			books = append(books[:i], books[i+1:]...)
			return c.SendStatus(fiber.StatusNoContent)
		}
	}

	return c.SendStatus(fiber.StatusNotFound)
}

ให้ข้อสังเกต 1 อย่าง

  • ในการ return ของ function fiber Handler จะทำการ return เป็น error ออกไป
  • Fiber expected ผ่านการ return ว่า ค่า error ที่ออกมาต้องออกมาเป็น nil โดย
    • ถ้าเป็น success case = return nil ออกมา และ Fiber ก็จะทำการนำค่าที่ return ไปใช้ตามปกติ
    • ถ้าเป็น fail = return error ออกไป และ Fiber จะสามารถตรวจจับ error และ handle error ออกไปเป็นแบบเคสเดียวกับ go ได้

แยก file Router

Note

  • เราสามารถแยกไฟล์ออกจาก main.go ได้โดยสามารถแยกตาม package
  • หรือจะแยกให้อยู่ใน main เหมือนกัน แต่คนละ file ก็ได้ (อยู่ directory เดียวกัน แต่ประกาศเป็น package main เหมือนกัน ก็จะสามารถเรียกหากันได้)

แยกเป็น

  • main.go
go
package main

import (
	"github.com/gofiber/fiber/v2"
)

func main() {
	app := fiber.New()

	// Setup routes
	app.Get("/book", getBooks)
	app.Get("/book/:id", getBook)
	app.Post("/book", createBook)
	app.Put("/book/:id", updateBook)
	app.Delete("/book/:id", deleteBook)

	app.Listen(":8080")
}
  • book.go
go
package main

import (
	"github.com/gofiber/fiber/v2"
	"strconv"
)

// Book struct to hold book data
type Book struct {
	ID     int    `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

// Initialize in-memory data
var books []Book = []Book{
	{ID: 1, Title: "1984", Author: "George Orwell"},
	{ID: 2, Title: "The Great Gatsby", Author: "F. Scott Fitzgerald"},
}

// Handler functions

func getBooks(c *fiber.Ctx) error {
	return c.JSON(books)
}

func getBook(c *fiber.Ctx) error {
	id, err := strconv.Atoi(c.Params("id"))
	if err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	for _, book := range books {
		if book.ID == id {
			return c.JSON(book)
		}
	}

	return c.SendStatus(fiber.StatusNotFound)
}

func createBook(c *fiber.Ctx) error {
	book := new(Book)

	if err := c.BodyParser(book); err != nil {
		return c.Status(fiber.StatusBadRequest).SendString(err.Error())
	}

	book.ID = len(books) + 1
	books = append(books, *book)

	return c.JSON(book)
}

func updateBook(c *fiber.Ctx) error {
	id, err := strconv.Atoi(c.Params("id"))
	if err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	updatedBook := new(Book)
	if err := c.BodyParser(updatedBook); err != nil {
		return c.Status(fiber.StatusBadRequest).SendString(err.Error())
	}

	for i, book := range books {
		if book.ID == id {
			book.Title = updatedBook.Title
			book.Author = updatedBook.Author
			books[i] = book
			return c.JSON(book)
		}
	}

	return c.SendStatus(fiber.StatusNotFound)
}

func deleteBook(c *fiber.Ctx) error {
	id, err := strconv.Atoi(c.Params("id"))
	if err != nil {
		return c.SendStatus(fiber.StatusBadRequest)
	}

	for i, book := range books {
		if book.ID == id {
			books = append(books[:i], books[i+1:]...)
			return c.SendStatus(fiber.StatusNoContent)
		}
	}

	return c.SendStatus(fiber.StatusNotFound)
}

เวลา run command ให้เปลี่ยนจากการ run ราย file เป็น run ทุกไฟล์แทน

bash
# run แบบปกติ
go run  *.go
go run .

# run แบบ build
go build

เปิด CORS

  • กรณีอยากทดสอบต่อกับ Frontend = สามารถเปิด CORS (เพิ่ม config เข้าไปได้)
  • library fiber มี support เรื่อง CORS อยู่แล้ว
go
package main

import (
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
)

func main() {
	app := fiber.New()

	// Apply CORS middleware
	app.Use(cors.New(cors.Config{
		AllowOrigins: "*", // Adjust this to be more restrictive if needed
		AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH",
		AllowHeaders: "Origin, Content-Type, Accept",
	}))

	// Setup routes
	app.Get("/book", getBooks)
	app.Get("/book/:id", getBook)
	app.Post("/book", createBook)
	app.Put("/book/:id", updateBook)
	app.Delete("/book/:id", deleteBook)

	app.Listen(":8080")
}

upload file

Note

  • ลองมารับ file ด้วยรูปแบบอื่นนอกจาก JSON กันบ้าง เปลี่ยนมารับไฟล์จาก FormData แทน
  • เช่นเคสนี้ เราจะลอง upload ภาพเข้ามา > แล้วนำภาพมา upload ลง directory เอาไว้
go
package main

import (
	"github.com/gofiber/fiber/v2"
)

func uploadImage(c *fiber.Ctx) error {
	// Read file from request
	file, err := c.FormFile("image")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).SendString(err.Error())
	}

	// Save the file to the server
	err = c.SaveFile(file, "./uploads/" + file.Filename)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
	}

	return c.SendString("File uploaded successfully: " + file.Filename)
}

func main() {
	app := fiber.New()

	// Setup route
	app.Post("/upload", uploadImage)

	app.Listen(":8080")
}