ถ้าพูดถึง Web Framework ที่พัฒนาด้วยภาษา Python ทุกคนอาจจะนึกถึง Django, Flask วันนี้ผมมาแนะนำอีกหนึ่งตัวที่เป็นหนึ่งใน Web Framework ที่พัฒนาด้วย Python เหมือนกันที่ชื่อว่า FastAPI ซึ่งเป็นอีกหนึ่งตัวที่น่าสนใจในการนำมาสร้าง API โดยตัวนี้ออกแบบมาให้ง่ายต่อการพัฒนาเป็นอย่างมากเราสามารถที่จะสร้าง API ขึ้นมาได้อย่างรวดเร็ว เข้าใจได้ง่ายและถ้าพูดถึงความเร็วในการใช้งานเชิงประสิทธิภาพนั้นเรียกได้ว่าเร็วเทียบเท่าตัว NodeJS และ Go เลยซึ่งในบทความนี้ผมก็จะพามาสร้าง API โดยใช้ FastaAPI กันซึ่งอาจจะเป็นแค่เบื้องต้นเพราะจริงๆแล้วมันสามารถทำอะไรได้เยอะมาก ๆ ทำไม่หมดในบทความเดียวแน่นอน
แนะนำ: คอร์สเรียน Full Stack Developer 2024 (Mini bootcamp)(เนื้อหาหลักหมื่นในราคาหลักพัน คุ้มค่าและจัดเต็มแน่นอนครับ แนะนำ) โดย Back-end ใช้ Python ส่วน Front-end ใช้ Vue.js พร้อมด้วย Docker และ deployment แบบจัดเต็ม
1. เพื่อให้เข้าใจการใช้งาน FastAPI เบื้องต้น
2. เพื่อเพิ่มความรู้ความเข้าใจเกี่ยวกับการสร้าง API ได้มากขึ้น
3. เพื่อให้สามารถพัฒนาโปรเจคท์ได้อย่างรวดเร็วมากยิ่งขึ้น
เพื่อให้ง่ายต่อการติดตั้งและใช้งาน package ต่าง ๆ ผมจะให้ทุกคนสร้าง Virtual Environment มาเพื่อรองรับ packages ต่าง ๆ ที่เราจะติดตั้งต่อจากนี้โดยให้เราสร้างไฟล์ขึ้นมาก่อนไฟล์หนึ่งอาจตั้งชื่อว่า myWork ก็ได้ครับแล้วก็สร้าง Virtual Environment ในนี้ได้เลยด้วยคำสั่ง
# บรรทัดแรกสำหรับคนที่ยังไม่ได้ติดตั้ง virtualenv ในเครื่องนะครับ
$ python -m pip install virtualenv
$ python -m venv env
หลังจากนั้นก็สั่ง Activate สำหรับคำสั่งก็ต่างกันไปตาม OS ตามนี้เลย
# Linux and macOS
source env/bin/activate
# Windows
.\env\Scripts\activate
เสร็จแล้วสำหรับการสร้าง Visual Environment ต่อจากนี้เรามาติดตั้ง package ที่เกี่ยวข้องกับ FastAPI กันดีกว่าครับโดยเริ่มต้นแล้วจะมีอยู่สองตัวที่เราจำเป็นต้องติดตั้งเลยคือ
1. FastAPI ซึ่งแน่นอนเป็นตัวนี้คือพระเอกของบทความนี้เพราะเราจะมาสร้าง API โดยเฟรมเวิร์คนี้
(env) pip install fastapi
2. Uvicorn จะเป็นอีกหนึ่งตัวที่สำคัญเหมือนกันในการเป็น Server ที่รันไฟล์ python ที่เราสร้างขึ้นมาโดยเขียนด้วย FastAPI Framework
(env) pip install uvicorn[standard]
เมื่อติดตั้งเสร็จเรียบร้อยแล้วเราสร้างไฟล์สำหรับไว้ใช้เป็นไฟล์หลักกันเลยดีกว่าซึ่งไฟล์ชื่อว่า main.py โดยตอนนี้หน้าตาของโครงสร้างโฟลเดอร์เราก็จะประมาณนี้
|-- myWork
| |-- env # Visual Environment
| |-- main.py # ไฟล์งานหลักที่พึ่งสร้างขึ้นมาใหม่
หลังจากสร้างไฟล์เรียบร้อยแล้วลองดูว่าตอนนี้โฟลเดอร์ของเรามีโครงสร้างตามด้านบนหรือเปล่าถ้าใช้ก็ให้ไปที่ไฟล์ main.py เพิ่ม code ลงไปนิดหน่อยเดี๋ยวเราจะทดลองรันกันโดยเพิ่ม code ตามนี้เลย
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
uvicorn main:app --reload
สามารถเข้าไปดูหน้าเว็บได้ที่ http://127.0.0.1:8000 ก็จะเห็นหนาเว็บประมาทนี้
ซึ่งจะเห็นว่าหน้าเว็บก็จะแสดงตัว JSON ที่เรา return ออกไป เท่านี้เราก็สร้าง API เสร็จแล้วนะครับ 1 ตัวซึ่งไวและง่ายมาก ๆ ไม่ต้องทำอะไรเยอะเลยแต่เท่านี้ยังไม่พอนะครับความสามารถที่ช่วยให้เราพัฒนาได้อย่างรวดเร็วและง่ายยังมีอีกเยอะอย่างเช่นเรื่องที่ผมจะพูดถัดไปนี้เช่นกัน
อีกหนึ่งความสามารถที่ทำให้ตัว FastAPI นั้นถูกพูดถึงเป็นอย่างมากก็คือเมื่อเราสร้าง API ของเราขึ้นมาเสร็จแล้วทาง FastAPI นั้นจะสร้างตัว Document ของ API ที่เราสร้างขึ้นมาทันทีไม่ต้องลงอะไรเพิ่มเติมโดยสร้างมาให้ถึง 2 Documents มีอะไรบ้างไปดูกันเลยครับ
2.1 Interactive API docs
ตัวนี้เรียกก็gป็น Document ที่นำ Swagger UI เข้ามาจัดการให้ซึ่งความสามารถมันนั้นไม่ได้เป็นแค่ Docs ที่เอาไว้ดูธรรมดามันยังสามารถทดลองยิง API ได้อีกด้วยทำได้ทุก HTTP method และยังมีความสามารถอื่น ๆ อีก เช่น ใส่ Header, ใส่ Parameters และง่ายต่อการทำงานร่วมกันผู้อื่นอีกด้วยเพราะเราก็สามารถแชร์ API Docs นี้ไปให้กับทีมพัฒนาทีมอื่นได้เลยไม่ต้องเสียเวลามาสร้างขึ้นใหม่เองโดยจะอยู่ที่ลิ้งค์นี้ http://127.0.0.1:8000/docs
2.2 Alternative API docs
สำหรับ Document นี้จะนำตัวของ ReDoc เข้ามาจัดการช่วยให้ได้หน้าตาของ Docs ที่สวยงามและง่ายต่อการมองและเข้าใจโครงสร้างของ API ที่เราสร้างขึ้นในส่วนของ Docs นี้ไม่สามารถทดลองยิง API ได้เอาไว้ดูเฉย ๆ ซึ่งก็สามารถเลือกใช้ได้ตามความเหมาะสมหรือตามความถนัดของผู้พัฒนาว่าจะใช้ Docs ตัวไหนซึ่งทาง FastAPI ก็ได้เตรียมไว้ให้ทั้งหมดเลยไม่จำเป็นต้องติดตั้งอะไรเพิ่มเติม http://127.0.0.1:8000/redoc
มาถึงช่วงหลักของเราคือการสร้าง API ซึ่งในตัว FastAPI นั้นสามารถสร้าง API ได้ง่ายมาก ๆ และยังมีความเร็วค่อนข้างสูงมากเลยทีเดี่ยวโดยผมจะจำลอง Database มาตัวหนึ่งซึ่งเป็น List นั้นละในบทความนี้ยังไม่ได้เชื่อมต่อ Database ใด ๆ ซึ่ง List นี้เก็บข้อมูลของ Book โดยแต่ละเล่มจะมีข้อมูลอยู่สองอย่างคือ title และ price แล้วผมจะสร้างให้มันอยู่ใน main.py ตามนี้เลยครับ
from fastapi import FastAPI
app = FastAPI()
# New
book_db = [
{
"title":"The C Programming",
"price": 720
},
{
"title":"Learn Python the Hard Way",
"price": 870
},
{
"title":"JavaScript: The Definitive Guide",
"price": 1369
},
{
"title":"Python for Data Analysis",
"price": 1394
},
{
"title":"Clean Code",
"price": 1500
},
]
@app.get("/")
async def root():
return {"message": "Hello World"}
3.1 GET Method
มาสู่ API แรกที่เราจะสร้างกันซึ่งเป็น API ที่เอาไว้ Return ข้อมูลของ book ออกมาจาก data ที่เราได้จำลองไว้โดยเราก็จะใช้ GET Method สำหรับ API ที่เอาไว้ดึงข้อมูลออกมาจากฐานข้อมูลที่เราเก็บไว้โดยเราก็จะเขียนเอาไว้ใน main.py เหมือนเดิมโดยสามารถเขียนได้ตามนี้เลย
@app.get("/book/")
async def get_books():
return book_db
เมื่อไปดูใน http://127.0.0.1:8000/docs/ ก็จะเห็น API ใหม่โผล่ขึ้นมาตามที่เราได้สร้างและเราสามารถทดลองยิง API ได้ด้วยโดยกดไปที่ Try it out และไปที่ Execute ตามนี้
ตัว API เราก็จะ Return ข้อมูลกลับมาให้เราตามที่เราได้เขียนไว้นะครับ
เมื่อเรามี API สำหรับการดึงข้อมูลทั้งหมดมาใช้งานแล้วเราก็จะขาดไม่ได้สำหรับ API ที่ใช้ในการดึงข้อมูลที่ละตัวโดยอิงตาม ID ซึ่งเราก็จะใช้ GET Method ในการดำเนินการเหมือนกันโดยเราจะใส่ book_id เป็น Parameter ผ่านทาง Path ที่เราสร้างขึ้นดังนี้
@app.get("/book/{book_id}")
async def get_book(book_id: int):
return book_db[book_id-1]
จะเห็นว่า book_id ถูกรับมาทาง Path ของเราและเราก็ได้เขียน Function เพื่อลองรับ book_id นี้ไว้ด้วยแล้วนำมาอ้างอิงถึงตำแหน่งตามไอดีนั้นๆ ที่ Client ต้องการเรียกใช้ถามว่าทำไมถึง -1 ก็เพราะว่า Array นั้นตำแหน่งแรกคือ 0 ถ้า Client ต้องการข้อมูลตำแหน่งคือ 0 นั้นเองแต่ Client น่าจะไม่เข้าใจถ้าเราจะไปเอาค่าตำแหน่งที่ 0 มาให้เขาเรียกใช้งานเลยให้เขานั้นเรียกใช้งานตามปกติเราแค่เอาตัวเลขนั้นมาลบออกก็จะได้ตำแหน่งที่เขาต้องการโดยเราสามารถทดลอง API นี้ได้ที่ http://127.0.0.1:8000/docs/ โดยสามารถใส่ตำแหน่งที่เราต้องการเรียกใช้ผ่านทาง Parameters
3.2 POST Method
อีกหนึ่ง Method ที่ขาดไม่ได้ในการเพิ่มข้อมูลลงใน Database ก็คือ Post Method นั้นเองโดยทำให้เราสามารถที่จะรับข้อมูลจากฝั่ง Client ได้โดยการที่จะรับข้อมูลมาได้นั้นเราต้องมี Model ไว้รองรับก่อนเพื่อให้ Client นั้นรู้ว่าทางฝั่ง Server นั้นต้องการข้อมูลอะไรไปเก็บลงใน Database ทำให้ง่ายต่อการพัฒนาและสะดวกกับทางฝั่ง Client ด้วยสามารถดูที่ Doc ได้เลยสะดวกมากๆแถมง่ายอีกด้วยอย่างแรกเลยมาสร้าง Model กันดีกว่าใส่เข้าไปใน main.py เหมือนเดิมครับ
@from pydantic import BaseModel # New import
...
# Model
class Book(BaseModel):
title: str
price: float
...
#create new book
@app.post("/book")
async def create_book(book: Book):
book_db.append(book.dict())
return book_db[-1]
จะเห็นว่าผม Return book_db[-1] ทำไมต้องตำแหน่ง -1 คือสิ่งที่ผมต้องการที่จะแสดงข้อมูลล่าสุดที่ Client ได้เพิ่มเข้าไปซึ่งการเรียกตำแหน่ง -1 ใน Array มันคือตำแหน่งสุดท้ายของ Array นั้นเองทำให้มันจะ Return book เล่มล่าสุดที่เราเพิ่มเข้าไปโดยสามารถทดสอบได้ดังนี้
Note: โดยข้อมูลที่เพิ่มไปนั้นเมื่อเรา Refresh หน้าก็จะหายไปไม่ได้ถูกบันทึกไว้เพราะเรายังไม่ได้เชื่อมต่อกับ Database
File Upload
สำหรับ FastAPI นั้นก็สามารถที่ช่วยในเรื่องของการอัพโหลดไฟล์ได้อย่างง่ายดายโดยเราก็จะใช้ POST Method นี้ละครับในการอัพโหลดไฟล์ผ่าน API โดยอย่างแรกที่ต้องทำเลยคือติดตั้ง package เพิ่มหนึ่งตัวที่มีชื่อว่า python-multipart โดยสามาถใช้คำสั่งติดตั้งได้ตามนี้เลย
(env) pip install python-multipart
เพียงแค่ติดตั้งก็สามารถใช้งานได้แล้วครับแล้วก็ต้องมีการ Import File, UploadFile จาก FastAPI มาเพิ่มจากนั้นก็สามารถสร้าง API รองรับการอัพโหลดไฟล์ได้แล้วครับสามารถเพิ่มเข้าใน main.py ได้เลย
from fastapi import FastAPI, File, UploadFile
...
# upload single file
@app.post("/img")
async def up_img_book(file: UploadFile = File(...)):
size = await file.read()
return { "File Name": file.filename, "size": len(size)}
จะเห็นว่า Function ที่เราสร้างขึ้นมาได้เขียนรับ File ที่มาจาก Client ไว้ตามด้านบนและได้นำไฟล์มาอ่านโดยใช้ความสามารถ Asynchronous ได้จะเห็นว่า FastAPI นั้นสามารถที่จะสร้าง Asynchronous Function ได้ด้วยหลังจากที่เราได้สร้างมาหลายๆ Function เราจะเห็นว่ามี async นำหน้าทุก Function ที่เราสร้างขึ้นทุก ๆ Function ก็คือ Asynchronous Function ทั้งหมดเลยโดยโค้ดด้านบนผมได้เขียนไว้ว่าให้ Return ชื่อไฟล์กับขนาดของไฟล์เรามาดูผลลัพธ์กันเลยดีกว่า
Multiple file uploads
หลังจากที่ได้สร้าง API รองรับ File upload ได้แล้วแต่ก็ทำได้เพียงแค่ 1 ไฟล์เท่านั้นแล้วถ้าเราต้องการที่จะอัพโหลดไฟล์หลายๆ ไฟล์ในครั้งเดียวละต้องเขียนแบบไหนซึ่งก็ไม่ยากอย่างที่คิดโดยที่ผมจะ Return ชื่อและขนาดของทุกไฟล์ที่เราอัพโหลดขึ้นไปออกมาเป็น Array ก็คือ List ในภาษาของ Python ดังนั้นแล้วอย่าลืม import List มาใช้ด้วยละงั้นเรามาลองเขียนโค้ดเพิ่มกันเลยดีกว่า
...
from typing import List
...
@app.post("/multi-img")
async def up_multi_file(files: List[UploadFile] = File(...)):
file = [
{
"File Name":file.filename,
"Size":len(await file.read())
} for file in files]
return file
ซึ่งถ้าไปทดลอง API ก็จะได้ผลลัพธ์ตามนี้เลยครับ
มาถึงอีกหนึ่ง Method ที่เกี่ยวกับการแก้ไขข้อมูลใน Database โดยเราจะให้ Client นั้นอ้างอิงถึงตำแหน่งที่ต้องการแก้ไขโดยใช้ id ในที่นี้ก็คือตำแหน่งใน Array นั้นละสามารถเขียนได้ในนี้เลยครับ
@app.put("/book/{book_id}")
async def edit_book(book_id: int, book: Book):
result = book.dict()
book_db[book_id-1].update(result)
return result
จะเห็นว่า Function มีการดึง Book Model มาใช้งานเพื่อให้ Client สามารถที่จะรู้ได้ว่า API นี้ต้องการข้อมูลอะไรบ้างคล้าย ๆ กับตอนทำ POST Method โดยเรามาทดลองแก้ไขข้อมูลกันดีกว่าครับในที่นี้ผมจะแก้ไขข้อมูลตัวแรกเปลี่ยนเป็นชื่อหนังสือ FastAPI ราคา 1899 ตามนี้เลย
3.4 Delete Method
มาถึง Method ตัวสุดท้ายกันแล้วนะครับในส่วนตัวนี้ไม่มีไรยากซึ่งจะเป็น Method ที่เราใช้ในการสื่อสารกับ Server ว่าต้องการที่จะลบข้อมูลตามชื่อมันละครับ Delete ในส่วนของข้อมูลของเราตอนนี้เป็น Array เราก็สามารถที่จะเขียน pop ข้อมูลออกมาได้เลยโดยสามารถเขียนได้ตามนี้เลยครับ
@app.delete("/book/{book_id}")
async def delete_book(book_id: int):
book = book_db.pop(book_id-1)
return book
มาทดลองยิง API เพื่อลบข้อมูลตำแหน่งแรกของ Array กันดีกว่าครับ
โดยถ้าเรายิง API เพื่อดูข้อมูลของ Array ทั้งหมดจะเห็นว่าข้อมูลของหนังสือเล่มแรกนั้นหายไปแล้ว
จะเห็นว่าตอนนี้โค้ดของเรานั้นเต็มหน้า main.py เป็นที่เรียบร้อยแล้วนี้แค่ API ที่เกี่ยวกับข้อมูล Books อย่างเดี่ยวนะครับถ้าเกิดว่าเรามี API ตัวใหม่เข้ามาจัดการเกี่ยวกับ User หน้า main.py ของเราจะดูสับสนขนาดไหนแล้วตอนนี้ Docs เราก็ยังไม่ได้ถูกจับแยก Tag ของตัวเองด้วยเช่น Book ก็อยู่ใน Book Tag ส่วนของ User ก็ไปอยู่ใน User Tag มันจะสามารถทำให้ดูได้ง่ายขึ้นทุกอย่างที่พูดมานี้เราจะใช้ Router ในการจัดการมันทั้งหมดโดยก่อนที่เราจะไปสร้าง Router ผมอยากให้ทุกคนลองสร้าง User API ง่าย ๆ ขึ้นมาก่อนนะครับเพื่อให้ดูถึงความรกของโค้ดในหน้า main.py โดยสามารถเขียนได้ตามนี้เลยครับ
...
user_db = [
{
"name": "Alice",
"age": 22
},
{
"name": "BOB",
"age": 24
},
{
"name": "John",
"age": 27
}
]
class User(BaseModel):
name: str
age: int
@app.get("/user/")
async def get_users():
return user_db
@app.get("/user/{user_id}")
async def get_user(user_id: int):
return user_db[user_id-1]
@app.post("/user/")
async def create_user(user: User):
result = user.dict()
user_db.append(result)
return user_db[-1]
@app.put("/users/{user_id}")
async def edit_user(user_id: int, user: User):
result = user.dict()
user_db[user_id-1].update(result)
return result
@app.delete("/user/{user_id}")
async def edit_user(user_id: int):
user = user_db.pop(user_id-1)
return user
ตอนนี้หน้า main.py คงเต็มไปด้วยโค้ดที่ตีกันไปตีกันมาทำให้รกหน้า main.py มาก ๆ เดี๋ยวเราจะจับแยกมันออกมาแต่ตอนนี้เรามาดู API ที่สร้างขึ้นใหม่กันดีกว่าครับ
ทุกคนจะเห็นว่าทั้ง API นั้นอยู่รวมกันที่เดี่ยวทั้งหมดก็คือ Default Tag ซึ่งเมื่อดูแล้วจะเห็นเลยว่ามันค่อนข้างจะดูยากว่า API ส่วนไหนเป็นส่วนไหนอาจจะทำให้การทำงานของเรานั้นลำบากมากยิ่งขึ้นเพื่อการนี้ทำให้ต้องเขียนแยกกันเป็นส่วน ๆ กันชัดเจนแล้วใช้ Router เป็นตัวรวมทุก ๆ API เข้ามาอยู่ในด้วยกันพร้อมติด Tag ให้แต่ละ API เพื่อให้ง่ายต่อการดู Document มาเริ่มจากการจัดการไฟล์กันเลยดีกว่าโดยเราจะมีโฟลเดอร์ app เพื่อเก็บงานของเราทั้งหมดและสร้างโฟลเดอร์ Router เพื่อมาเก็บ API แบ่งแยกตามประเภทของมันโดยสามารถสร้างได้ตามโครงสร้างนี้เลย
|-- myWork
| |-- env # Visual Environment
| |-- app # โฟลเดอร์ที่เอาไว้เก็บงานของเราไว้ทั้งหมด
| |-- main.py # ตัวไฟล์งานหลักของเรา
| |-- routers # โฟลเดอร์ที่เอาไว้เก็บ API
| |-- user.py # ไฟล์ของ User API
| |-- book.py # ไฟล์ของ Book API
| |-- file.py # ไฟล์ของ Upload File API
เราจะทำการแยกโค้ดจากตัว main.py ที่เรายัดไว้เต็มไปหมดให้ไปอยู่ใน book.py , และ file.py โดยนำ router เข้าไปช่วยจัดการเราเริ่มกันที่ book.py กันเลยดีกว่าครับ
book.py
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter(
prefix="/book",
tags=["Book"],
responses={404: {"message": "Not found"}}
)
book_db = [
{
"title":"The C Programming",
"price": 720
},
{
"title":"Learn Python the Hard Way",
"price": 870
},
{
"title":"JavaScript: The Definitive Guide",
"price": 1369
},
{
"title":"Python for Data Analysis",
"price": 1394
},
{
"title":"Clean Code",
"price": 1500
},
]
class Book(BaseModel):
title: str
price: float
@router.get("/")
async def get_books():
return book_db
@router.get("/{book_id}")
async def get_book(book_id: int):
return book_db[book_id-1]
@router.post("")
async def create_book(book: Book):
book_db.append(book.dict())
return book_db[-1]
@router.put("/{book_id}")
async def edit_book(book_id: int, book: Book):
result = book.dict()
book_db[book_id-1].update(result)
return result
@router.delete("/{book_id}")
async def delete_book(book_id: int):
book = book_db.pop(book_id-1)
return book
จะเห็นทุก ๆ อย่างนั้นคล้าย ๆ กันที่เราเขียนตรง main.py เลยแต่จะมี router เพิ่มขึ้นมาจากเดิมเราจะใช้ app เราเปลี่ยนมาใช้ router แทนโดยข้างใน router เราสามารถกำหนดได้หลาย ๆ อย่างแต่ข้างบนผมกำหนดให้ดูแค่ 3 อย่างก็คือ prefix, tags, responses เรามาอธิบายทีละตัวกันดีกว่าครับ
Prefix คือก็ตามชื่อมันเลยครับเมื่อเรียกใช้ Router นี้ที่เก็บ Book API ไว้มันจะเริ่มต้นชื่อ Link ตามที่เรากำหนดไว้อย่างด้านบนนั้นผมกำหนดให้เริ่มด้วย /book เพราะฉะนั้นเวลาเราใส่ Link ใน API เราก็ไม่ต้องมานั่งเพิ่ม /book ที่ละตัวแล้วกำหนดใน APIRouter ได้เลย
Tag เพื่อให้แบ่ง API กันเป็นประเภทเราก็จะติด Tag ให้มันเพื่อบอกว่ามันคือ API อะไรและสามารถแสดงใน Document ได้อีกด้วย
Responses เมื่อ Client ร้องขอข้อมูลจาก API มาผิดจากที่เรากำหนดไว้ก็สามารถที่จะต้องข้อความที่ตอบกลับเขาได้เวลาเกิดเหตุการณ์ Error
หลังจากนั้นเราก็ทำคล้าย ๆ กันกับ book.py และ file.py
user.py
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter(
prefix="/user",
tags=["User"],
responses={404: {"message": "Not found"}}
)
user_db = [
{
"name": "Alice",
"age": 22
},
{
"name": "BOB",
"age": 24
},
{
"name": "John",
"age": 27
}
]
class User(BaseModel):
name: str
age: int
@router.get("/")
async def get_users():
return user_db
@router.get("/{user_id}")
async def get_user(user_id: int):
return user_db[user_id-1]
@router.post("/")
async def create_user(user: User):
result = user.dict()
user_db.append(result)
return user_db[-1]
@router.put("/{user_id}")
async def edit_user(user_id: int, user: User):
result = user.dict()
user_db[user_id-1].update(result)
return result
@router.delete("/{user_id}")
async def edit_user(user_id: int):
user = user_db.pop(user_id-1)
return user
file.py
from fastapi import File, UploadFile, APIRouter
from typing import List
router = APIRouter(
prefix="/img",
tags=["File"],
responses={404: {"message": "Not found"}}
)
@router.post("/")
async def up_img(file: UploadFile = File(...)):
size = await file.read()
return { "File Name": file.filename, "size": len(size)}
@router.post("/multi")
async def up_multi_file(files: List[UploadFile] = File(...)):
file = [
{
"File Name":file.filename,
"Size":len(await file.read())
} for file in files]
return file
หลังจากนำ API ไปแยกอยู่แต่ละ Router แล้วเราก็นำมารวมกันที่ main.py เพื่อที่จะสามารถทำให้มันใช้งานได้โดยเราจะนำเข้าไปยัง app เหมือนเดิมสามารถทำตามนี้ได้เลย
from fastapi import FastAPI
from pydantic import BaseModel
from .routers import book, user, file
app = FastAPI()
app.include_router(book.router)
app.include_router(user.router)
app.include_router(file.router)
@app.get("/")
async def root():
return {"message": "Hello World"}
เราจะ import ไฟล์ book.py , user.py และ file.py เพื่อที่นำ Router ที่สร้างขึ้นมาใหม่มารวมกันอยู่ใน app เพื่อให้สามารถเรียกใช้ได้แล้วจากนั้นก็ include มันเข้าไปใน app เราลองมาดู document กันดีกว่าครับว่าตอนนี้มันจะเป็นยังไงบ้างโดยเราเปลี่ยนที่อยู่ไฟล์ main.py แล้วเราจำเป็นต้องเข้าไปใน app ก่อนเพื่อที่จะเข้าถึง main ทำให้การรัน server นั้นแตกต่างจากเดิมโดยสามารถพิมคำสั่งรัน server ได้ตามนี้เลย
uvicorn app.main:app --reload
จะเห็นว่า API ของเรานั้นแบ่งตาม Tag ตามที่เราได้แบ่งแยกเอาไว้ซึ่งสามารถที่จะง่ายต่อการดูมากยิ่งขึ้นและดูเป็นระเบียบมากกว่าเดิมมาก เท่านี้ก็สามารถที่จะทำงานได้ง่ายมากยิ่งขึ้นและสะอาดตาไม่รกหูรกตาเหมือนแบบเดิม
หลังจากที่เราสร้าง API เสร็จแล้วเราก็ต้องการให้มันไปเชื่อมต่อกับฝั่ง Client ถูกไหมครับ แต่ถ้าเรานำ API ที่เราสร้างขึ้นไปเชื่อมต่อเลยมันอาจจะเกิด Error เกี่ยวกับการกำหนดการเข้าถึงได้เพื่อความปลอดภัย Browser จะไม่ให้ Domain ที่ไม่ได้รับอนุญาติจากฝั่ง Server เข้าถึง API ได้เพราะฉะนั้นเราต้องกำหนด Domain ที่ต้องการให้เข้าถึง API โดยเราจะกำหนดผ่านทาง CORS ซึ่งก็มีหลายอย่างมากกว่า Domain ให้เราได้ตั้งค่าในการเข้าถึง API โดยสามารถทำได้ไม่ยากเลยใน FastAPI ในที่นี้ผมขอตั้งค่าไว้ใน main.py เลยนะครับสามารถเพิ่มโค้ดได้ตามนี้เลย
...
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost:8080",
"http://localhost:3000",
"https://stackpython.co"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
...
เรามาสรุปกันอีกรอบนะครับเพื่อทวนความเข้าใจอย่างแรกเลยเพื่อที่จะเริ่มสร้าง FastAPI Project เราต้อง install 2 package ก็คือ 1) FastAPI 2) Uvicorn แล้วจุดเด่นของ FastAPI เลยก็คือความเร็วและความง่ายต่อการพัฒนาเพราะเมื่อสร้าง API เสร็จแล้วก็มี Document ให้ใช้แบบอัตโนมัติเลยโดยมีให้ถึง 2 แบบด้วยกัน Interactive docs, Alternative docs ทำให้ง่ายต่อการทดสอบ API มาก ๆ แถมมี UI อีกด้วยทำให้ใช้ง่ายเข้าไปอีกและในการสร้าง API ต่างๆก็ค่อนข้างตรงไปตรงมาไม่ได้ซับซ้อนมากมายและเพื่อให้ง่ายต่อการจัดการ API ต่าง ๆ เราต้องใช้ความสามารถของ Router เข้ามาจัดระเบียบของ API ที่เราสร้างขึ้นและติด Tag แบ่งกลุ่มตาม API ที่เราได้ออกแบบไว้ทำให้ง่ายต่อการดู API ผ่าน Document อีกด้วย ต่อมาเพื่อให้ Browser นั้นอนุญาตให้ Client เข้าถึง API ของเรานั้นจำเป็นต้องตั้งค่า CORS อีกด้วยซึ่งสามารถทำได้ง่ายมาก ๆ ใน FastAPI
Source Code: FastAPI_Stackpython (github.com)
Reference
[ Python Docs ] - FastAPI (tiangolo.com)
กิจกรรมที่กำลังจะมาถึง
ไม่พลาดกิจกรรมเด็ด ๆ ที่น่าสนใจ
Event นี้จะเริ่มขึ้นใน April 25, 2023
รายละเอียดเพิ่มเติม/สมัครเข้าร่วมคอร์สเรียนไพธอนออนไลน์ที่เราได้รวบรวมและได้ย่อยจากประสบการณ์จริงและเพื่อย่นระยะเวลาในการเรียนรู้ ลองผิด ลองถูกด้วยตัวเองมาให้แล้ว เพราะเวลามีค่าเป็นอย่างยิ่ง พร้อมด้วยการซัพพอร์ตอย่างดี