FastAPI: สร้าง API โคตรเร็ว โค้ดก็สวยด้วย
เฮ้ย ใครที่ยังใช้ Flask, Django REST framework แบบเดิมๆ อยู่บ้าง? ไม่ได้ว่านะ แต่ถ้าจะเริ่มโปรเจกต์ API ใหม่ๆ อ่ะ ลองดู FastAPI ดิ ว้าวมากบอกเลย โคตรชอบ
ทำไมต้อง FastAPI วะ?
เหตุผลง่ายๆ เลยนะ มันเร็ว! โคตรเร็วอะ เพราะมันสร้างมาบน Starlette (สำหรับเว็บพาร์ท) กับ Pydantic (สำหรับ Data Validation/Serialization) ซึ่งมันใช้ Async/Await ได้เต็มที่ เหมาะกับงาน I/O-bound สุดๆ ไม่ต้องรอคิวแบบที่พวก Sync Framework ต้องเจอ
แล้วที่เจ๋งกว่านั้นคือ มันใช้ Python Type Hint มาช่วย validate request/response data ให้เราเลย ไม่ต้องมานั่งเขียน if/else เช็ค request.json['field'] ให้เมื่อยมือ โค้ดจะอ่านง่ายขึ้นเยอะ แถมยังมี Auto-generated Docs ทั้ง Swagger UI กับ ReDoc ให้เราด้วย สะดวกชิบเป๋ง
มาลองดูกัน ง่ายนิดเดียว
ก่อนอื่นก็ pip install มันซะก่อน
pip install fastapi uvicorn[standard]
จากนั้นก็เขียนโค้ดง่ายๆ แบบนี้ใน main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Hello, FastAPI!"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
# item_id จะถูกแปลงเป็น int ให้อัตโนมัติ ถ้าใส่มาผิดก็ error เลย
# q ก็เป็น optional string
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
# ลองรันด้วย:
# uvicorn main:app --reload
เห็นปะ โค้ดมันอ่านง่ายชิบเป๋ง แค่ใส่ Type Hint เข้าไป FastAPI ก็เข้าใจละว่าเราต้องการอะไร เจ๋ง!
Dependency Injection: ของดีที่ต้องใช้!
ทีเด็ดเลยคือ DI (Dependency Injection) เนี่ยแหละ ทำให้โค้ดเรา modular ขึ้นเยอะ เทสก็ง่าย ไม่ต้องยัดทุกอย่างลงไปในฟังก์ชันเดียว
สมมติเรามีฟังก์ชันที่ต้องต่อ DB หรือดึงค่า config อะไรบางอย่าง:
from fastapi import FastAPI, Depends, HTTPException, status
from typing import Annotated
app = FastAPI()
# สมมติว่านี่คือฟังก์ชันที่ไปดึง DB session มา
# ในโลกจริงอาจจะเป็นการต่อ database หรือดึงข้อมูล user จาก Header
async def get_current_user_id():
# สมมติว่าดึงมาจาก database หรือ JWT token
# อาจจะมีการเช็ค auth ตรงนี้ด้วย
print("กำลังดึง User ID...")
# user_id_from_db = await some_db_call()
# ถ้ามีปัญหาอาจจะ raise HTTPException
# raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authorized")
return 123 # Hardcode ไปก่อนนะ
# เราสามารถใช้ Depends ใน parameter ของ endpoint ได้เลย
@app.get("/users/me")
async def read_current_user(user_id: Annotated[int, Depends(get_current_user_id)]):
return {"current_user_id": user_id}
# ลองรันด้วย:
# uvicorn main:app --reload
# แล้วเข้า http://localhost:8000/users/me
โคตรคลีนเลยใช่ไหม? ฟังก์ชัน get_current_user_id จะถูกเรียกก่อนที่ read_current_user จะรัน ถ้ามัน return ค่ามาอะไร ก็จะถูกส่งเป็น user_id เข้าไปในฟังก์ชัน read_current_user ทันที
ระวังเรื่อง Blocking I/O ใน Async Code
อันนี้โคตรสำคัญเลยนะ เคยเจอไหม รันโปรเจกต์ FastAPI แล้วมันชอบค้างๆ หรือรู้สึกว่ามันช้าๆ ทั้งๆ ที่เขียน async def เต็มไปหมด? บางทีมันก็ไม่ได้ช่วยอะไรนะ ถ้าข้างในคุณยังไปเรียกไลบรารีที่มันเป็น blocking I/O (เช่น requests แบบปกติ, หรือ Database Driver บางตัวที่ไม่ได้รองรับ async โดยตรง) แบบนี้อะ:
import time
import requests # อันนี้เป็น sync library
# ...ใน main.py ต่อจากโค้ดข้างบน
@app.get("/blocking_example")
async def blocking_example():
print("เริ่ม blocking call")
# เนี่ยแหละตัวดี! มันจะบล็อก event loop ทั้งหมดเลยนะ
time.sleep(5) # สมมติว่านี่คือการเรียก API ที่ใช้เวลานานมากๆ
# response = requests.get("http://some-slow-api.com") # ถ้าใช้ requests ตรงๆ ใน async function ก็จะ block เหมือนกันถ้าไม่ใช้ run_in_threadpool
print("จบ blocking call")
return {"message": "ทำเสร็จแล้วนะ แต่รอนานหน่อย"}
# ลองรันแล้วลองเปิดแท็บ /blocking_example สองแท็บพร้อมกันดูดิ
# แท็บที่สองจะรอจนแท็บแรกเสร็จก่อนถึงจะเริ่ม process
โค้ดมันก็รันไปนะ แต่เส้นทางอื่นแม่งก็ต้องรอด้วย ถ้าเจอไอ้ blocking function นี่แหละ น่าหงุดหงิดชะมัด! ทางแก้คือถ้าต้องทำอะไรที่ block นานๆ ให้ย้ายไปรันใน ThreadPoolExecutor ของ Starlette (ซึ่ง FastAPI ก็ใช้) ด้วย run_in_threadpool หรือใช้ไลบรารีที่เป็น async โดยตรงอย่าง httpx แทน requests หรือ asyncpg แทน psycopg2 อะไรแบบนั้น
สรุปนะ
โดยรวมนะ ถ้าจะทำ API ใหม่ๆ แล้วอยากได้ที่มันเร็ว เขียนง่าย (ด้วย Type Hint) เทสสะดวก (ด้วย DI) แถมมีเอกสารให้ฟรีๆ ด้วย FastAPI คือคำตอบอะ เชื่อดิ ลองแล้วจะติดใจ! มันทำให้การเขียน API สนุกขึ้นเยอะเลยนะ ไม่ใช่เรื่องน่าปวดหัวอีกต่อไป
ไปลองเล่นดู! แล้วคุณจะรู้สึกว่ามัน 'ว้าว!' จริงๆ