สัปดาห์ที่ 5

Hono API บน Cloudflare Workers (Routing, Middleware, JSON)

CLO3
📖 ทฤษฎี

Cloudflare Workers คืออะไร

Cloudflare Workers คือ Serverless Platform ที่รันโค้ด JavaScript ที่ Edge Node ของ Cloudflare ทั่วโลก (มากกว่า 300 เมือง) แทนที่จะรันที่ Server กลางเพียงแห่งเดียว ทำให้ผู้ใช้ได้รับการตอบสนองจาก Node ที่อยู่ใกล้ที่สุด Latency จึงต่ำกว่าการใช้ Server ดั้งเดิมมาก

สิ่งที่ทำให้ Cloudflare Workers โดดเด่น:

  • V8 Isolates — แต่ละ Worker รันใน Isolate แยกกันของ V8 Engine (เดียวกับ Chrome) ไม่ใช่ Container หรือ VM ทำให้ Cold Start เร็วมาก (ต่ำกว่า 1ms) เทียบกับ AWS Lambda ที่อาจใช้เวลาหลายร้อย ms
  • Edge Runtime — โค้ดรันที่ Edge Node ใกล้ผู้ใช้ ลด Latency จากการส่งข้อมูลข้ามทวีป
  • Web Standards API — ใช้ API มาตรฐานเดียวกับ Browser เช่น Request, Response, fetch(), Headers ทำให้โค้ดคุ้นเคยและย้ายแพลตฟอร์มได้ง่าย
  • Free Tier ใจดี — 100,000 requests/วัน ฟรี เหมาะสำหรับโปรเจกต์ขนาดเล็กและนักศึกษา
สถาปัตยกรรม Cloudflare Workers:

  ผู้ใช้ในไทย          ผู้ใช้ในญี่ปุ่น       ผู้ใช้ในยุโรป
       │                     │                    │
       ▼                     ▼                    ▼
  CF Edge: Bangkok    CF Edge: Tokyo      CF Edge: Frankfurt
       │                     │                    │
       └─────────────────────┴────────────────────┘
                             │
                    Cloudflare Network
                    (Worker รันที่ Edge ใกล้ผู้ใช้)

  เปรียบเทียบ: Traditional Server
  ผู้ใช้ทุกคน ──────────────────────► Server กลาง (เช่น สิงคโปร์)
  (ทุกคนรอเท่ากัน ไม่ว่าอยู่ที่ไหน)
          

Hono Framework — ทำไมเลือก Hono

Hono (แปลว่า "เปลวไฟ" ในภาษาญี่ปุ่น) คือ Web Framework ที่ออกแบบมาสำหรับ Edge Runtime โดยเฉพาะ เป็น Framework ที่เล็ก เร็ว และใช้ Web Standards API

เหตุผลที่เลือก Hono สำหรับคอร์สนี้:

  • เบาและเร็ว — Bundle Size ต่ำกว่า 15KB ไม่มี Dependency ภายนอก ใช้งานได้บน Cloudflare Workers, Deno, Bun, และ Node.js
  • Web Standards API — ใช้ Request / Response มาตรฐาน ไม่ใช่ Object แบบ Express.js ทำให้โค้ดพกพาได้ระหว่างแพลตฟอร์ม
  • API ที่เข้าใจง่าย — Syntax คล้าย Express.js ที่หลายคนคุ้นเคย แต่รองรับ Async/Await โดยตรงทุก Handler
  • Built-in Middleware — มี Middleware สำเร็จรูปพร้อมใช้ เช่น CORS, Logger, bearerAuth, compress, cache
เคล็ดลับ — Hono vs Express

Express.js ใช้ req, res แบบ Node.js เดิม ส่วน Hono ใช้ c (Context) ที่ครอบ Request และ Response มาตรฐาน เช่น c.req.param('id') และ c.json(data) ถ้าเคยใช้ Express มาก่อน จะปรับตัวได้เร็วมาก

Routing ใน Hono

Hono รองรับ HTTP Methods ครบถ้วนสำหรับการสร้าง REST API:

  • app.get(path, handler) — ดึงข้อมูล (Read) เช่น ดึงรายการบริการ
  • app.post(path, handler) — สร้างข้อมูลใหม่ (Create) เช่น สร้างการจอง
  • app.put(path, handler) — อัปเดตข้อมูลทั้งหมด (Update) เช่น แก้ไขข้อมูลบริการ
  • app.delete(path, handler) — ลบข้อมูล (Delete) เช่น ยกเลิกการจอง
  • app.patch(path, handler) — อัปเดตข้อมูลบางส่วน (Partial Update)

Dynamic Route Parameters: ใช้ :param ใน Path เพื่อรับค่าจาก URL

  • app.get('/api/services/:id', (c) => { const id = c.req.param('id'); ... })
  • app.get('/api/users/:userId/bookings/:bookingId', (c) => { ... }) — หลาย Params ได้

JSON Response: Hono มี c.json(data, statusCode) สำหรับส่ง JSON Response โดย Status Code default คือ 200 ถ้าไม่ระบุ

REST API Convention สำหรับ BookEasy:

  Method    Path                    ทำหน้าที่
  ─────────────────────────────────────────────────────
  GET       /api/services           ดึงรายการบริการทั้งหมด
  GET       /api/services/:id       ดึงบริการเดียวตาม ID
  POST      /api/services           สร้างบริการใหม่
  PUT       /api/services/:id       แก้ไขบริการทั้งหมด
  DELETE    /api/services/:id       ลบบริการ

  GET       /api/bookings           ดึงรายการจองทั้งหมด
  POST      /api/bookings           สร้างการจองใหม่
  GET       /api/bookings/:id       ดึงรายละเอียดการจอง
          

Middleware ใน Hono

Middleware คือฟังก์ชันที่ทำงานก่อน (หรือหลัง) Route Handler ใช้สำหรับงานที่ต้องทำซ้ำทุก Request เช่น Logging, Authentication, CORS

Middleware สำคัญที่ใช้ในคอร์สนี้:

  • logger() — บันทึก Log ทุก Request ที่เข้ามา แสดง Method, Path, Status, Response Time มีประโยชน์มากระหว่าง Development
  • cors() — ตั้งค่า CORS (Cross-Origin Resource Sharing) เพื่อให้ Frontend ที่รันบน Domain อื่น (เช่น localhost:5173) สามารถเรียก API ได้ Browser จะ Block Request ข้ามต้นทางโดย Default ถ้าไม่มี CORS Header
  • bearerAuth() — ตรวจสอบ Token ใน Header Authorization: Bearer <token> ใช้ป้องกัน Route ที่ต้องการ Authentication
ข้อควรระวัง — CORS

ถ้าไม่เพิ่ม CORS Middleware เบราว์เซอร์จะ Block การเรียก API จาก Frontend ด้วย Error "Access to fetch at 'http://localhost:8787' from origin 'http://localhost:5173' has been blocked by CORS policy" ต้องตั้งค่า cors({ origin: 'http://localhost:5173' }) เสมอเมื่อ Frontend กับ Backend รันบน Port ต่างกัน

Wrangler CLI

Wrangler คือ CLI สำหรับจัดการ Cloudflare Workers พัฒนาโดย Cloudflare เอง ใช้สำหรับ Development, Testing, และ Deploy

  • npx wrangler dev — รัน Worker ใน Local Development Mode เข้าถึงได้ที่ http://localhost:8787 Wrangler จะ Watch ไฟล์และ Reload อัตโนมัติเมื่อโค้ดเปลี่ยน
  • npx wrangler deploy — Deploy Worker ขึ้น Cloudflare Edge Network จริง ต้องมีบัญชี Cloudflare และ Login ก่อนด้วย npx wrangler login
  • npx wrangler tail — ดู Real-time Logs ของ Worker ที่รันบน Production
หมายเหตุ — wrangler.toml

ไฟล์ wrangler.toml ใน Root ของโปรเจกต์เป็นไฟล์ Config สำหรับ Wrangler กำหนด name (ชื่อ Worker), main (Entry File), และ compatibility_date สร้างอัตโนมัติเมื่อใช้ npm create hono@latest

💻 โค้ดตัวอย่าง
terminal Bash
# snippet 1: สร้าง Hono Worker
npm create hono@latest bookeasy-api -- --template cloudflare-workers
cd bookeasy-api
npm install
npx wrangler dev
# เปิด http://localhost:8787
src/index.js JavaScript
// snippet 2: Hono routes พื้นฐาน — src/index.js
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';

const app = new Hono();

// Middleware
app.use('*', logger());
app.use('/api/*', cors({ origin: 'http://localhost:5173' }));

// Mock data
const SERVICES = [
  { id: 1, name: 'ตัดผมชาย', description: 'ตัดผมสไตล์เกาหลี', price: 150, duration_min: 45 },
  { id: 2, name: 'นวดแผนไทย', description: 'นวดผ่อนคลาย', price: 400, duration_min: 60 },
];

// Routes
app.get('/api/services', (c) => {
  return c.json({ success: true, data: SERVICES });
});

app.get('/api/services/:id', (c) => {
  const id = Number(c.req.param('id'));
  const service = SERVICES.find(s => s.id === id);
  if (!service) return c.json({ error: 'ไม่พบบริการ' }, 404);
  return c.json({ success: true, data: service });
});

app.post('/api/bookings', async (c) => {
  const body = await c.req.json();
  const booking = { id: Date.now(), ...body, status: 'pending' };
  return c.json({ success: true, data: booking }, 201);
});

export default app;
terminal Bash
# snippet 3: ทดสอบ API ด้วย curl
# GET ทุก services
curl http://localhost:8787/api/services

# GET service เดี่ยว
curl http://localhost:8787/api/services/1

# POST จอง
curl -X POST http://localhost:8787/api/bookings \
  -H "Content-Type: application/json" \
  -d '{"service_id":1,"booking_date":"2026-07-10","booking_time":"09:00"}'

🧪 ปฏิบัติการ Lab 4 — BookEasy: สร้าง Hono API

ฝึกสร้าง Backend API ด้วย Hono บน Cloudflare Workers พร้อม Routes ครบและทดสอบด้วย curl

ต่อยอดจาก Lab 3

ต่อยอดจาก Lab 3 — สร้าง backend API ที่ frontend จะเรียกใช้ใน Week 7

1
ตั้งค่า bookeasy-api project

สร้าง Hono project ใหม่ใน folder แยกต่างหากจาก Frontend และตรวจสอบว่า Dev Server ทำงานได้

  • รันคำสั่ง npm create hono@latest bookeasy-api -- --template cloudflare-workers ใน folder เดียวกับ bookeasy/
  • เข้าไปใน folder ที่สร้างใหม่: cd bookeasy-api แล้วรัน npm install
  • รัน npx wrangler dev และเปิดเบราว์เซอร์ที่ http://localhost:8787
  • ตรวจสอบว่า Hono ตอบกลับด้วย Response (ปกติจะเห็น "Hello Hono!" หรือ JSON ว่าง ๆ)
  • เปิดไฟล์ src/index.js เพื่อดูโครงสร้างเริ่มต้น
เกณฑ์การผ่าน

รัน npx wrangler dev ได้โดยไม่มี Error ใน Terminal เปิด http://localhost:8787 แล้วเบราว์เซอร์แสดง Response บางอย่าง (ไม่ใช่ "Connection Refused") ถ่ายภาพหน้าจอ Terminal ที่แสดง "Listening on http://localhost:8787"

คำแนะนำ

ถ้า npm create hono@latest ถามคำถาม ให้เลือก cloudflare-workers เป็น Template และเลือก No สำหรับ TypeScript เพื่อใช้ JavaScript ธรรมดา Wrangler จะสร้างไฟล์ wrangler.toml ให้อัตโนมัติ ไม่ต้องแก้ไข

2
สร้าง routes ครบ

เขียน Routes ครบ 3 Endpoint พร้อม Mock Data และ CORS Middleware

  • แก้ไข src/index.js ให้มี Mock Data SERVICES เป็น Array พร้อมข้อมูลบริการอย่างน้อย 2 รายการ (ดูตัวอย่างจาก Code Snippet ด้านบน)
  • เพิ่ม Middleware: app.use('*', logger()) และ app.use('/api/*', cors({ origin: 'http://localhost:5173' }))
  • สร้าง GET /api/services — คืน JSON { success: true, data: SERVICES }
  • สร้าง GET /api/services/:id — ค้นหาด้วย SERVICES.find() ถ้าไม่พบให้คืน c.json({ error: 'ไม่พบบริการ' }, 404)
  • สร้าง POST /api/bookings — รับ Body ด้วย await c.req.json() แล้วคืน Booking Object พร้อม id: Date.now() และ status: 'pending' ด้วย Status Code 201
เกณฑ์การผ่าน

รัน npx wrangler dev แล้วเรียก GET /api/services ได้ JSON ที่มี Array ของบริการ เรียก GET /api/services/999 ได้ Response 404 พร้อม { "error": "ไม่พบบริการ" } Terminal แสดง Log ทุกครั้งที่มี Request เข้ามา (จาก logger middleware)

คำแนะนำ

ต้อง Import Middleware ก่อนใช้งาน: import { cors } from 'hono/cors' และ import { logger } from 'hono/logger' c.req.param('id') คืนค่าเป็น String เสมอ ต้องแปลงเป็น Number ด้วย Number(c.req.param('id')) ก่อน Compare กับ id ใน Array ที่เป็น Number

3
ทดสอบด้วย curl

รัน Dev Server แล้วทดสอบทุก Route ด้วย curl และบันทึก Response JSON

  • เปิด Terminal ใหม่ (ขณะที่ npx wrangler dev ยังรันอยู่ใน Terminal เดิม)
  • ทดสอบ GET /api/services: curl http://localhost:8787/api/services
  • ทดสอบ GET /api/services/1: curl http://localhost:8787/api/services/1
  • ทดสอบ 404: curl http://localhost:8787/api/services/999 ตรวจว่าได้ Status 404 และ { "error": "ไม่พบบริการ" }
  • ทดสอบ POST /api/bookings ด้วย curl พร้อม JSON Body ตามตัวอย่างใน Snippet 3
  • บันทึก JSON Response ของทุก Endpoint แล้วส่งเป็นหลักฐาน
เกณฑ์การผ่าน

ทุก Endpoint ตอบสนองถูกต้อง: GET /api/services คืน Array, GET /api/services/1 คืน Object ของบริการ ID 1, POST /api/bookings คืน Booking Object ที่มี id และ status: "pending" ส่งภาพหน้าจอ Terminal ที่แสดง curl output ของทั้ง 3 Endpoint

คำแนะนำ

เพิ่ม Flag -i ใน curl เพื่อดู HTTP Status Code พร้อม Response Headers ด้วย เช่น curl -i http://localhost:8787/api/services/999 จะเห็น HTTP/1.1 404 Not Found อยู่ด้านบน บน Windows ให้ใช้ Git Bash หรือ PowerShell แทน Command Prompt