
สวัสดีครับ! ในยุคดิจิทัลที่ทุกอย่างเชื่อมโยงถึงกัน การสร้าง API (Application Programming Interface) เพื่อให้แอปพลิเคชันต่าง ๆ สื่อสารกันได้ กลายเป็นหัวใจสำคัญของการพัฒนาซอฟต์แวร์สมัยใหม่เลยก็ว่าได้ครับ และเมื่อพูดถึงการสร้าง REST API ด้วย Python หนึ่งในเฟรมเวิร์กที่มาแรงและน่าจับตามองที่สุดในตอนนี้ คงหนีไม่พ้น FastAPI ครับ ด้วยประสิทธิภาพที่เหนือกว่า, ความง่ายในการใช้งาน, และคุณสมบัติที่อัดแน่น ทำให้ FastAPI กลายเป็นตัวเลือกอันดับต้น ๆ สำหรับนักพัฒนาทั่วโลกที่ต้องการสร้าง API ที่รวดเร็วและแข็งแกร่ง วันนี้ SiamLancard.com จะพาทุกท่านไปเจาะลึกถึงวิธีการสร้าง REST API ด้วย FastAPI ตั้งแต่เริ่มต้นจนคุณสามารถนำไปประยุกต์ใช้ได้จริงเลยทีเดียวครับ พร้อมแล้ว ไปลุยกันเลย!
สารบัญ
- ทำความเข้าใจ REST API เบื้องต้น
- ทำความรู้จักกับ FastAPI
- การเตรียมความพร้อมก่อนเริ่มต้น
- แนวคิดหลักของ FastAPI ที่ควรรู้
- ลงมือสร้าง REST API (CRUD) ด้วย FastAPI: ตัวอย่างระบบจัดการงาน (Task Manager)
- การตั้งค่าโปรเจกต์เบื้องต้น
- FastAPI “Hello World” แรกของคุณ
- การสร้าง Data Model ด้วย Pydantic
- การจำลองฐานข้อมูล In-memory
- สร้าง API Endpoint: POST /tasks (เพิ่มงานใหม่)
- สร้าง API Endpoint: GET /tasks (ดึงงานทั้งหมด)
- สร้าง API Endpoint: GET /tasks/{task_id} (ดึงงานตาม ID)
- สร้าง API Endpoint: PUT /tasks/{task_id} (อัปเดตงาน)
- สร้าง API Endpoint: DELETE /tasks/{task_id} (ลบงาน)
- วิธีรันและทดสอบ API ของคุณ
- แนวคิดขั้นสูงใน FastAPI (ภาพรวม)
- ข้อควรพิจารณาในการ Deploy FastAPI Application
- คำถามที่พบบ่อย (FAQ)
- สรุปและก้าวต่อไป
ทำความเข้าใจ REST API เบื้องต้น
ก่อนที่เราจะดำดิ่งสู่โลกของ FastAPI เรามาทำความเข้าใจพื้นฐานของ REST API กันก่อน เพื่อให้เรามีรากฐานที่มั่นคงในการสร้าง API ที่ดีและมีประสิทธิภาพครับ
REST API คืออะไร?
REST ย่อมาจาก Representational State Transfer ครับ ไม่ใช่ภาษาโปรแกรมมิ่งหรือเฟรมเวิร์ก แต่เป็นชุดของหลักการออกแบบสถาปัตยกรรมสำหรับระบบเครือข่าย โดยเฉพาะอย่างยิ่งสำหรับเว็บเซอร์วิส ที่ช่วยให้แอปพลิเคชันต่าง ๆ สามารถสื่อสารและแลกเปลี่ยนข้อมูลกันได้อย่างง่ายดายและมีประสิทธิภาพครับ API ที่ถูกสร้างขึ้นตามหลักการ REST จะเรียกว่า RESTful API ครับ
หลักการสำคัญของ REST มีดังนี้ครับ:
- Client-Server: แยกส่วนของ Client (ผู้ใช้งาน) และ Server (ผู้ให้บริการ) ออกจากกันอย่างชัดเจน ทำให้แต่ละส่วนสามารถพัฒนาได้อย่างอิสระครับ
- Stateless: Server จะไม่เก็บสถานะของ Client ระหว่าง Request แต่ละครั้ง ทุก Request จะต้องมีข้อมูลที่จำเป็นทั้งหมดเพื่อให้ Server ประมวลผลได้ครับ
- Cacheable: Resource สามารถถูก Cache ได้ ช่วยเพิ่มประสิทธิภาพและลดภาระของ Server ครับ
- Layered System: Client ไม่จำเป็นต้องรู้ว่ากำลังเชื่อมต่อโดยตรงกับ Server หรือผ่าน Layer อื่น ๆ เช่น Load Balancer หรือ Proxy Server ครับ
- Uniform Interface: เป็นหลักการที่สำคัญที่สุดของ REST กำหนดให้มีวิธีการสื่อสารที่เป็นมาตรฐานและสม่ำเสมอ ซึ่งรวมถึงการใช้ URI ในการระบุ Resource และ HTTP Methods ในการดำเนินการครับ
HTTP Methods ที่สำคัญ
HTTP Methods หรือ Verb เป็นตัวกำหนดว่า Client ต้องการทำอะไรกับ Resource ที่ระบุครับ นี่คือ Methods หลัก ๆ ที่เราจะใช้บ่อยในการสร้าง REST API ครับ:
- GET: ใช้สำหรับขอข้อมูลจาก Server โดยไม่เปลี่ยนแปลงสถานะของ Resource นั้น ๆ ครับ (Read)
- POST: ใช้สำหรับส่งข้อมูลใหม่ไปยัง Server เพื่อสร้าง Resource ใหม่ครับ (Create)
- PUT: ใช้สำหรับอัปเดตข้อมูลของ Resource ที่มีอยู่แล้วบน Server โดยมักจะใช้กับการอัปเดตข้อมูลทั้งหมดของ Resource นั้นครับ (Update)
- DELETE: ใช้สำหรับลบ Resource ที่ระบุออกจาก Server ครับ (Delete)
- PATCH: ใช้สำหรับอัปเดตข้อมูลบางส่วนของ Resource ที่มีอยู่แล้วบน Server ครับ (Partial Update)
HTTP Status Codes ที่ควรรู้
เมื่อ Client ส่ง Request ไปยัง Server Server จะตอบกลับมาพร้อมกับ HTTP Status Code ซึ่งเป็นรหัสตัวเลข 3 หลัก เพื่อบอกสถานะของ Request นั้น ๆ ครับ การเข้าใจ Status Code เหล่านี้จะช่วยให้เราดีบั๊กและทำความเข้าใจการทำงานของ API ได้ดีขึ้นครับ
- 2xx – Success: Request ประสบความสำเร็จ
200 OK: Request สำเร็จและ Server ได้ส่งข้อมูลกลับมาตามที่ร้องขอครับ201 Created: Request สำเร็จและมีการสร้าง Resource ใหม่บน Server ครับ มักใช้กับ POST Request ครับ204 No Content: Request สำเร็จ แต่ Server ไม่มีข้อมูลจะส่งกลับมาให้ครับ มักใช้กับ DELETE หรือ PUT Request ที่ไม่ต้องการส่งข้อมูลกลับครับ
- 4xx – Client Error: มีข้อผิดพลาดจากฝั่ง Client
400 Bad Request: Server ไม่สามารถประมวลผล Request ได้เนื่องจากรูปแบบของ Request ไม่ถูกต้องครับ401 Unauthorized: Client ไม่ได้รับอนุญาตให้เข้าถึง Resource นั้น ๆ ครับ (ต้องยืนยันตัวตน)403 Forbidden: Client ได้รับการยืนยันตัวตนแล้ว แต่ไม่มีสิทธิ์เข้าถึง Resource นั้น ๆ ครับ404 Not Found: ไม่พบ Resource ที่ร้องขอครับ405 Method Not Allowed: Method ที่ใช้ไม่ได้รับอนุญาตสำหรับ Resource นั้น ๆ ครับ409 Conflict: Request ขัดแย้งกับสถานะปัจจุบันของ Resource ครับ มักเกิดจากการพยายามสร้าง Resource ที่มีอยู่แล้วครับ422 Unprocessable Entity: Request ถูกต้องตามไวยากรณ์ แต่ไม่สามารถประมวลผลได้เนื่องจากมีข้อผิดพลาดทางตรรกะหรือข้อมูลไม่ถูกต้องตามรูปแบบที่กำหนดครับ (สำคัญมากสำหรับ FastAPI ที่ใช้ Pydantic ในการ Validate)
- 5xx – Server Error: มีข้อผิดพลาดจากฝั่ง Server
500 Internal Server Error: เกิดข้อผิดพลาดที่ไม่คาดคิดบน Server ครับ502 Bad Gateway: Server ได้รับ Response ที่ไม่ถูกต้องจาก Upstream Server ครับ503 Service Unavailable: Server ไม่พร้อมที่จะจัดการ Request อาจเกิดจากการโอเวอร์โหลดหรือการบำรุงรักษาครับ
ทำความรู้จักกับ FastAPI
หลังจากที่เราเข้าใจพื้นฐานของ REST API แล้ว ตอนนี้ได้เวลามาทำความรู้จักกับพระเอกของเรา นั่นก็คือ FastAPI ครับ
ทำไมต้อง FastAPI?
FastAPI เป็นเฟรมเวิร์กเว็บประสิทธิภาพสูงที่สร้างขึ้นบน Python 3.7+ สำหรับการสร้าง API โดยเฉพาะครับ มันถูกออกแบบมาให้ทำงานร่วมกับ Standard Python Type Hints ซึ่งช่วยให้การเขียนโค้ดง่ายขึ้น, ลดข้อผิดพลาด และเพิ่มความเร็วในการพัฒนาอย่างมากครับ
คุณสมบัติเด่นของ FastAPI
FastAPI มีคุณสมบัติเด่นมากมายที่ทำให้มันโดดเด่นกว่าเฟรมเวิร์กอื่น ๆ ครับ:
- ความเร็วสูง (High Performance): สร้างขึ้นบน Starlette (สำหรับเว็บพาร์ท) และ Pydantic (สำหรับการจัดการข้อมูล) ทำให้มันเป็นหนึ่งในเฟรมเวิร์ก Python ที่เร็วที่สุดครับ เทียบเท่ากับ Node.js และ Go เลยทีเดียว
- การเขียนโค้ดแบบ Asynchronous (Async/Await): รองรับ
asyncและawaitได้อย่างเต็มที่ ทำให้สามารถจัดการ Concurrency ได้ดีเยี่ยม เหมาะสำหรับ I/O-bound Operations เช่น การอ่าน/เขียนฐานข้อมูล หรือการเรียกใช้ External API ครับ - Pydantic สำหรับ Data Validation และ Serialization: Pydantic เป็นไลบรารีที่ยอดเยี่ยมสำหรับการกำหนดรูปแบบข้อมูลและการตรวจสอบความถูกต้องของข้อมูล (Data Validation) FastAPI ใช้ Pydantic ในการตรวจสอบข้อมูลขาเข้า (Request Body) และกำหนดรูปแบบข้อมูลขาออก (Response Body) โดยอัตโนมัติ ทำให้โค้ดสะอาดขึ้นและลด boilerplate code ครับ
- เอกสาร API อัตโนมัติ (Automatic API Documentation): FastAPI สร้างเอกสาร API แบบอินเทอร์แอกทีฟ (Interactive Documentation) โดยอัตโนมัติ ตามมาตรฐาน OpenAPI (เดิมคือ Swagger) ทำให้คุณสามารถเข้าถึง Swagger UI และ ReDoc ได้ทันทีหลังจากรัน API ของคุณ เอกสารเหล่านี้ช่วยให้นักพัฒนาทั้งฝั่ง Backend และ Frontend เข้าใจ API ได้ง่ายขึ้นมากครับ
- ระบบ Type Hints ที่ทรงพลัง: ใช้ Python Type Hints ในการประกาศ Path Parameters, Query Parameters, Request Body และ Response Models ทำให้ IDE สามารถให้คำแนะนำ (Autocompletion) และตรวจสอบข้อผิดพลาด (Type Checking) ได้อย่างมีประสิทธิภาพครับ
- Dependency Injection System: มีระบบ Dependency Injection ที่ใช้งานง่ายและทรงพลัง ช่วยให้คุณสามารถจัดการกับ Dependency ต่าง ๆ เช่น การเชื่อมต่อฐานข้อมูล หรือการตรวจสอบสิทธิ์ผู้ใช้งาน ได้อย่างเป็นระเบียบและทดสอบง่ายครับ
- ลดเวลาในการพัฒนา: ด้วยคุณสมบัติทั้งหมดที่กล่าวมา FastAPI ช่วยให้นักพัฒนาสามารถเขียนโค้ดได้เร็วขึ้น 2-3 เท่า และลดข้อผิดพลาดลงได้ถึง 40% ครับ
เปรียบเทียบ FastAPI กับ Flask และ Django
เพื่อทำความเข้าใจว่า FastAPI โดดเด่นอย่างไร เรามาดูการเปรียบเทียบกับเฟรมเวิร์ก Python ยอดนิยมอื่น ๆ อย่าง Flask และ Django กันครับ
หมายเหตุ: การเลือกใช้เฟรมเวิร์กใด ๆ ขึ้นอยู่กับความต้องการและลักษณะของโปรเจกต์เป็นหลักครับ แต่ละเฟรมเวิร์กมีจุดเด่นและจุดด้อยที่แตกต่างกันไปครับ
| คุณสมบัติ | FastAPI | Flask | Django |
|---|---|---|---|
| วัตถุประสงค์หลัก | สร้าง REST API, Microservices | Microframework, เว็บแอปพลิเคชันขนาดเล็ก-กลาง, API | Full-stack Web Framework, เว็บแอปพลิเคชันขนาดใหญ่ |
| ประสิทธิภาพ (Performance) | ยอดเยี่ยม, สูงมาก (รองรับ Async/Await อย่างเต็มที่) | ดี, แต่โดยพื้นฐานเป็น Synchronous (สามารถใช้ Async ด้วย Extensions) | ดี, แต่โดยพื้นฐานเป็น Synchronous (รองรับ Async บางส่วนในเวอร์ชันใหม่ ๆ) |
| Data Validation / Serialization | ยอดเยี่ยม, ใช้ Pydantic ในตัว (อัตโนมัติและทรงพลัง) | ต้องใช้ไลบรารีภายนอก (เช่น Marshmallow, Pydantic) | Django ORM และ Forms มี Validation ในตัว, สำหรับ API ต้องใช้ Django REST Framework |
| เอกสาร API อัตโนมัติ | มีในตัว (OpenAPI/Swagger UI, ReDoc) | ต้องใช้ไลบรารีภายนอก (เช่น Flask-RESTX, Connexion) | ต้องใช้ Django REST Framework และ Extensions เพิ่มเติม |
| การเรียนรู้ | ปานกลาง-ง่าย (ถ้าคุ้นเคยกับ Type Hints) | ง่าย (Microframework) | ปานกลาง-ยาก (Full-stack มีแนวคิดเยอะ) |
| ขนาดของโค้ด (Boilerplate) | น้อยมาก | น้อย | ปานกลาง-มาก (มีโครงสร้างและ Convention เยอะ) |
| Type Hints | ใช้ Type Hints อย่างหนักหน่วงและเป็นประโยชน์ | รองรับแต่ไม่บังคับใช้ | รองรับแต่ไม่บังคับใช้ |
| ระบบนิเวศ (Ecosystem) | ใหม่แต่กำลังเติบโตอย่างรวดเร็ว | ใหญ่และมีมานาน (มี Extensions เยอะ) | ใหญ่และมีมานาน (มี Built-in Features และ Third-party Apps เยอะ) |
| เหมาะสำหรับ | REST APIs ประสิทธิภาพสูง, Microservices, งานที่ต้องการความเร็วและการตรวจสอบข้อมูลที่เข้มงวด | API ขนาดเล็ก, เว็บแอปพลิเคชันง่ายๆ, Prototype | เว็บแอปพลิเคชันขนาดใหญ่, แอปพลิเคชันที่ต้องการ Admin Panel, ORM, Template Engine ในตัว |
จากตารางเปรียบเทียบ จะเห็นได้ชัดว่า FastAPI มีจุดเด่นด้านประสิทธิภาพ, การตรวจสอบข้อมูล, และเอกสาร API อัตโนมัติ ซึ่งเป็นสิ่งสำคัญมากสำหรับการสร้าง REST API ในปัจจุบันครับ
การเตรียมความพร้อมก่อนเริ่มต้น
ก่อนที่เราจะเริ่มเขียนโค้ดจริง ๆ เราต้องเตรียมสภาพแวดล้อมการทำงานของเราให้พร้อมก่อนครับ
ติดตั้ง Python
FastAPI ต้องการ Python เวอร์ชัน 3.7 ขึ้นไปครับ ถ้าคุณยังไม่ได้ติดตั้ง Python สามารถดาวน์โหลดได้จากเว็บไซต์ทางการของ Python (python.org/downloads) ครับ แนะนำให้ติดตั้งเวอร์ชันล่าสุดเพื่อใช้ประโยชน์จากคุณสมบัติใหม่ ๆ ครับ
สร้างและใช้งาน Virtual Environment
การใช้ Virtual Environment เป็นแนวทางปฏิบัติที่ดีที่สุดในการพัฒนา Python ครับ ช่วยให้คุณสามารถจัดการแพ็กเกจ (Dependencies) ของแต่ละโปรเจกต์ได้อย่างอิสระ โดยไม่ไปรบกวนแพ็กเกจของโปรเจกต์อื่น ๆ หรือ Python ที่ติดตั้งมากับระบบครับ
ขั้นตอน:
-
สร้างไดเรกทอรีโปรเจกต์:
mkdir my-fastapi-app cd my-fastapi-app -
สร้าง Virtual Environment:
ใช้โมดูลvenvที่มาพร้อมกับ Python ครับpython3 -m venv venvคำสั่งนี้จะสร้างโฟลเดอร์ชื่อ
venvภายในโปรเจกต์ของคุณ ซึ่งจะเก็บไฟล์ที่จำเป็นสำหรับ Virtual Environment ครับ -
เปิดใช้งาน Virtual Environment:
- บน macOS/Linux:
source venv/bin/activate - บน Windows (Command Prompt):
venv\Scripts\activate.bat - บน Windows (PowerShell):
.\venv\Scripts\Activate.ps1
เมื่อเปิดใช้งานแล้ว คุณจะเห็น
(venv)นำหน้า Prompt ของ Terminal ซึ่งแสดงว่าคุณกำลังทำงานอยู่ใน Virtual Environment ครับ - บน macOS/Linux:
ติดตั้ง FastAPI และ Uvicorn
เมื่อ Virtual Environment พร้อมแล้ว เราก็ติดตั้ง FastAPI และ Uvicorn ได้เลยครับ
- FastAPI: ตัวเฟรมเวิร์กหลัก
- Uvicorn: ASGI server ที่ใช้รัน FastAPI application (ASGI ย่อมาจาก Asynchronous Server Gateway Interface เป็นมาตรฐานที่คล้ายกับ WSGI แต่รองรับ Async/Await)
ใช้คำสั่ง pip ในการติดตั้งครับ:
pip install fastapi uvicorn
ในบางกรณี คุณอาจต้องการติดตั้ง Uvicorn แบบ Standard ด้วย:
pip install "uvicorn[standard]"
ซึ่งจะรวมแพ็กเกจเสริมที่อาจจำเป็นสำหรับการทำงานของ Uvicorn ครับ
ตอนนี้คุณพร้อมที่จะเริ่มเขียนโค้ด FastAPI แล้วครับ!
แนวคิดหลักของ FastAPI ที่ควรรู้
ก่อนที่เราจะเริ่มสร้าง API จริงจัง เรามาทำความเข้าใจแนวคิดพื้นฐานบางอย่างของ FastAPI กันก่อนครับ สิ่งเหล่านี้เป็นแกนหลักที่คุณจะต้องใช้บ่อย ๆ ครับ
Path Operations
ใน FastAPI การสร้าง API Endpoint (หรือ Route) จะถูกเรียกว่า “Path Operation” ครับ โดยเราจะใช้ “decorator” (ฟังก์ชันที่เอาไปแปะบนฟังก์ชันอื่น) เพื่อระบุ HTTP Method และ Path ของ Endpoint นั้น ๆ ครับ
from fastapi import FastAPI
app = FastAPI()
@app.get("/") # นี่คือ Path Operation Decorator สำหรับ GET request ที่ Path "/"
async def read_root():
return {"message": "Hello, FastAPI!"}
@app.post("/items/") # สำหรับ POST request ที่ Path "/items/"
async def create_item():
return {"message": "Item created!"}
ในตัวอย่างข้างต้น @app.get("/") และ @app.post("/items/") คือ Path Operation Decorator ครับ
Path Parameters
Path Parameters คือค่าที่ถูกส่งมาใน URL path เพื่อระบุ Resource ที่เฉพาะเจาะจงครับ เช่น การดึงข้อมูลผู้ใช้งานที่มี ID เป็น 123 เราอาจจะมี Path เป็น /users/123
ใน FastAPI คุณสามารถประกาศ Path Parameters ได้โดยการใส่ชื่อพารามิเตอร์ในวงเล็บปีกกา {} ใน Path Operation Decorator และกำหนดให้เป็น Argument ของฟังก์ชัน Path Operation ครับ
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int): # กำหนด Type Hint เป็น int
return {"item_id": item_id}
FastAPI จะทำการตรวจสอบ Type (Type Validation) ให้อัตโนมัติด้วย Pydantic ครับ หาก item_id ที่ส่งมาไม่ใช่ตัวเลข จะเกิดข้อผิดพลาด 422 Unprocessable Entity ครับ
Query Parameters
Query Parameters คือค่าที่ส่งมาใน URL หลังจากเครื่องหมาย ? เพื่อกรอง, จัดเรียง หรือจำกัดข้อมูลที่ต้องการครับ เช่น /items/?skip=0&limit=10
ใน FastAPI คุณสามารถประกาศ Query Parameters ได้โดยการกำหนด Argument ให้กับฟังก์ชัน Path Operation ที่ไม่ได้อยู่ใน Path ครับ
from fastapi import FastAPI
from typing import Optional
app = FastAPI()
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10, q: Optional[str] = None):
results = {"skip": skip, "limit": limit}
if q:
results.update({"q": q})
return results
ในตัวอย่างนี้ skip และ limit มี Default Value และ q เป็น Optional (อาจมีหรือไม่มีก็ได้) ครับ FastAPI จะทำการตรวจสอบ Type และแปลงค่าให้อัตโนมัติครับ
Request Body และ Pydantic Models
เมื่อ Client ต้องการส่งข้อมูลจำนวนมากไปยัง Server (เช่น ใน POST หรือ PUT Request) ข้อมูลเหล่านั้นจะถูกส่งมาใน “Request Body” ครับ FastAPI ใช้ Pydantic ในการกำหนดรูปแบบและตรวจสอบความถูกต้องของข้อมูลใน Request Body ครับ
เราต้องสร้าง Pydantic Model (คลาสที่สืบทอดมาจาก BaseModel) เพื่อกำหนดโครงสร้างของข้อมูลครับ
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
# 1. สร้าง Pydantic Model สำหรับ Request Body
class Item(BaseModel):
name: str
description: str | None = None # Optional string, can be None
price: float
tax: float | None = None # Optional float
@app.post("/items/")
async def create_item(item: Item): # ประกาศ Type Hint ของ Argument เป็น Pydantic Model
item_dict = item.dict() # แปลง Pydantic object เป็น Python dictionary
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
เมื่อ Client ส่ง JSON data มายัง Endpoint /items/ FastAPI จะใช้ Pydantic เพื่อ:
- ตรวจสอบว่าข้อมูลที่ส่งมามีโครงสร้างและ Type ตรงกับ
ItemModel หรือไม่ - แปลงข้อมูล JSON เป็น Pydantic Object (
itemในตัวอย่าง) - ถ้าข้อมูลไม่ถูกต้อง จะส่ง
422 Unprocessable Entityกลับไปโดยอัตโนมัติ พร้อมรายละเอียดข้อผิดพลาดครับ
Response Model
นอกจากการตรวจสอบข้อมูลขาเข้าแล้ว FastAPI ยังสามารถกำหนดรูปแบบของข้อมูลขาออก (Response Body) ได้ด้วย response_model Argument ใน Path Operation Decorator ครับ สิ่งนี้มีประโยชน์มากในการ:
- รับประกันว่า API ของคุณจะส่งข้อมูลกลับไปในรูปแบบที่ถูกต้องเสมอ
- ซ่อนข้อมูลบางอย่างที่ไม่ต้องการให้ Client เห็น (เช่น รหัสผ่าน)
- ช่วยให้เอกสาร API ชัดเจนยิ่งขึ้น
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class ItemBase(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class ItemCreate(ItemBase):
pass # ใช้สำหรับสร้าง
class ItemResponse(ItemBase):
id: int # เพิ่ม id เข้ามาสำหรับ Response
owner_id: int # สมมติว่ามี owner_id
# สามารถเพิ่มฟิลด์ที่คำนวณได้ หรือซ่อนฟิลด์ที่ไม่ต้องการให้เห็นได้
class Config:
orm_mode = True # สำคัญสำหรับการทำงานกับ ORM
@app.post("/items/", response_model=ItemResponse) # กำหนด response_model
async def create_item(item: ItemCreate):
# สมมติว่านี่คือการบันทึกข้อมูลลงฐานข้อมูลและได้ id กลับมา
fake_db_item = {"id": 1, "owner_id": 10, **item.dict()}
return fake_db_item
ในตัวอย่างนี้ แม้ว่า fake_db_item อาจจะมีฟิลด์อื่น ๆ แต่ FastAPI จะกรองและส่งกลับเฉพาะฟิลด์ที่กำหนดใน ItemResponse Model เท่านั้นครับ
การจัดการข้อผิดพลาด (Error Handling)
FastAPI มีกลไกการจัดการข้อผิดพลาดในตัวที่ดีอยู่แล้ว โดยเฉพาะการใช้ Pydantic ในการ Validate ข้อมูลขาเข้า แต่บางครั้งเราอาจต้องการจัดการข้อผิดพลาดแบบกำหนดเองครับ
เราสามารถใช้ HTTPException จากโมดูล fastapi เพื่อส่ง HTTP Error Response กลับไปได้ครับ
from fastapi import FastAPI, HTTPException, status
app = FastAPI()
fake_items_db = {"foo": {"name": "Foo", "price": 50.2}}
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Item not found",
headers={"X-Error": "There goes my error"}, # สามารถเพิ่ม Custom Headers ได้
)
return {"item": fake_items_db[item_id]}
ในตัวอย่างนี้ ถ้า item_id ไม่พบในฐานข้อมูล เราจะส่ง HTTPException ที่มี Status Code 404 Not Found และข้อความ "Item not found" กลับไปครับ
ลงมือสร้าง REST API (CRUD) ด้วย FastAPI: ตัวอย่างระบบจัดการงาน (Task Manager)
ได้เวลาลงมือสร้าง API จริง ๆ กันแล้วครับ! เราจะสร้าง REST API สำหรับระบบจัดการงาน (Task Manager) ที่สามารถทำ CRUD (Create, Read, Update, Delete) ได้ครับ
ในตัวอย่างนี้ เราจะใช้ In-memory Database (ข้อมูลเก็บในตัวแปรในหน่วยความจำ) เพื่อให้ง่ายต่อการสาธิตครับ เมื่อ Server รีสตาร์ท ข้อมูลจะหายไป แต่ในโปรเจกต์จริง คุณจะต้องเชื่อมต่อกับฐานข้อมูลจริง ๆ เช่น PostgreSQL, MySQL, MongoDB เป็นต้นครับ
การตั้งค่าโปรเจกต์เบื้องต้น
ถ้าคุณยังไม่ได้ทำ ให้สร้างไดเรกทอรีโปรเจกต์และ Virtual Environment ตามขั้นตอนในส่วน การเตรียมความพร้อมก่อนเริ่มต้น ครับ
mkdir fastapi_task_manager
cd fastapi_task_manager
python3 -m venv venv
source venv/bin/activate # บน macOS/Linux
# หรือ venv\Scripts\activate.bat บน Windows CMD
# หรือ .\venv\Scripts\Activate.ps1 บน Windows PowerShell
pip install fastapi uvicorn
จากนั้น สร้างไฟล์ main.py ในไดเรกทอรี fastapi_task_manager ครับ
FastAPI “Hello World” แรกของคุณ
มาเริ่มต้นด้วย API Endpoint ง่าย ๆ เพื่อทดสอบว่าทุกอย่างทำงานได้ถูกต้องครับ
ไฟล์: main.py
from fastapi import FastAPI
# สร้าง Instance ของ FastAPI Application
app = FastAPI()
# กำหนด Path Operation สำหรับ GET Request ที่ Root Path ("/")
@app.get("/")
async def read_root():
"""
Endpoint สำหรับทดสอบว่า API ทำงานอยู่หรือไม่
ส่งคืนข้อความต้อนรับ
"""
return {"message": "Welcome to FastAPI Task Manager API!"}
วิธีรัน API:
เปิด Terminal (ที่เปิดใช้งาน Virtual Environment) ในไดเรกทอรีโปรเจกต์ของคุณ และรันคำสั่ง Uvicorn ครับ:
uvicorn main:app --reload
main: ชื่อไฟล์ Python ของเรา (main.py)app: ชื่อ Instance ของ FastAPI ที่เราสร้างในไฟล์ (app = FastAPI())--reload: โหมดนี้จะทำให้ Server รีโหลดอัตโนมัติเมื่อมีการเปลี่ยนแปลงโค้ดในไฟล์ครับ
คุณจะเห็นข้อความประมาณนี้ใน Terminal ครับ:
INFO: Will watch for changes in these directories: ['/path/to/fastapi_task_manager']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [xxxxx] using statreload
INFO: Started server process [xxxxx]
INFO: Waiting for application startup.
INFO: Application startup complete.
จากนั้น เปิดเว็บเบราว์เซอร์แล้วไปที่ http://127.0.0.1:8000 คุณควรจะเห็น JSON Response: {"message": "Welcome to FastAPI Task Manager API!"}
นอกจากนี้ FastAPI ยังสร้างเอกสาร API ให้อัตโนมัติครับ ลองไปที่ http://127.0.0.1:8000/docs (Swagger UI) หรือ http://127.0.0.1:8000/redoc (ReDoc) คุณจะเห็นเอกสาร API ที่สร้างขึ้นสำหรับ Endpoint / ของคุณครับ นี่คือหนึ่งในคุณสมบัติที่ยอดเยี่ยมของ FastAPI ครับ!
การสร้าง Data Model ด้วย Pydantic
เราจะสร้าง Pydantic Models เพื่อกำหนดโครงสร้างข้อมูลสำหรับ Task (งาน) ของเราครับ
ไฟล์: main.py (ต่อจากโค้ดเดิม)
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
from typing import List, Optional, Dict
import uuid # สำหรับสร้าง Task ID ที่ไม่ซ้ำกัน
# สร้าง Instance ของ FastAPI Application
app = FastAPI(
title="FastAPI Task Manager",
description="A simple CRUD API for managing tasks.",
version="1.0.0",
)
# --- Pydantic Models สำหรับ Task ---
# Base Model สำหรับ Task - กำหนดฟิลด์พื้นฐานที่ทุก Model จะมี
class TaskBase(BaseModel):
title: str = Field(..., example="ซื้อของเข้าบ้าน", description="ชื่อของงานที่ต้องทำ")
description: Optional[str] = Field(None, example="นม, ไข่, ขนมปัง", description="รายละเอียดเพิ่มเติมของงาน")
completed: bool = Field(False, description="สถานะของงานว่าเสร็จสิ้นแล้วหรือไม่")
# Model สำหรับการสร้าง Task ใหม่ (ใช้ใน POST Request)
# สืบทอดจาก TaskBase
class TaskCreate(TaskBase):
pass # ไม่มีฟิลด์เพิ่มเติมจาก TaskBase
# Model สำหรับการอัปเดต Task (ใช้ใน PUT/PATCH Request)
# ทุกฟิลด์เป็น Optional เพื่อให้สามารถอัปเดตบางส่วนได้
class TaskUpdate(TaskBase):
title: Optional[str] = Field(None, example="ซื้อของเข้าบ้าน", description="ชื่อของงานที่ต้องทำ")
description: Optional[str] = Field(None, example="นม, ไข่, ขนมปัง, ผลไม้", description="รายละเอียดเพิ่มเติมของงาน")
completed: Optional[bool] = Field(None, description="สถานะของงานว่าเสร็จสิ้นแล้วหรือไม่")
# Model สำหรับ Response ที่ส่งกลับไปยัง Client
# เพิ่ม 'id' เข้ามา ซึ่งเป็นสิ่งที่จะถูกสร้างขึ้นโดย Server
class TaskResponse(TaskBase):
id: uuid.UUID = Field(..., example="a1b2c3d4-e5f6-7890-1234-567890abcdef", description="Unique identifier ของงาน")
class Config:
# Pydantic จะพยายามอ่านข้อมูลจาก ORM objects (เช่น SQLAlchemy)
# แม้ว่าในตัวอย่างนี้เราจะใช้ dict แต่ก็เป็นการเตรียมพร้อมที่ดีครับ
orm_mode = True
คำอธิบายโค้ด:
uuid: ใช้สำหรับสร้าง ID ที่ไม่ซ้ำกันสำหรับแต่ละ Task ครับTaskBase: กำหนดฟิลด์title,description,completedที่เป็นพื้นฐานของ Task ครับField(...): ใช้สำหรับเพิ่ม Metadata ให้กับฟิลด์ เช่นexampleสำหรับเอกสาร API และdescriptionครับOptional[str]หรือstr | None(Python 3.10+): ระบุว่าฟิลด์นี้เป็น Optional และสามารถเป็นNoneได้
TaskCreate: ใช้สำหรับรับข้อมูลเมื่อ Client ต้องการสร้าง Task ใหม่ครับTaskUpdate: ใช้สำหรับรับข้อมูลเมื่อ Client ต้องการอัปเดต Task ครับ ทุกฟิลด์เป็นOptionalเพื่อรองรับการอัปเดตบางส่วนครับTaskResponse: ใช้สำหรับกำหนดรูปแบบของข้อมูลที่ Server จะส่งกลับไปยัง Client ครับ เราเพิ่มidเข้ามาที่นี่ ซึ่งเป็น ID ที่ Server สร้างขึ้นมาครับclass Config: orm_mode = True: เป็นการบอก Pydantic ให้สามารถอ่านข้อมูลจาก object ที่ไม่ได้เป็น dictionary ตรง ๆ ได้ (เช่น object จาก ORM) แม้ในตัวอย่างนี้เราจะยังไม่ใช้ ORM แต่ก็เป็น Best Practice ครับ
การจำลองฐานข้อมูล In-memory
เราจะใช้ Python Dictionary เพื่อเก็บข้อมูล Task ชั่วคราวครับ คล้ายกับการเป็นฐานข้อมูลขนาดเล็กในหน่วยความจำครับ
ไฟล์: main.py (ต่อจากโค้ดเดิม)
# --- In-memory Database (สำหรับตัวอย่าง) ---
# เก็บ Task ด้วย id เป็น key และ TaskResponse (dict) เป็น value
tasks_db: Dict[uuid.UUID, TaskResponse] = {}
# เพื่อให้ง่ายต่อการทดสอบ เราเพิ่มข้อมูลเริ่มต้นไปบ้าง
# (ถ้าใช้ --reload ข้อมูลจะถูกรีเซ็ตทุกครั้งที่ไฟล์ถูกบันทึก)
initial_task_id_1 = uuid.uuid4()
tasks_db[initial_task_id_1] = TaskResponse(
id=initial_task_id_1,
title="เรียนรู้ FastAPI",
description="อ่านเอกสารและลองเขียนโค้ด",
completed=False
)
initial_task_id_2 = uuid.uuid4()
tasks_db[initial_task_id_2] = TaskResponse(
id=initial_task_id_2,
title="สร้างโปรเจกต์แรก",
description="นำความรู้ที่ได้มาสร้าง API จริง",
completed=True
)
สร้าง API Endpoint: POST /tasks (เพิ่มงานใหม่)
Endpoint นี้จะใช้สำหรับสร้าง Task ใหม่ครับ
ไฟล์: main.py (ต่อจากโค้ดเดิม)
# --- Path Operations (API Endpoints) ---
@app.post(
"/tasks/",
response_model=TaskResponse,
status_code=status.HTTP_201_CREATED,
summary="สร้างงานใหม่",
description="สร้างงานใหม่ด้วยข้อมูลที่ให้มา และส่งคืนข้อมูลงานที่ถูกสร้างพร้อม ID",
tags=["Tasks"] # จัดกลุ่มในเอกสาร Swagger UI
)
async def create_task(task: TaskCreate):
"""
รับข้อมูล Task จาก Request Body และสร้าง Task ใหม่
- **title**: ชื่อของงาน (required)
- **description**: รายละเอียดของงาน (optional)
- **completed**: สถานะของงาน (default: False)
"""
new_id = uuid.uuid4()
# สร้าง TaskResponse object จาก TaskCreate และเพิ่ม ID เข้าไป
new_task = TaskResponse(id=new_id, **task.dict())
tasks_db[new_id] = new_task # บันทึกลงในฐานข้อมูลจำลอง
return new_task
คำอธิบาย:
@app.post("/tasks/"): กำหนดว่าเป็น POST Request ที่ Path/tasks/response_model=TaskResponse: ระบุว่า Response ที่ส่งกลับไปจะต้องมีรูปแบบตามTaskResponsestatus_code=status.HTTP_201_CREATED: เมื่อสร้างสำเร็จ จะคืน Status Code 201 Created ครับtask: TaskCreate: FastAPI จะใช้ Pydantic ModelTaskCreateในการตรวจสอบข้อมูลจาก Request Body ครับ- เราสร้าง
uuid.uuid4()เพื่อเป็น ID สำหรับ Task ใหม่ครับ - แปลง
TaskCreateobject เป็น dictionary ด้วยtask.dict()แล้วรวมกับ ID เพื่อสร้างTaskResponseobject
สร้าง API Endpoint: GET /tasks (ดึงงานทั้งหมด)
Endpoint นี้จะใช้สำหรับดึง Task ทั้งหมดครับ พร้อมรองรับ Query Parameters สำหรับ Pagination
ไฟล์: main.py (ต่อจากโค้ดเดิม)
@app.get(
"/tasks/",
response_model=List[TaskResponse], # Response เป็น List ของ TaskResponse
summary="ดึงงานทั้งหมด",
description="ดึงรายการงานทั้งหมด สามารถใช้ Query Parameters เพื่อจำกัดจำนวนและข้ามรายการได้",
tags=["Tasks"]
)
async def get_all_tasks(
skip: int = Field(0, ge=0, description="จำนวนรายการที่ต้องการข้าม"), # ge=0 หมายถึง ต้องมากกว่าหรือเท่ากับ 0
limit: int = Field(10, gt=0, le=100, description="จำนวนรายการสูงสุดที่ต้องการดึง (1-100)") # gt=0, le=100 คือ 1 ถึง 100
):
"""
ดึงรายการงานทั้งหมดจากฐานข้อมูลจำลอง
- **skip**: จำนวนงานที่ต้องการข้าม (ค่าเริ่มต้น 0)
- **limit**: จำนวนงานสูงสุดที่ต้องการดึง (ค่าเริ่มต้น 10, สูงสุด 100)
"""
# ดึงค่าจาก tasks_db มาเป็น List แล้วทำการ Slice ตาม skip และ limit
tasks_list = list(tasks_db.values())
return tasks_list[skip : skip + limit]
คำอธิบาย:
response_model=List[TaskResponse]: ระบุว่า Response เป็น List ของTaskResponseobjects ครับskip: int = Field(0, ge=0, ...)และlimit: int = Field(10, gt=0, le=100, ...): ใช้ Field เพื่อเพิ่ม Validation ให้กับ Query Parameters ครับ เช่นge=0(greater or equal) และgt=0, le=100(greater than 0, less or equal than 100)- เราแปลง
tasks_db.values()ให้เป็น List แล้วใช้ Slice เพื่อ implement Pagination อย่างง่ายครับ
สร้าง API Endpoint: GET /tasks/{task_id} (ดึงงานตาม ID)
Endpoint นี้จะใช้สำหรับดึง Task เฉพาะเจาะจงตาม ID ครับ
ไฟล์: main.py (ต่อจากโค้ดเดิม)
@app.get(
"/tasks/{task_id}",
response_model=TaskResponse,
summary="ดึงงานตาม ID",
description="ดึงข้อมูลงานเดี่ยวตาม ID ที่ระบุ",
tags=["Tasks"]
)
async def get_task_by_id(task_id: uuid.UUID = Field(..., description="ID ของงานที่ต้องการดึง")):
"""
ดึงข้อมูลงานจากฐานข้อมูลจำลองตาม ID ที่ระบุ
- **task_id**: ID ของงาน (required)
"""
if task_id not in tasks_db:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Task not found")
return tasks_db[task_id]
คำอธิบาย:
task_id: uuid.UUID: กำหนดให้task_idใน Path เป็น Typeuuid.UUIDครับ FastAPI จะตรวจสอบ Type ให้อัตโนมัติครับ- ถ้าไม่พบ Task ด้วย ID ที่ระบุ เราจะส่ง
HTTPExceptionพร้อม Status Code 404 Not Found กลับไปครับ
สร้าง API Endpoint: PUT /tasks/{task_id} (อัปเดตงาน)
Endpoint นี้จะใช้สำหรับอัปเดตข้อมูล Task ที่มีอยู่ครับ
ไฟล์: main.py (ต่อจากโค้ดเดิม)
@app.put(
"/tasks/{task_id}",
response_model=TaskResponse,
summary="อัปเดตงาน",
description="อัปเดตข้อมูลงานที่มีอยู่ตาม ID ที่ระบุ",
tags=["Tasks"]
)
async def update_task(
task_id: uuid.UUID = Field(..., description="ID ของงานที่ต้องการอัปเดต"),
task_update: TaskUpdate # ใช้ TaskUpdate Model สำหรับ Request Body
):
"""
อัปเดตข้อมูลงานในฐานข้อมูลจำลองตาม ID ที่ระบุ
- **task_id**: ID ของงาน (required)
- **task_update**: ข้อมูลงานที่ต้องการอัปเดต (Request Body)
"""
if task_id not in tasks_db:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Task not found")
existing_task = tasks_db[task_id]
# อัปเดตเฉพาะฟิลด์ที่มีค่าส่งมา (ไม่ใช่ None)
update_data = task_update.dict(exclude_unset=True) # exclude_unset=True จะไม่รวมฟิลด์ที่ไม่ถูกตั้งค่าใน Request Body
# ทำการอัปเดตข้อมูลใน existing_task
updated_task_data = existing_task.copy(update=update_data) # ใช้ .copy(update=...) ของ Pydantic
tasks_db[task_id] = updated_task_data # บันทึก Task ที่อัปเดตแล้วกลับเข้าไปใน DB
return updated_task_data
คำอธิบาย:
task_update: TaskUpdate: ใช้TaskUpdateModel ที่ทุกฟิลด์เป็น Optional เพื่อรองรับการอัปเดตบางส่วนครับtask_update.dict(exclude_unset=True): เป็นเทคนิคสำคัญในการอัปเดตข้อมูลครับexclude_unset=Trueจะบอกให้ Pydantic สร้าง Dictionary ที่มีเฉพาะฟิลด์ที่ถูกส่งมาใน Request Body เท่านั้น ไม่รวมฟิลด์ที่ไม่ถูกส่งมา (ซึ่งจะเป็นNoneตาม Default ของOptional)existing_task.copy(update=update_data): เป็นวิธีที่ปลอดภัยและง่ายในการอัปเดต Pydantic Model โดยการสร้าง Instance ใหม่ที่ผสานข้อมูลเดิมกับข้อมูลที่ต้องการอัปเดตครับ
อ่านเพิ่มเติมเกี่ยวกับ Pydantic Model updates
สร้าง API Endpoint: DELETE /tasks/{task_id} (ลบงาน)
Endpoint นี้จะใช้สำหรับลบ Task ครับ
ไฟล์: main.py (ต่อจากโค้ดเดิม)
@app.delete(
"/tasks/{task_id}",
status_code=status.HTTP_204_NO_CONTENT, # เมื่อลบสำเร็จ จะคืน 204 No Content
summary="ลบงาน",
description="ลบงานออกจากระบบตาม ID ที่ระบุ",
tags=["Tasks"]
)
async def delete_task(task_id: uuid.UUID = Field(..., description="ID ของงานที่ต้องการลบ")):
"""
ลบงานจากฐานข้อมูลจำลองตาม ID ที่ระบุ
- **task_id**: ID ของงาน (required)
"""
if task_id not in tasks_db:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Task not found")
del tasks_db[task_id] # ลบ Task ออกจาก dictionary
return # ไม่ส่ง Content กลับไปสำหรับ 204 No Content
คำอธิบาย:
status_code=status.HTTP_204_NO_CONTENT: เมื่อลบสำเร็จ เราจะคืน Status Code 204 ซึ่งหมายถึงว่า Server ประสบความสำเร็จในการประมวลผล Request แต่ไม่มี Content ใด ๆ จะส่งกลับไปครับdel tasks_db[task_id]: ลบ Entry ออกจาก Dictionary ครับ
วิธีรันและทดสอบ API ของคุณ
ตอนนี้โค้ด main.py ของเราควรจะมีครบทุกส่วนแล้วครับ
-
บันทึกไฟล์
main.py -
ตรวจสอบว่า Uvicorn Server ยังทำงานอยู่ (ถ้าใช้
--reloadมันจะรีโหลดอัตโนมัติ) ถ้าไม่ ให้รันใหม่ด้วยuvicorn main:app --reload -
เข้าถึงเอกสาร API (Swagger UI): เปิดเว็บเบราว์เซอร์ไปที่ http://127.0.0.1:8000/docs
คุณจะเห็นเอกสาร API ที่สร้างขึ้นมาโดยอัตโนมัติสำหรับทุก Endpoint ที่เราสร้างไปครับ!
- ทดสอบ GET /tasks: คลิกที่
GET /tasksแล้วคลิก “Try it out” จากนั้นคลิก “Execute” คุณจะเห็นรายการ Task ทั้งหมดที่อยู่ในฐานข้อมูลจำลองครับ - ทดสอบ POST /tasks: คลิกที่
POST /tasksแล้วคลิก “Try it out” ในส่วน “Request body” คุณสามารถแก้ไขข้อมูล JSON เพื่อสร้าง Task ใหม่ได้ครับ เช่น:{ "title": "ออกกำลังกาย", "description": "วิ่ง 30 นาที", "completed": false }จากนั้นคลิก “Execute” คุณจะเห็น Response ที่เป็น Task ที่ถูกสร้างขึ้นมาพร้อม ID ครับ
- ทดสอบ GET /tasks/{task_id}: คัดลอก ID จาก Task ที่เพิ่งสร้างไป หรือจาก Task เริ่มต้น จากนั้นวางลงในช่อง
task_idใต้GET /tasks/{task_id}แล้วคลิก “Execute” คุณควรจะเห็นข้อมูลของ Task นั้น ๆ ครับ - ทดสอบ PUT /tasks/{task_id}: คล้ายกับการ GET แต่ใส่ข้อมูลอัปเดตใน “Request body” ครับ เช่น เปลี่ยน
completedเป็นtrueแล้วคลิก “Execute” - ทดสอบ DELETE /tasks/{task_id}: ใส่ ID ของ Task ที่ต้องการลบ แล้วคลิก “Execute” คุณควรจะได้รับ 204 No Content ครับ
- ทดสอบ GET /tasks: คลิกที่
ยินดีด้วยครับ! คุณได้สร้าง REST API ที่สมบูรณ์แบบด้วย FastAPI ที่สามารถทำ CRUD ได้แล้วครับ!
แนวคิดขั้นสูงใน FastAPI (ภาพรวม)
เมื่อคุณคุ้นเคยกับพื้นฐานแล้ว คุณอาจต้องการสำรวจคุณสมบัติขั้นสูงของ FastAPI เพื่อสร้าง API ที่ซับซ้อนและมีประสิทธิภาพมากขึ้นครับ
Dependency Injection
Dependency Injection (DI) เป็นหนึ่งในคุณสมบัติที่ทรงพลังที่สุดของ FastAPI ครับ ช่วยให้คุณสามารถประกาศ “Dependencies” (เช่น การเชื่อมต่อฐานข้อมูล, การตรวจสอบสิทธิ์ผู้ใช้ปัจจุบัน) ที่ Path Operation ฟังก์ชันของคุณต้องการได้อย่างง่ายดาย
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI()
# นี่คือ Dependency Function
async def get_current_user(token: str):
if token != "secret-token":
raise HTTPException(status_code=400, detail="Invalid token")
return {"username": "admin"}
@app.get("/users/me/")
async def read_current_user(current_user: dict = Depends(get_current_user)):
return current_user
ในตัวอย่างนี้ get_current_user เป็น Dependency ที่จะถูกเรียกก่อน read_current_user ครับ ผลลัพธ์ของ get_current_user จะถูกส่งเป็น Argument current_user ให้กับ read_current_user ครับ
APIRouter และการจัดโครงสร้างโปรเจกต์
สำหรับโปรเจกต์ขนาดใหญ่ การรวม Path Operations ทั้งหมดไว้ในไฟล์ main.py ไฟล์เดียวอาจทำให้โค้ดอ่านยากและจัดการลำบากครับ FastAPI มี APIRouter เพื่อช่วยในการจัดโครงสร้างโค้ดของคุณให้เป็นโมดูลาร์มากขึ้นครับ
คุณสามารถสร้างไฟล์แยกสำหรับแต่ละกลุ่มของ Endpoint (เช่น users.py, items.py) แล้วนำ Router มารวมกันใน main.py ครับ
# ตัวอย่าง users.py
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/")
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
# ตัวอย่าง main.py
from fastapi import FastAPI
from .users import router as users_router # import router จากไฟล์ users.py
app = FastAPI()
app.include_router(users_router, prefix="/api/v1") # เพิ่ม prefix ให้กับทุก path ใน router นี้
การใช้ APIRouter ช่วยให้โค้ดของคุณเป็นระเบียบ, จัดการง่าย, และนำกลับมาใช้ใหม่ได้ครับ อ่านเพิ่มเติมเกี่ยวกับการจัดโครงสร้างโปรเจกต์ FastAPI
การรักษาความปลอดภัย (Authentication & Authorization)
FastAPI มีเครื่องมือและแนวทางปฏิบัติที่ยอดเยี่ยมสำหรับการรักษาความปลอดภัย API ของคุณครับ มันรองรับมาตรฐานความปลอดภัยเว็บที่หลากหลาย รวมถึง:
- OAuth2 (พร้อม JWT tokens)
- HTTP Basic Auth
- API Keys
โดยใช้ระบบ Dependency Injection ร่วมกับฟังก์ชัน Helper จาก fastapi.security ทำให้การเพิ่มระบบยืนยันตัวตนและการอนุญาตเป็นเรื่องง่ายและได้มาตรฐานครับ
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # "token" คือ URL ที่ client จะส่ง username/password มาแลก token
@app.get("/users/me_secure/")
async def read_current_user_secure(token: str = Depends(oauth2_scheme)):
# ในโลกจริง คุณจะต้อง verify token นี้กับ database หรือ third-party service
if token == "johndoe_token":
return {"username": "johndoe", "token": token}
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
การใช้ Depends(oauth2_scheme) ทำให้ FastAPI ตรวจสอบว่ามี Token ส่งมาใน Header Authorization: Bearer <token> หรือไม่ครับ
CORS (Cross-Origin Resource Sharing)
CORS เป็นกลไกความปลอดภัยของเบราว์เซอร์ที่ป้องกันไม่ให้เว็บเพจจากโดเมนหนึ่งส่ง Request ไปยัง API ที่อยู่ในอีกโดเมนหนึ่งครับ หากคุณกำลังพัฒนา API ที่จะถูกเรียกใช้โดย Frontend ที่อยู่ในโดเมนอื่น คุณจะต้องเปิดใช้งาน CORS ครับ
FastAPI มี CORSMiddleware ที่ใช้งานง่ายครับ
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost",
"http://localhost:8080",
"https://your-frontend-domain.com", # เพิ่มโดเมนของ Frontend ของคุณที่นี่
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # อนุญาตเฉพาะโดเมนใน List นี้
allow_credentials=True,
allow_methods=["*"], # อนุญาตทุก HTTP Methods
allow_headers=["*"], # อนุญาตทุก Headers
)
@app.get("/")
async def root():
return {"message": "Hello CORS!"}
เมื่อเพิ่ม CORSMiddleware แล้ว เบราว์เซอร์จะสามารถส่ง Request จากโดเมนที่ระบุใน allow_origins มายัง API ของคุณได้ครับ
ข้อควรพิจารณาในการ Deploy FastAPI Application
เมื่อคุณสร้าง API เสร็จแล้ว ขั้นตอนต่อไปคือการนำมันไปใช้งานจริง (Deployment) เพื่อให้คนอื่น ๆ สามารถเข้าถึงได้ครับ
Uvicorn เป็น ASGI server ที่ยอดเยี่ยมสำหรับการพัฒนา แต่สำหรับการ Production คุณอาจต้องการใช้ร่วมกับ Gunicorn ครับ
- Gunicorn: เป็น WSGI HTTP Server ที่ใช้กันอย่างแพร่หลายใน Python เพื่อจัดการ Process และ Workers ครับ แม้ FastAPI จะเป็น ASGI แต่ Gunicorn สามารถใช้เป็น Process Manager เพื่อรัน Uvicorn Workers หลาย ๆ ตัวได้ครับ
- Nginx/Traefik: เป็น Reverse Proxy ที่อยู่ด้านหน้า Gunicorn/Uvicorn เพื่อจัดการเรื่อง SSL/TLS (HTTPS), Load Balancing, และการให้บริการ Static Files ครับ
- Docker: เป็นเครื่องมือที่ยอดเยี่ยมในการทำ Containerization ครับ ช่วยให้คุณสามารถบรรจุแอปพลิเคชันและ Dependencies ทั้งหมดลงใน Container เดียว ทำให้การ Deploy ง่ายขึ้นและมีความสอดคล้องกันในทุกสภาพแวดล้อมครับ
- Cloud Platforms:
- Heroku: ง่ายต่อการเริ่มต้นและ Deploy
- AWS (EC2, ECS, Lambda), Google Cloud (Compute Engine, Cloud Run), Azure (App Service): ให้ความยืดหยุ่นและ Scalability สูง แต่ก็มีความซับซ้อนในการตั้งค่ามากกว่าครับ
การใช้ Docker และ Gunicorn + Uvicorn เป็นแนวทางที่นิยมสำหรับการ Deploy FastAPI ใน Production ครับ
อ่านเพิ่มเติมเกี่ยวกับ Deployment FastAPI
คำถามที่พบบ่อย (FAQ)
Q1: Pydantic คืออะไร และทำไม FastAPI ถึงใช้มัน?
A: Pydantic เป็นไลบรารี Python สำหรับการตรวจสอบและจัดการข้อมูล (Data Validation และ Settings Management) ครับ FastAPI ใช้ Pydantic เป็นหัวใจหลักในการ:
- ตรวจสอบข้อมูลขาเข้า (Request Body): เมื่อ Client ส่ง JSON มา FastAPI จะใช้ Pydantic ตรวจสอบว่าข้อมูลมี Type และโครงสร้างที่ถูกต้องตามที่คุณกำหนดไว้ใน Pydantic Models หรือไม่ ถ้าไม่ถูกต้องจะส่ง
422 Unprocessable Entityกลับไปโดยอัตโนมัติครับ - แปลงข้อมูล (Serialization/Deserialization): แปลงข้อมูล JSON ที่รับเข้ามาเป็น Python Object (Pydantic Model Instance) และแปลง Pydantic Model Instance กลับเป็น JSON สำหรับ Response ครับ
- สร้างเอกสาร API: Pydantic Models เป็นพื้นฐานในการสร้างเอกสาร OpenAPI (Swagger UI/ReDoc) ให้กับ API ของคุณโดยอัตโนมัติ ทำให้เอกสารมีความถูกต้องและเป็นปัจจุบันเสมอครับ
การใช้ Pydantic ช่วยลด boilerplate code, เพิ่มความปลอดภัย, และทำให้การพัฒนา API รวดเร็วขึ้นมากครับ
Q2: ทำไม FastAPI ถึงเน้นเรื่อง Async/Await?
A: FastAPI ถูกออกแบบมาให้ใช้ประโยชน์จากความสามารถของ Python ในการเขียนโค้ดแบบ Asynchronous (async/await) ได้อย่างเต็มที่ครับ เหตุผลหลักคือ:
- ประสิทธิภาพสูง: โดยเฉพาะสำหรับ I/O-bound Operations (เช่น การรอข้อมูลจากฐานข้อมูล, การเรียก External API, การอ่าน/เขียนไฟล์) การใช้ Async/Await ทำให้ Server ไม่ต้องรอให้ Operation หนึ่งเสร็จสิ้นก่อนที่จะไปประมวลผล Request อื่น ๆ ครับ ทำให้ Server สามารถจัดการ Request พร้อมกันได้จำนวนมาก (High Concurrency) โดยใช้ทรัพยากรน้อยลง
- Scalability: ช่วยให้ API ของคุณสามารถรองรับปริมาณงานที่สูงขึ้นได้ดีกว่า โดยไม่ต้องเพิ่มทรัพยากร Server มากเกินไปครับ
- Modern Python: เป็นแนวทางปฏิบัติที่ทันสมัยในการพัฒนา Python Application สำหรับงานเว็บที่ต้องการประสิทธิภาพสูงครับ
ถึงแม้ FastAPI จะอนุญาตให้คุณเขียน Path Operations แบบ Synchronous ได้ แต่การใช้ async def จะปลดล็อกศักยภาพสูงสุดของเฟรมเวิร์กนี้ครับ
Q3: จะเชื่อมต่อ FastAPI กับฐานข้อมูลจริงได้อย่างไร?
A: สำหรับการเชื่อมต่อกับฐานข้อมูลจริง คุณจะต้องใช้ไลบรารีประเภท ORM (Object-Relational Mapper) หรือ Query Builder ครับ:
- SQL Databases (PostgreSQL, MySQL, SQLite, etc.):
- SQLAlchemy: เป็น ORM ที่ทรงพลังและยืดหยุ่นที่สุดสำหรับ Python ครับ FastAPI สามารถทำงานร่วมกับ SQLAlchemy ได้อย่างดีเยี่ยม โดยเฉพาะเมื่อใช้ร่วมกับระบบ Dependency Injection ในการจัดการ Session ของฐานข้อมูลครับ
- SQLModel: เป็นไลบรารีที่สร้างโดยผู้สร้าง FastAPI เอง