ทดลองสร้าง API Endpoint
ทำความเข้าใจโครงสร้างของ FastAPI ⚡
หลังจากที่เราติดตั้ง FastAPI และทดลองรัน Code ตัวอย่างได้เรียบร้อยแล้ว คราวนี้เรามาทำความเข้าใจกันหน่อยว่า Code ที่เรารันไปเมื่อกี้นี้ มันทำงานยังไงกันนะ
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}Initializer
เริ่มจาก
app = FastAPI()บรรทัดนี้เป็นการสร้าง Instance ของ FastAPI ที่เราได้ Import มานั่นเอง พูดให้เข้าใจง่าย ๆ คือ FastAPI คือ Class หรือ Blueprint ซึ่ง Programmer คนอื่น เขียนมาให้เราแล้ว เราสามารถนำ Blueprint มาสร้างเป็น Instance ของเราเพื่อใช้งานได้เลย โดยที่ไม่จำเป็นต้องเข้าใจว่าเบื้องหลัง FastAPI จะทำงานอย่างไร (แต่ใครที่อยากอ่านว่าข้างใน FastAPI จริง ๆ แล้วทำงานอย่างไร สามารถกด Ctrl + Left Click ที่ชื่อ FastAPI() ดูได้นะ)
คิดว่าถ้าได้เรียนวิชา Object Oriented Programming น่าจะเข้าใจสิ่งที่เรากำลังทำมากขึ้นนะ
Endpoint Declaration
API Endpoint คือ จุดสิ้นสุดของ API (?) ... เอาเป็นว่า มันคือ Method + Path ที่เฉพาะเจาะจง เพื่อ "ทำอะไรบางอย่าง" กับ Resource ที่เรามี เช่น
Endpoint
GET /productsหมายถึง หากมีการเรียกใช้งาน (Method + Path ที่ถูกต้อง) แล้ว จะมีการคืนค่าสินค้าทั้งหมดที่มีในระบบEndpoint
GET /products/{barcode_no}หมายถึง หากมีการเรียกใช้งานและใส่ Path Variable เป็นหมายเลขบาร์โค้ดของสินค้า จะคืนข้อมูลรายละเอียดของสินค้านั้น ๆEndpoint
POST /productsหมายถึง หากมีการเรียกใช้งานและส่ง Request Body เป็นรายละเอียดของสินค้า ตาม Model ที่ Programmer ระบุ จะเพิ่มสินค้าใหม่ลงในระบบ
อย่างไรก็ตาม เมื่อเราประกาศ Endpoint หนึ่ง ๆ ขึ้นมา เช่น Endpoint ที่ใช้ในการเรียกดูสินค้าทั้งหมดในระบบ (GET /products) เราซึ่งเป็น Programmer ก็ต้องมาระบุเองอยู่ดีว่า การเรียกดูข้อมูลสินค้าในระบบทั้งหมดต้องทำอย่างไร
จากตัวอย่างที่เราทำไปเมื่อสักครู่ @app.get("/") คือ Decorator ที่บอกว่าเรากำลังจะสร้าง Endpoint สำหรับ Method GET ที่ path / หรือ Root path จากนั้น ฟังก์ชัน read_root() จะทำหน้าที่ในการประมวลผลและคืนค่า (Handle) Request นั้น ๆ
จากในตัวอย่าง ฟังก์ชัน read_root() จะทำหน้าที่เพียงคืนค่า {"Hello": "World"} กลับไป แต่พอเข้าใจไอเดียแล้วใช่มั้ยครับ ว่าเราจะทำอะไรใน ฟังก์ชัน read_root() ก็ได้ เพื่อให้มันส่งค่าอะไรกลับคืนไปก็ได้
แบบฝึกหัด !!
ไหนลองทำให้เวลาเราทำ GET Request ที่ path / แล้วมันคืนค่าเป็นจำนวนครั้งที่มีการเรียก Request มาที่ Path นี้หน่อยซิ เช่น
ครั้งที่ 1 ->
{"Hello World": 1}ครั้งที่ 2 ->
{"Hello World": 2}ครั้งที่ 3 ->
{"Hello World": 3}
Hint:
เราต้องแก้ไขฟังก์ชัน
read_root()ให้เปลี่ยนค่าที่ Return กลับมาเราจะรู้ได้ยังไงนะว่ามีการ Request มาที่ path
/กี่ครั้งแล้ว
Path Variable ✈️
Concept
จากที่เราได้ยกตัวอย่างไปข้างต้น บางครั้งเวลาเรา Request เราก็อยากใส่ตัวแปรบางอย่างเข้าไปใน Request ด้วย เช่น GET /products/{barcode_no} ซึ่งเป็นการแสดงข้อมูลสินค้าจากหมายเลขบาร์โค้ด แน่นอนว่าเราต้องใส่ตัวแปรเป็นหมายเลขบาร์โค้ดของสินค้า เพื่อให้ระบบสามารถหาข้อมูลสินค้านั้น ๆ และ Return ข้อมูลได้อย่างถูกต้อง
เราจะยังไม่พูด Logic ในการหาข้อมูลสินค้าจากหมายเลขบาร์โค้ดในหัวข้อนี้ เนื่องจากเป็นเรื่องที่ทุกคนต้องไปศึกษาต่อด้วยตนเอง แต่เราจะพูดในมุมมองของการนำ Path Variable มาใช้ในฟังก์ชันของเรา เพื่อประมวลผลอะไรบางอย่างต่อไป
วันนี้ เราจะสร้าง Endpoint GET /greet/{name} โดยจะมี Response เป็น {"Hello": <name>} โดย <name> เป็น Variable ที่ผู้ใช้ป้อนเข้ามาผ่าน Path Variable เริ่มโดยการนำ Code ต่อไปนี้ วางต่อจาก Endpoint เมื่อกี้ได้เลย จะเป็นการเพิ่ม Endpoint มาอีกหนึ่ง Endpoint
สังเกตจาก Decorator ว่าเรามีการใส่ {name} ต่อท้าย /greet/ เพื่อระบุว่า เราจะรับค่าทั้งหมดที่อยู่หลังจาก /greet/ เป็น Path Variable ชื่อ name เช่น
GET /greet/Oak-> name = 'Oak'GET /greet/1234-> name = '1234'GET /greet/YoYo-> name = 'YoYo'
จากนั้น ในขั้นตอนของการประกาศฟังก์ชัน เราสามารถรับ name เป็น Parameter ของฟังก์ชันได้ จากนั้น เราจะเอาตัวแปรนั้นมาใช้ทำอะไรในฟังก์ชันของเรา มันก็เป็นเรื่องของเราแล้ว !
Application
สำหรับการใช้งานจริง Endpoint GET /products/{barcode_no} จะมีการนำ barcode_no ซึ่งเป็น Path Variable ไป Query หาข้อมูลจาก Database (หรืออะไรก็ตาม) เช่นตามตัวอย่าง จะเก็บข้อมูล Product ไว้ใน List และเมื่อมีการ Request GET /products/{barcode_no} จะทำ barcode_no ไปค้นหาสินค้าที่มี barcode_no ตรงกัน มาเป็น Response
Logic ที่สำคัญ อยู่บริเวณบรรทัดที่ 16 - 22
โดย Lab ที่จะให้ทดลองทำในหัวข้อนี้ เราจะเก็บข้อมูลต่าง ๆ โดยใช้ List เพื่อความง่ายในการพัฒนา (ไม่งั้นเราต้องอธิบายเรื่อง Database กันอีก) อย่างไรก็ตาม อย่าลืมว่าตัวแปรทั้งหมดจะหายไปหลังจากเรา Restart Web Server ของเรา ดังนั้นก็ระวังกันด้วยนะ
Request Body 📃
ต่อมา เราจะมาพูดถึง Request Body กัน ยกตัวอย่าง เราต้องการสร้าง Endpoint POST /products เพื่อเพิ่มสินค้าใหม่เข้าไปในระบบ โดยต้องการโครงสร้าง Request Body ดังนี้
เราสามารถกำหนดโครงสร้างของ Request Body ได้ โดยใช้ Pydantic BaseModelเริ่มต้นจากการ Import กันก่อน
จากนั้น สร้าง Model ข้อมูลขึ้นมา ในที่นี้จะใช้ชื่อ ProductDTO (Data Transfer Object) โดยสร้างเป็น Class ที่ Inherit มาจาก BaseModel (อ่านเพิ่มเติมได้ที่นี่)
ต่อมา สร้าง Endpoint ใหม่ของพวกเรา โดยใช้ @app.post(<path>) เพื่อ Handle POST Request และให้ ProductDTO เป็น Parameter ของฟังก์ชัน แล้วจึงเขียน Logic ในการเพิ่มสินค้าใหม่เข้าไปใน List สุดท้ายจึง Response เป็นข้อมูลของสินค้าที่เพิ่มเข้าไป
เสร็จแล้ววว ! เรามาลองส่ง Request กันดีกว่า อย่าลืมใส่ Request Body ตามที่เรากำหนดด้วยนะ

สังเกตว่าได้ Status Code เป็น 200 OK และ Response Body เป็นรายละเอียดของสินค้าที่เราเพิ่งเพิ่มไปตามที่เราเขียน Code เลย คราวนี้เรามาลองใช้ Endpoint GET /products/{barcode_no} เพื่อดูข้อมูลสินค้าที่เราเพิ่งเพิ่มกันไปดีกว่า โดยใช้หมายเลขบาร์โค้ดของสินค้าที่เราเพิ่งเพิ่มไปเป็น Path Parameter

Data Validation
ในการทำงานจริง ควรมีการ Validate ข้อมูลจากผู้ใช้ก่อนทุกครั้ง เช่น ตรวจสอบว่าหมายเลขบาร์โค้ดซ้ำกับหมายเลขบาร์โค้ดที่มีอยู่แล้วในระบบหรือไม่ หากผู้ใช้พยายามเพิ่มสินค้าเดียวกันมากกว่าหนึ่งครั้ง ก็ไม่ควรให้ผู้ใช้ทำได้
ลองทำดู !!
เขียนโปรแกรม โดยเพิ่ม Data Validation ต่อไปนี้ในฟังก์ชัน add_new_product
ตรวจสอบว่าหมายเลขบาร์โค้ดที่ผู้ใช้ใส่มา ซ้ำกับสินค้าที่มีในระบบแล้วหรือไม่
ตรวจสอบว่าหมายเลขบาร์โค้ดเป็นตัวเลขที่มี 13 หลักหรือไม่
ตรวจสอบว่าราคาสินค้า ต้องเป็นจำนวนจริงบวกหรือศูนย์เท่านั้น
SwaggerUI 📚
ที่ผ่านมา เราใช้โปรแกรม Postman ในการทดสอบ API ของเรา อย่างไรก็ตาม ใน FastAPI ได้มี Built-in SwaggerUI ซึ่งเป็น Graphic Interface สำหรับ Document และ ทดสอบ API ได้ ซึ่งจะสร้างความสะดวกในการพัฒนาให้กับทั้ง Frontend และ Backend Developer
สามารถเข้าสู่หน้า SwaggerUI ได้ผ่าน GET /docs เพื่อความสะดวก สามารถเปิด http://localhost:8000/docs ใน Browser ได้เลย

SwaggerUI จะบอกตั้งแต่ API ของเรามี Endpoint ใดบ้าง แต่ละ Endpoint มีรูปแบบ Request Body อย่างไร และแต่ละ Endpoint มีรูปแบบ Response อย่างไร ทำให้ Frontend Dev ไม่ต้องวิ่งไปถาม Backend Dev บ่อย ๆ นั่นเอง
Security Issue
หลังจากพัฒนา API เสร็จสิ้น และต้องมีการ Deploy on Production ควรปิด SwaggerUI ก่อนทุกครั้ง เนื่องจากหากปล่อยให้ใครก็ได้สามารถเข้าถึงหน้า SwaggerUI ของเราได้ คนคนนั้นก็จะรู้โครงสร้างทั้งหมดของ API เรา และอาจจะซนได้นั่นเอง
Conclusion 🔎
สรุปแล้ว เราได้เรียนรู้วิธีการสร้าง API Endpoint แต่ละ Method เพื่อจัดการกับ Resource ไม่ว่าจะเป็น Endpoint ที่มีและไม่มี Path Variable รวมถึง Endpoint ที่ต้องมีการรับ Request Body นอกจากนั้นยังเรียนรู้การทำ Data Validation สามารถสรุป Code ที่เราเขียนถึงปัจจุบันได้ดังนี้
ต่อไป เราจะนำความรู้เหล่านี้มารวม ๆ กัน แล้วสร้างเป็น Application ของเรากัน ซึ่งแน่นอนว่ามันจะมีมากกว่า 1 Object และ Object เหล่านั้นจะมีความสัมพันธ์บางอย่างต่อกัน (สำหรับรายละเอียดเพิ่มเติมจะได้เรียนในวิชา OOP)
Last updated
Was this helpful?