30 Ngày Chinh Phục FastAPI - [Ngày 2]
FastAPI là một framework web mạnh mẽ và dễ sử dụng, đặc biệt khi làm việc với APIs. Trong bài viết này, chúng ta sẽ tìm hiểu về Routing trong FastAPI, cách định nghĩa các đường dẫn (endpoints) và các HTTP methods, giúp bạn dễ dàng xây dựng các API có cấu trúc rõ ràng và dễ bảo trì.
1. Routing trong FastAPI là gì?
Routing là quá trình định nghĩa các đường dẫn (endpoints) trong API và liên kết chúng với các hàm xử lý (route handlers). Khi một client gửi yêu cầu (request) tới server, FastAPI sẽ dựa vào route được định nghĩa để xử lý yêu cầu đó.
Ví dụ, khi client gửi một GET request đến đường dẫn
/
, FastAPI sẽ tìm và thực thi hàm xử lý được định nghĩa cho route này. Tương tự, khi client gửi một POST request đến /items/
, FastAPI sẽ tìm và thực thi hàm xử lý tương ứng với method POST.
2. Định nghĩa Routing trong FastAPI
FastAPI sử dụng decorators để định nghĩa các route cho các HTTP methods. Các decorators này giúp chúng ta chỉ định loại phương thức HTTP (GET, POST, PUT, DELETE) và liên kết chúng với các hàm xử lý tương ứng.
Ví dụ:
from fastapi import FastAPI
app = FastAPI()
# Định nghĩa route cho GET request tại đường dẫn "/"
@app.get("/")
def read_root():
return {"Hello": "World"}
# Định nghĩa route cho POST request tại đường dẫn "/items/"
@app.post("/items/")
async def create_item(item: dict):
return item
- @app.get("/"): Đây là route cho GET request tại đường dẫn
/
. Khi người dùng gửi yêu cầu GET đến đường dẫn này, hàmread_root
sẽ được gọi và trả về một phản hồi với dữ liệu{"Hello": "World"}
. - @app.post("/items/"): Đây là route cho POST request tại đường dẫn
/items/
. Khi người dùng gửi dữ liệu (thường ở dạng JSON) đến đường dẫn này, hàmcreate_item
sẽ nhận và trả về dữ liệu đó.
3. HTTP Methods trong Routing
Mỗi HTTP method (GET, POST, PUT, DELETE) có một mục đích riêng. Chúng được sử dụng để thực hiện các hành động khác nhau trên các tài nguyên (resources) của server.
Các HTTP Methods Phổ Biến:
- GET: Lấy dữ liệu từ server mà không thay đổi trạng thái của server.
- POST: Tạo một tài nguyên mới trên server.
- PUT: Cập nhật tài nguyên hiện có trên server (thay thế toàn bộ tài nguyên).
- DELETE: Xóa tài nguyên trên server.
Ví dụ sử dụng các HTTP methods trong FastAPI:
from fastapi import FastAPI
app = FastAPI()
# GET request
@app.get("/")
def read_root():
return {"Hello": "World"}
# POST request
@app.post("/items/")
async def create_item(item: dict):
return item
# PUT request
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: dict):
return {"item_id": item_id, "item": item}
# DELETE request
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"item_id": item_id}
- GET /: Lấy dữ liệu (trong trường hợp này là
"Hello": "World"
). - POST /items/: Tạo một tài nguyên mới (trong trường hợp này là một item dưới dạng dictionary).
- PUT /items/{item_id}: Cập nhật tài nguyên có
item_id
bằng cách thay thế dữ liệu cũ với dữ liệu mới. - DELETE /items/{item_id}: Xóa tài nguyên có
item_id
từ server.
4. Tại Sao Routing Quan Trọng?
- Quản lý dễ dàng: Các route giúp chúng ta định nghĩa rõ ràng các điểm cuối của API và phương thức HTTP tương ứng. Điều này giúp quản lý API một cách dễ dàng hơn khi ứng dụng trở nên phức tạp.
- Tăng khả năng mở rộng: Với FastAPI, bạn có thể dễ dàng thêm các endpoint mới và sử dụng các HTTP methods khác nhau để thực hiện các hành động tương ứng với các tài nguyên khác nhau.
5. Ví Dụ Cụ Thể về Routing
Dưới đây là một ví dụ cụ thể về cách sử dụng routing trong FastAPI để xây dựng một ứng dụng API cho một cửa hàng bán hàng trực tuyến:
from fastapi import FastAPI
app = FastAPI()
# Lấy thông tin về tất cả sản phẩm
@app.get("/products/")
def get_products():
return {"products": [{"name": "Laptop", "price": 1000}, {"name": "Smartphone", "price": 500}]}
# Tạo mới một sản phẩm
@app.post("/products/")
async def create_product(product: dict):
return {"message": "Product created", "product": product}
# Cập nhật thông tin sản phẩm
@app.put("/products/{product_id}")
async def update_product(product_id: int, product: dict):
return {"message": "Product updated", "product_id": product_id, "product": product}
# Xóa một sản phẩm
@app.delete("/products/{product_id}")
async def delete_product(product_id: int):
return {"message": "Product deleted", "product_id": product_id}
Trong ví dụ trên:
- GET /products/: Lấy danh sách tất cả các sản phẩm.
- POST /products/: Thêm mới một sản phẩm.
- PUT /products/{product_id}: Cập nhật thông tin sản phẩm với
product_id
tương ứng. - DELETE /products/{product_id}: Xóa sản phẩm với
product_id
tương ứng.
6. Tóm Tắt
- Routing trong FastAPI giúp bạn định nghĩa các điểm cuối và xử lý các yêu cầu HTTP từ client.
- Các HTTP methods như GET, POST, PUT, DELETE có các mục đích khác nhau khi tương tác với tài nguyên trên server.
- FastAPI sử dụng decorators để dễ dàng định nghĩa và xử lý các yêu cầu từ client.
7. Thực hành với một mini project
Lý thuyết lùng bùng đầu quá hen, giờ thử làm bài tập nhỏ nhỏ cho đỡ buồn ngủ nha. Bài tập hôm nay sẽ là Xây dựng API quản lý sách.
Mục tiêu:
- Tạo một API cho phép người dùng quản lý một danh sách các sách trong thư viện.
- Cung cấp các chức năng:
- Lấy danh sách tất cả sách (GET).
- Thêm sách mới (POST).
- Cập nhật thông tin sách (PUT).
- Xóa sách (DELETE).
Các bước thực hiện:
- Tạo một ứng dụng FastAPI mới.
- Định nghĩa các route cho các phương thức HTTP:
- GET để lấy danh sách sách.
- POST để thêm sách mới vào danh sách.
- PUT để cập nhật thông tin của sách.
- DELETE để xóa sách khỏi danh sách.
- Sử dụng một danh sách trong bộ nhớ (in-memory) để lưu trữ các sách thay vì sử dụng cơ sở dữ liệu.
Tạo FastAPI
Uh giờ ôn lại bài cũ đi, nhớ đoạn basic nhất khi khởi tạo một fastAPI là gì không? Chưa nhớ thì quay lại bài 1 để coi nha 😒
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
Rồi, để tôi đã đưa lại bài cũ vào đây luôn. Chắc chắn các bạn phải thắc mắc và hỏi tôi là? Ơ cái async
là gì thế, sao ông anh, ông bạn, bà dì của em có thấy xài nó đâu.
Tính của tôi là không thích dùng khái niệm quá khó hiểu nên nói đại đại thế này: Kiểu như giờ muốn làm nhiều việc cùng một lúc ấy, con người tôi vốn khó hiểu mà giờ không có đợi hết việc này rồi làm tới việc khác mà thích ôm cả cục làm cho nhanh. Ví dụ như giờ tôi có 3 việc cần làm:
- Ăn cơm
- Xem ti vi
- Chơi game
Nếu như tôi làm việc theo kiểu bình thường (synchronous) thì phải làm từng cái một:
- Ăn cơm xong thì xem ti vi
- Xem ti vi xong thì chơi game
Còn giờ với async
bạn cứ nghĩ là tôi đa nhiệm đi, tôi làm nhiều việc cùng một lúc mà không phải đợi xong cái này mới tới cái nọ:
- Tôi đi ăn cơm
- Trong khi ăn cơm thì tôi xem ti vi
- Trong khi tôi xem ti vi thì tôi chơi game luôn
=> Túm váy lại là gì? Là tôi đã tiết kiệm thời gian kha khá vì không phải chờ đợi mỗi việc phải xong thì mới được làm việc tiếp theo. Và đương nhiên là vẫn nên nói về 2 mặt, khi nào cần dùng async
và khi nào thì không cần?
Cần:
-
Xử lý các tác vụ tốn thời gian (I/O-bound tasks): Giả sử bạn có một API cần gọi đến một dịch vụ bên ngoài để lấy dữ liệu. Nếu không dùng async, server sẽ phải đợi kết quả trả về từ dịch vụ đó trước khi có thể xử lý yêu cầu tiếp theo. Với async, trong khi đợi kết quả trả về, server có thể xử lý các yêu cầu khác.
-
Xử lý nhiều yêu cầu đồng thời (Concurrency): Nếu ứng dụng của bạn phải xử lý nhiều yêu cầu cùng lúc, async giúp server không bị chặn khi xử lý các yêu cầu dài hạn, giúp tăng khả năng đáp ứng cho nhiều người dùng.
-
Tối ưu hóa hiệu suất với các tác vụ bất đồng bộ: FastAPI được thiết kế để tận dụng tốt asynchronous programming. Nếu bạn kết hợp async với các thư viện hỗ trợ bất đồng bộ như asyncio, httpx (để gửi HTTP requests), hoặc databases (để kết nối cơ sở dữ liệu bất đồng bộ), bạn sẽ thấy hiệu quả rõ rệt về hiệu suất.
Không cần
- Với các tác vụ nhanh và đồng bộ: Nếu bạn chỉ xử lý những tác vụ đơn giản, như trả về dữ liệu tĩnh hoặc thực hiện các phép toán đơn giản, bạn không cần phải sử dụng async vì nó không giúp ích nhiều và sẽ phức tạp hóa mã nguồn.
- Trong trường hợp bạn chỉ đang làm việc với dữ liệu trong bộ nhớ, không cần dùng async.
Uh thôi luyên thuyên lắm rồi, giờ vô làm đi: Đầu tiên là tạo file main.py hen.
GET
from fastapi import FastAPI
app = FastAPI()
book = [
{
"id": 1,
"title": "Bắt trẻ đồng xanh",
"author": "J.D. Salinger",
"year": 1951,
},
{
"id": 2,
"title": "Giết con chim nhại",
"author": "Harper Lee",
"year": 1960,
}
]
# 1. Lấy danh sách tất cả sách (GET request)
@app.get("/books/")
def get_books():
return books
Rồi giờ chạy thử hen, mở terminal và chạy lệnh sau:
uvicorn main:app --reload
Bạn thấy không, nếu giờ mà bấm thẳng dường dẫn http://127.0.0.1:8000 thì làm gì có gì 🤣
=> Nhớ là thêm /books
vào đường dẫn nữa nhé.
Uh, giờ tiếp tục làm các hàm còn lại nào:
POST
Thêm sách mới:
- Gửi một POST request tới
http://127.0.0.1:8000/books/
với body là dữ liệu sách mới. - Ví dụ nhập body :
{ "id": 3, "title": "Số đỏ", "author": "Nguyễn Hồng", "year": 1936 }
Hahaha, giờ thì bạn lại bối rối vì không biết gửi cái body đó lên kiểu gì phải không? 🤣. Tính ra giờ cái bài này dài ghê hén, giờ tôi chỉ bạn 2 cách:
- Học cách sử dụng Postman
- Python Request
- Postman: Thật ra thì tôi khá lười viết hướng dẫn về Postman vì có tỉ tỉ bài hướng dẫn khác rồi, tôi sẽ tham khảo một bài viết trên viblo: POSTMAN - Gọi API chưa bao giờ dễ dàng như vậy . Tôi thấy bạn này chụp màn hình tương đối chi tiết để bạn có thể hiểu và gửi POST rồi này. Ah nếu như không thấy được mấy cái tương tác GET/POST gì ấy thì đóng cái tab giới thiệu ban đầu đi rồi sau đó mở một tab mới là được nhé.
Sau đó đổi sang GET và nhấn SEND sẽ thấy dữ liệu đã được cập nhật:
Bắt đầu thắc mắc là tại sao em cũng bấm và làm y thế mà nó không hiện thị được giống bài này rồi đúng không? 🤣🤣 Ừa, tại tôi chưa hướng dẫn các bạn rằng phải cập nhật lại code ở main.py mà.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
book = [
{
"id": 1,
"title": "Bắt trẻ đồng xanh",
"author": "J.D. Salinger",
"year": 1951,
},
{
"id": 2,
"title": "Giết con chim nhại",
"author": "Harper Lee",
"year": 1960,
}
]
class Book(BaseModel):
title: str
author: str
year: int
# 1. Lấy thông tin sách
@app.get("/books")
def get_book():
return book
# 2. Thêm sách
@app.post("/books")
def add_book(new_book: Book):
new_book_dict = {
"id": len(book) + 1,
"title": new_book.title,
"author": new_book.author,
"year": new_book.year,
}
book.append(new_book_dict)
return new_book_dict
Rồi tiếp tiếp, giờ là Python Request: mình đang dùng python framework mà nhề, nên phải học cách làm việc với API trong python thôi.
- Bước 1: Cài thư viện Request thông qua pip
bash pip install requests
- Bước 2: Tạo đại 1 file tên post_book.py
đi hen, file này chứa nội dung như sau:
import requests
# Địa chỉ API
url = "http://127.0.0.1:8000/books/"
# Dữ liệu cần gửi dưới dạng JSON
data = {
"title": "Số hồng",
"author": "Test",
"year": 1999
}
try:
# Gửi yêu cầu POST với dữ liệu JSON
response = requests.post(url, json=data)
response.raise_for_status() # Kiểm tra lỗi HTTP
# Hiển thị kết quả phản hồi
print(response.json())
except requests.exceptions.RequestException as e:
# Hiển thị lỗi nếu có
print(f"Đã xảy ra lỗi: {e}")
Kết quả sẽ là:
PUT
Cập nhật thông tin sách:
-
Gửi một PUT request tới
http://127.0.0.1:8000/books/1
để cập nhật sách có ID = 1. -
Ví dụ body:
{ "title": "The Catcher in the Rye (Updated)", "author": "J.D. Salinger", "year": 1951 }
Cập nhật thêm code vào main.py nhé:
# 3. Cập nhật sách
@app.put("/books/{book_id}")
def update_book(book_id: int, new_book: Book):
for b in book:
if b["id"] == book_id:
b["title"] = new_book.title
b["author"] = new_book.author
b["year"] = new_book.year
return b
return {"error": "Không tìm thấy sách"}
DELETE
Xóa sách:
- Gửi một DELETE request tới
http://127.0.0.1:8000/books/1
để xóa sách có ID = 1.
# 4. Xóa sách theo ID
@app.delete("/books/{book_id}")
def delete_book(book_id: int):
for b in book:
if b["id"] == book_id:
book.remove(b)
return {"message": "Xóa sách thành công"}
return {"error": "Không tìm thấy sách"}
Kiểm tra trong giao diện Swagger UI
FastAPI tự động tạo một giao diện Swagger UI để bạn có thể thử nghiệm các API mà không cần phải sử dụng công cụ như Postman. Để truy cập vào Swagger UI, bạn chỉ cần vào đường dẫn sau trong trình duyệt của mình:
http://127.0.0.1:8000/docs
Giao diện Swagger UI sẽ hiển thị tất cả các route của ứng dụng và cho phép bạn thử nghiệm các API trực tiếp.
Kết
Thật ra bài này khá dài, mình cũng lỡ hướng dẫn lố phần Pydantic - nhẽ ra sẽ tìm hiểu ở ngày 3 (này bạn xem kỹ ở code POST là sẽ hiểu nhen). Haha, có gian nan thử thách thì mới có thành công chứ. Túm lại là, qua bài tập này, bạn đã thực hành các kỹ năng cơ bản về Routing trong FastAPI cũng như sử dụng Postman để tương tác với API. Tự tạo một API đơn giản với các route và sử dụng các HTTP methods như GET, POST, PUT và DELETE để quản lý tài nguyên (sách). Đây là nền tảng để phát triển các ứng dụng API phức tạp hơn.
Tiếp tục học hỏi nhiều hơn nữa hén! Đón chờ ngày 3 - Pydantic và Validate Dữ liệu
All rights reserved