Programming
น้องๆ เคยสงสัยไหมว่า Web API มันคืออะไร แล้วทำไมใครๆ ก็พูดถึงแต่ Go Golang ในวงการนี้? สมัยผมทำร้านเน็ต SiamCafe.net เมื่อ 20 กว่าปีก่อน (โอ้โห นานมาก!) ตอนนั้นยังไม่มีอะไรแบบนี้เลย ทุกอย่างมันซับซ้อนกว่าเยอะ
Web API ก็เหมือนเป็น "พนักงานเสิร์ฟ" ในร้านอาหารดิจิทัลของเรานี่แหละครับ ลูกค้า (Client) สั่งอาหาร (Request) พนักงานเสิร์ฟก็ไปเอาอาหารจากครัว (Server) แล้วเสิร์ฟให้ลูกค้า (Response) ง่ายๆ แค่นี้เอง! ส่วน Go Golang เนี่ย เป็นเหมือน "เชฟ" ที่ทำอาหารได้อร่อย รวดเร็ว และประหยัดต้นทุน เพราะมัน compile เร็ว กิน resource น้อย เหมาะกับงานที่ต้องการ performance สูงๆ
สำคัญยังไงน่ะเหรอ? ลองคิดดูว่าถ้าเว็บหรือแอปที่เราใช้ทุกวันมันช้า อืดอาด หรือล่มบ่อยๆ เราจะรู้สึกยังไง? Web API ที่เขียนด้วย Go ช่วยแก้ปัญหาพวกนี้ได้ครับ ทำให้ระบบของเรา stable และ scalable มากขึ้น
HTTP Methods ก็เหมือน "กริยา" ที่เราใช้สื่อสารกับ Server ครับ ที่ใช้บ่อยๆ ก็จะมี GET (ดึงข้อมูล), POST (สร้างข้อมูล), PUT (แก้ไขข้อมูลทั้งหมด), DELETE (ลบข้อมูล) เวลาออกแบบ API เราต้องเลือกใช้ Method ให้เหมาะสมกับ operation ที่เราต้องการ
// ตัวอย่างการใช้ HTTP Methods ใน Go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
fmt.Fprintln(w, "List all users")
case "POST":
fmt.Fprintln(w, "Create a new user")
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
http.ListenAndServe(":8080", nil)
}
JSON เป็น format ที่ใช้ในการแลกเปลี่ยนข้อมูลระหว่าง Client กับ Server ครับ มันอ่านง่าย เข้าใจง่าย และ Go ก็มี library ที่ช่วยจัดการกับ JSON ได้อย่างสะดวกสบาย
// ตัวอย่างการ encode และ decode JSON ใน Go
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Encode JSON
user := User{ID: 1, Name: "John Doe", Email: "john.doe@example.com"}
jsonData, err := json.Marshal(user)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(jsonData))
// Decode JSON
var newUser User
err = json.Unmarshal(jsonData, &newUser)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v\n", newUser)
}
เริ่มต้นสร้าง Web API ด้วย Go ไม่ยากอย่างที่คิดครับ สมัยผมหัดเขียนโปรแกรมแรกๆ (ตอนที่ยังไม่มี stackoverflow ด้วยซ้ำ!) ต้องอ่านหนังสือเป็นตั้งๆ กว่าจะทำอะไรได้ เดี๋ยวนี้มี tutorial ดีๆ เยอะแยะเลยครับ
ก่อนอื่นเราต้องสร้าง project directory ก่อน จากนั้นก็ initialize Go module เพื่อจัดการ dependencies
mkdir my-go-api
cd my-go-api
go mod init my-go-api
จากนั้นก็ import package ที่จำเป็น เช่น net/http สำหรับสร้าง server และ encoding/json สำหรับจัดการ JSON
Handler คือ function ที่จะจัดการกับ request ที่เข้ามา ส่วน Router คือตัวที่ map request URL ไปยัง handler ที่เหมาะสม
// ตัวอย่างการสร้าง handler และ router
package main
import (
"fmt"
"net/http"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Welcome to the Home Page!")
}
func main() {
http.HandleFunc("/", homeHandler)
http.ListenAndServe(":8080", nil)
}
ลองรัน code นี้แล้วเปิด browser ไปที่ http://localhost:8080 จะเห็นข้อความ "Welcome to the Home Page!"
ใน handler เราสามารถเข้าถึงข้อมูลใน request ได้ เช่น headers, body, query parameters และสามารถส่ง response กลับไปให้ client ได้ในรูปแบบต่างๆ เช่น JSON, XML, HTML
// ตัวอย่างการจัดการ request และ response
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type Message struct {
Text string `json:"text"`
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "Guest"
}
message := Message{Text: "Hello, " + name + "!"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(message)
}
func main() {
http.HandleFunc("/hello", helloHandler)
http.ListenAndServe(":8080", nil)
}
ลองรัน code นี้แล้วเปิด browser ไปที่ http://localhost:8080/hello?name=SiamCafe จะเห็น JSON response ที่มีข้อความ "Hello, SiamCafe!"
Go ไม่ได้เป็นภาษาเดียวที่ใช้สร้าง Web API ได้ครับ ยังมีภาษาอื่นๆ อีกมากมาย เช่น Node.js, Python (Flask/Django), Java (Spring Boot) แต่ละภาษาก็มีข้อดีข้อเสียต่างกันไป
| ภาษา | ข้อดี | ข้อเสีย |
|---|---|---|
| Go | Performance สูง, Concurrency ดี, Compile เร็ว | Error handling ค่อนข้าง verbose, Library ยังไม่เยอะเท่าภาษาอื่น |
| Node.js | เขียนง่าย, Ecosystem ใหญ่, Non-blocking I/O | Single-threaded, Performance อาจไม่ดีเท่า Go |
| Python | เขียนง่ายมาก, Library เยอะมาก, เหมาะกับ data science | Performance ไม่สูง, Global Interpreter Lock (GIL) |
| Java | Mature ecosystem, Performance ดี (ถ้า optimize ดีๆ), Scalable | Boilerplate เยอะ, Compile ช้า |
สมัยผมทำ SiamCafe.net ก็เคยลองใช้หลายภาษาครับ สุดท้ายก็เลือกใช้ Go เพราะมันตอบโจทย์เรื่อง performance และ scalability ได้ดีที่สุด แต่ก็ขึ้นอยู่กับ use case และความถนัดของแต่ละคนด้วยนะครับ
ถ้าอยากรู้เรื่อง Go เพิ่มเติม ลองแวะไปดูที่ SiamCafe Blog นะครับ มีบทความดีๆ เกี่ยวกับ IT อีกเยอะเลย
สุดท้ายนี้ อยากฝากถึงน้องๆ ที่กำลังเริ่มต้นเขียน Web API ด้วย Go ว่า "อย่ากลัวที่จะลองผิดลองถูก" ครับ ไม่มีใครเก่งมาตั้งแต่เกิด ทุกอย่างต้องใช้เวลาและการฝึกฝน ถ้าเจอปัญหาอะไรก็อย่าท้อแท้ ลอง search หาข้อมูลใน internet หรือถามเพื่อนๆ ใน community ดู SiamCafe Blog ก็เป็นแหล่งความรู้ที่ดีนะครับ
ดูวิดีโอเพิ่มเติมเกี่ยวกับGo Golang Web Api Guide:
เฮ้ น้องๆ สาย Go! อ.บอมเองนะ สมัยผมทำร้านเน็ต SiamCafe เนี่ย บอกเลยว่าเรื่อง performance สำคัญสุดๆ เว็บ API ก็เหมือนกัน ถ้าช้า ลูกค้าหนีหมด! วันนี้เลยจะมาแชร์เคล็ดลับที่ใช้ได้จริงจากประสบการณ์ 28+ ปี
จำไว้ว่า "Code is King" แต่ "Performance is Queen"! เขียนโค้ดให้ดีอย่างเดียวไม่พอ ต้องเร็วด้วย!
ลองนึกภาพร้านเน็ตสมัยก่อน ถ้าเด็กๆ แห่กันมาเล่น Counter-Strike พร้อมกัน เครื่อง Server แทบระเบิด เพราะอะไร? เพราะ resource มันจำกัด! Web API ก็เหมือนกัน การเปิด connection ไป database ทุกครั้งที่ request เข้ามา มันเปลือง resource สุดๆ
Connection pooling ช่วยแก้ปัญหานี้ได้ ลองดูโค้ดตัวอย่างง่ายๆ:
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
if err != nil {
panic(err.Error())
}
db.SetMaxIdleConns(10) // ปรับตามความเหมาะสม
db.SetMaxOpenConns(100) // ปรับตามความเหมาะสม
defer db.Close()
SetMaxIdleConns คือจำนวน connection ที่ไม่ได้ใช้งาน แต่ยังคงเปิดไว้รอ request ใหม่ ส่วน SetMaxOpenConns คือจำนวน connection สูงสุดที่ระบบจะเปิดได้ ลองปรับค่าพวกนี้ให้เหมาะสมกับ traffic ของ API น้องๆ ดูนะ
สมัยผมทำร้านเน็ต มีโปรแกรมคิดเงินอยู่ตัวนึง ต้อง query ข้อมูลลูกค้าบ่อยมาก ถ้า query ทุกครั้ง Server ก็ตาย! ผมเลยทำ Cache ไว้ใน Memory แค่ query ครั้งแรก ครั้งต่อไปดึงจาก Cache เร็วขึ้นเป็นกอง!
Web API ก็เหมือนกัน ข้อมูลบางอย่างมันไม่ได้เปลี่ยนแปลงบ่อยๆ เช่น ข้อมูล category สินค้า, config ต่างๆ เอาไป Cache ไว้ใน Memory หรือ Redis ซะ แล้วชีวิตน้องๆ จะง่ายขึ้นเยอะ
ตัวอย่างการใช้ Redis ใน Go:
import "github.com/go-redis/redis/v8"
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
// Set value
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
// Get value
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val)
เคยไหม? ตอน deploy เว็บใหม่ แล้ว user กำลังใช้งานอยู่พอดี แล้วเว็บพัง! ลูกค้าด่ากระจาย! สมัยผมทำร้านเน็ต ถ้า server ล่ม เด็กๆ โวยวายกันทั้งร้าน!
Graceful Shutdown ช่วยให้เรา shutdown server ได้อย่างนุ่มนวล คือรอให้ request ที่กำลัง process อยู่เสร็จก่อน แล้วค่อยปิด server จริงๆ
ตัวอย่างโค้ด:
import (
"context"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
// ... โค้ดสร้าง server ...
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
quit := make(chan os.Signal, 1)
// kill (can also be signal.Notify like signal.Ignore)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
// The context is used to inform the server it has 5 seconds to finish
// the request it is currently handling
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exiting")
}
Go มันเร็ว compile ง่าย deploy ง่าย resource ไม่กินเยอะ เหมาะกับทำ Web API ที่ต้องการ performance สูงๆ ครับ
อย่า ignore error! ตรวจสอบ error ทุกครั้ง แล้ว handle ให้เหมาะสม อาจจะ log error หรือ return error ให้ client ก็ได้
เขียน Unit Test, Integration Test, End-to-End Test ให้ครอบคลุมทุก case ครับ Test เยอะๆ จะได้ไม่ต้องปวดหัวตอน Production!
ไม่จำเป็นเสมอไป ถ้า query ไม่ซับซ้อน ใช้ database/sql ก็ได้ แต่ถ้า query ซับซ้อน ORM ก็ช่วยให้ชีวิตง่ายขึ้นเยอะครับ ลองดู iCafeForex ประกอบได้นะ
การทำ Web API ด้วย Go ไม่ยาก แต่ต้องใส่ใจรายละเอียดเรื่อง performance, error handling, และ testing น้องๆ ลองเอาเทคนิคที่ผมแชร์ไปปรับใช้ดูนะครับ รับรองว่า Web API ของน้องๆ จะเร็ว แรง ทะลุนรก!
อย่าลืมติดตาม SiamCafe Blog นะครับ ผมจะมาแชร์ประสบการณ์ IT อีกเยอะเลย!