Gleam เป็นภาษาโปรแกรมแบบ Functional ที่ทำงานบน BEAM (Erlang VM) มีระบบ Type ที่เข้มงวด ช่วยให้โค้ดปลอดภัยและบำรุงรักษาง่าย Wisp คือ Web Framework สำหรับ Gleam ที่ออกแบบมาให้พัฒนาได้รวดเร็วและดูแลรักษาง่าย โดยมีแนวคิดหลักคือ Handlers และ Middleware
บทความนี้จะพาคุณสร้าง RESTful API ตั้งแต่เริ่มต้นจนใช้งานได้จริง โดยใช้ Wisp เวอร์ชัน 2.1.1
เริ่มต้นด้วยการสร้างโปรเจกต์ Gleam ใหม่:
gleam new my_api
cd my_api
เพิ่ม packages ที่จำเป็นสำหรับการสร้าง Web API:
gleam add wisp@2.1.1
gleam add mist
gleam add gleam_http
gleam add gleam_json
gleam add gleam_erlang
รายละเอียดของแต่ละ package:
| Package | หน้าที่ |
|---|---|
wisp |
Web Framework หลัก |
mist |
HTTP Server สำหรับ Gleam |
gleam_http |
Types และ functions สำหรับ HTTP |
gleam_json |
Encode/Decode JSON |
gleam_erlang |
ฟังก์ชันสำหรับ Erlang runtime |
my_api/
├── src/
│ ├── my_api.gleam # Entry point
│ ├── my_api/
│ │ ├── router.gleam # จัดการ routing
│ │ ├── web.gleam # Middleware
│ │ └── handlers/
│ │ └── user.gleam # User handlers
├── test/
└── gleam.toml
แก้ไขไฟล์ src/my_api.gleam:
import gleam/erlang/process
import mist
import wisp
import wisp/wisp_mist
import my_api/router
pub fn main() {
// ตั้งค่า logger
wisp.configure_logger()
// Secret key สำหรับ cookies และข้อมูลที่ต้องเซ็น
let secret_key_base = wisp.random_string(64)
// สร้างและเริ่มต้น server
let assert Ok(_) =
wisp_mist.handler(router.handle_request, secret_key_base)
|> mist.new
|> mist.port(8080)
|> mist.start_http
// รักษา process ให้ทำงานต่อเนื่อง
process.sleep_forever()
}
สร้างไฟล์ src/my_api/web.gleam:
import wisp.{type Request, type Response}
/// Middleware หลักที่ใช้กับทุก request
pub fn middleware(
req: Request,
handle_request: fn(Request) -> Response,
) -> Response {
// เปิดใช้ method override (สำหรับ forms ที่ต้องการใช้ PUT/DELETE)
let req = wisp.method_override(req)
// Log ทุก request
use <- wisp.log_request(req)
// จัดการ crashes อัตโนมัติ
use <- wisp.rescue_crashes
// จัดการ HEAD requests
use req <- wisp.handle_head(req)
// ป้องกัน CSRF
use req <- wisp.csrf_known_header_protection(req)
handle_request(req)
}
สร้างไฟล์ src/my_api/router.gleam:
import gleam/http.{Delete, Get, Post, Put}
import wisp.{type Request, type Response}
import my_api/web
import my_api/handlers/user
/// Handler หลักที่รับทุก request
pub fn handle_request(req: Request) -> Response {
use req <- web.middleware(req)
// ใช้ pattern matching สำหรับ routing
// Wisp ไม่มี router abstraction พิเศษ แต่ใช้ pattern matching แทน
case wisp.path_segments(req) {
// GET /
[] -> home(req)
// /api/users
["api", "users"] -> user.handle_users(req)
// /api/users/:id
["api", "users", id] -> user.handle_user(req, id)
// /health
["health"] -> health_check(req)
// 404 สำหรับ routes อื่นๆ
_ -> wisp.not_found()
}
}
fn home(req: Request) -> Response {
use <- wisp.require_method(req, Get)
wisp.ok()
|> wisp.string_body("Welcome to My API - Built with Wisp 2.1.1")
}
fn health_check(req: Request) -> Response {
use <- wisp.require_method(req, Get)
wisp.json_response(
"{\"status\": \"healthy\", \"version\": \"1.0.0\"}",
200,
)
}
สร้างไฟล์ src/my_api/handlers/user.gleam:
import gleam/dynamic
import gleam/http.{Delete, Get, Post, Put}
import gleam/int
import gleam/json
import gleam/list
import gleam/option.{None, Some}
import gleam/result
import wisp.{type Request, type Response}
/// User type
pub type User {
User(id: Int, name: String, email: String)
}
/// จำลองข้อมูล users (ในการใช้งานจริงควรใช้ database)
fn get_mock_users() -> List(User) {
[
User(1, "สมชาย ใจดี", "somchai@example.com"),
User(2, "สมหญิง รักเรียน", "somying@example.com"),
User(3, "John Doe", "john@example.com"),
]
}
/// Handle requests ไปที่ /api/users
pub fn handle_users(req: Request) -> Response {
case req.method {
Get -> list_users(req)
Post -> create_user(req)
_ -> wisp.method_not_allowed([Get, Post])
}
}
/// Handle requests ไปที่ /api/users/:id
pub fn handle_user(req: Request, id: String) -> Response {
case req.method {
Get -> get_user(req, id)
Put -> update_user(req, id)
Delete -> delete_user(req, id)
_ -> wisp.method_not_allowed([Get, Put, Delete])
}
}
/// GET /api/users - รายการ users ทั้งหมด
fn list_users(_req: Request) -> Response {
let users = get_mock_users()
let json_body = users_to_json(users)
wisp.json_response(json_body, 200)
}
/// GET /api/users/:id - ดึงข้อมูล user ตาม id
fn get_user(_req: Request, id: String) -> Response {
case int.parse(id) {
Ok(user_id) -> {
let users = get_mock_users()
case list.find(users, fn(u) { u.id == user_id }) {
Ok(user) -> wisp.json_response(user_to_json(user), 200)
Error(_) -> {
wisp.json_response(
error_json("User not found"),
404,
)
}
}
}
Error(_) -> {
wisp.json_response(
error_json("Invalid user ID"),
400,
)
}
}
}
/// POST /api/users - สร้าง user ใหม่
fn create_user(req: Request) -> Response {
use json_body <- wisp.require_json(req)
// Decode JSON body
let decoder =
dynamic.decode2(
fn(name, email) { User(4, name, email) },
dynamic.field("name", dynamic.string),
dynamic.field("email", dynamic.string),
)
case decoder(json_body) {
Ok(user) -> {
wisp.json_response(user_to_json(user), 201)
}
Error(_) -> {
wisp.json_response(
error_json("Invalid request body"),
400,
)
}
}
}
/// PUT /api/users/:id - อัพเดท user
fn update_user(req: Request, id: String) -> Response {
use json_body <- wisp.require_json(req)
case int.parse(id) {
Ok(user_id) -> {
let decoder =
dynamic.decode2(
fn(name, email) { User(user_id, name, email) },
dynamic.field("name", dynamic.string),
dynamic.field("email", dynamic.string),
)
case decoder(json_body) {
Ok(user) -> {
wisp.json_response(user_to_json(user), 200)
}
Error(_) -> {
wisp.json_response(
error_json("Invalid request body"),
400,
)
}
}
}
Error(_) -> {
wisp.json_response(
error_json("Invalid user ID"),
400,
)
}
}
}
/// DELETE /api/users/:id - ลบ user
fn delete_user(_req: Request, id: String) -> Response {
case int.parse(id) {
Ok(_user_id) -> {
wisp.json_response(
"{\"message\": \"User deleted successfully\"}",
200,
)
}
Error(_) -> {
wisp.json_response(
error_json("Invalid user ID"),
400,
)
}
}
}
/// แปลง User เป็น JSON string
fn user_to_json(user: User) -> String {
json.object([
#("id", json.int(user.id)),
#("name", json.string(user.name)),
#("email", json.string(user.email)),
])
|> json.to_string
}
/// แปลง List ของ Users เป็น JSON string
fn users_to_json(users: List(User)) -> String {
json.array(users, fn(user) {
json.object([
#("id", json.int(user.id)),
#("name", json.string(user.name)),
#("email", json.string(user.email)),
])
})
|> json.to_string
}
/// สร้าง error JSON response
fn error_json(message: String) -> String {
json.object([
#("error", json.string(message)),
])
|> json.to_string
}
gleam run
คุณจะเห็นข้อความ:
Listening on http://127.0.0.1:8080
# ทดสอบ Home
curl http://localhost:8080
# ทดสอบ Health Check
curl http://localhost:8080/health
# ดึงรายการ Users ทั้งหมด
curl http://localhost:8080/api/users
# ดึง User ตาม ID
curl http://localhost:8080/api/users/1
# สร้าง User ใหม่
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name": "New User", "email": "new@example.com"}'
# อัพเดท User
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"name": "Updated Name", "email": "updated@example.com"}'
# ลบ User
curl -X DELETE http://localhost:8080/api/users/1
ใช้ Pattern Matching ของ Gleam ในการจัดการ routes ทำให้ปลอดภัยและรวดเร็วกว่า router แบบดั้งเดิม
Middleware สามารถ compose กันได้อย่างยืดหยุ่นโดยใช้ use syntax ของ Gleam
รองรับการ parse และสร้าง JSON ได้ง่ายผ่าน gleam_json package
use <- wisp.serve_static(req, under: "/static", from: "/public")
import envoy
pub fn main() {
let secret =
envoy.get("SECRET_KEY_BASE")
|> result.unwrap("default_secret_for_dev")
// ...
}
สร้าง Context type เพื่อส่งข้อมูลที่ใช้ร่วมกัน เช่น database connection:
pub type Context {
Context(
db: Database,
secret: String,
)
}
pub fn handle_request(req: Request, ctx: Context) -> Response {
// ใช้ ctx.db ในการ query database
wisp.ok()
}
import gleam/result.{try}
pub fn create_item(req: Request, ctx: Context) -> Response {
use json <- wisp.require_json(req)
let result = {
use params <- try(parse_params(json))
use item <- try(save_to_db(params, ctx.db))
Ok(item_to_json(item))
}
case result {
Ok(body) -> wisp.json_response(body, 201)
Error(_) -> wisp.bad_request()
}
}
Wisp 2.1.1 เป็น Web Framework ที่เรียบง่ายแต่ทรงพลัง เหมาะสำหรับการสร้าง Web API ด้วยภาษา Gleam โดยมีข้อดีหลักคือ:
บทความนี้ใช้ Wisp เวอร์ชัน 2.1.1 (Released: December 11, 2025)