Tạo REST API nhanh với FastAPI: kiểu dữ liệu, tài liệu tự động và kiểm tra đầu vào

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

FastAPI là framework Python hiện đại cho phép viết API nhanh, có kiểm tra dữ liệu tự động và tài liệu tương tác ngay khi chạy. Bài này hướng dẫn bạn dựng API quản lý sản phẩm với đầy đủ thao tác CRUD, tận dụng gợi ý kiểu của Python.

Điểm nổi bật của FastAPI

FastAPI dựa trên Starlette và Pydantic. Bạn khai báo kiểu dữ liệu bằng chú thích kiểu của Python, framework sẽ tự kiểm tra đầu vào, chuyển đổi dữ liệu và sinh tài liệu OpenAPI. Nhờ đó bạn viết ít code lặp, ít lỗi vặt, và có sẵn trang tài liệu để thử API.

Bước 1: Cài đặt

python -m venv venv
# kích hoạt môi trường ảo, rồi:
pip install fastapi "uvicorn[standard]"

Uvicorn là máy chủ ASGI để chạy ứng dụng FastAPI.

Bước 2: API đầu tiên

Tạo file main.py:

from fastapi import FastAPI

app = FastAPI(title="API sản phẩm")

@app.get("/")
def home():
    return {"message": "FastAPI đang chạy"}

Chạy bằng lệnh:

uvicorn main:app --reload

Tùy chọn --reload tự khởi động lại khi bạn sửa code, chỉ nên dùng khi phát triển. Mở http://127.0.0.1:8000 để xem kết quả. Quan trọng hơn, mở http://127.0.0.1:8000/docs bạn sẽ thấy giao diện Swagger UI với danh sách endpoint, có thể bấm nút để thử trực tiếp. Còn có /redoc là kiểu tài liệu khác.

Bước 3: Mô hình dữ liệu với Pydantic

from typing import Optional
from pydantic import BaseModel, Field

class ProductIn(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    price: float = Field(gt=0)
    description: Optional[str] = None

class Product(ProductIn):
    id: int

Lớp ProductIn mô tả dữ liệu khách gửi lên, kèm ràng buộc: tên không rỗng, giá lớn hơn 0. Lớp Product kế thừa và thêm id cho dữ liệu trả về. Tách "đầu vào" và "đầu ra" là thói quen tốt để không vô tình cho client đặt các trường như id.

Bước 4: CRUD với lưu trữ trong bộ nhớ

from fastapi import HTTPException, status

products: dict[int, Product] = {}
next_id = 1

@app.get("/products", response_model=list[Product])
def list_products(max_price: Optional[float] = None):
    items = list(products.values())
    if max_price is not None:
        items = [p for p in items if p.price <= max_price]
    return items

@app.get("/products/{product_id}", response_model=Product)
def get_product(product_id: int):
    product = products.get(product_id)
    if product is None:
        raise HTTPException(status_code=404, detail="Không tìm thấy sản phẩm")
    return product

@app.post("/products", response_model=Product, status_code=status.HTTP_201_CREATED)
def create_product(data: ProductIn):
    global next_id
    product = Product(id=next_id, **data.model_dump())
    products[next_id] = product
    next_id += 1
    return product

@app.put("/products/{product_id}", response_model=Product)
def update_product(product_id: int, data: ProductIn):
    if product_id not in products:
        raise HTTPException(status_code=404, detail="Không tìm thấy sản phẩm")
    product = Product(id=product_id, **data.model_dump())
    products[product_id] = product
    return product

@app.delete("/products/{product_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_product(product_id: int):
    if products.pop(product_id, None) is None:
        raise HTTPException(status_code=404, detail="Không tìm thấy sản phẩm")

Một số điểm hay: tham số trong đường dẫn {product_id} được khai báo kiểu int nên gửi chữ sẽ bị từ chối với mã 422 kèm giải thích. Tham số không có trong đường dẫn như max_price tự trở thành tham số truy vấn tùy chọn. Tham số response_model lọc và định dạng dữ liệu trả về. Hàm model_dump() là cách của Pydantic phiên bản 2 để chuyển model thành từ điển; nếu bạn dùng phiên bản 1, hãy dùng dict().

Bước 5: Thử bằng Swagger UI

  1. Mở /docs, chọn POST /products, bấm Try it out.
  2. Nhập JSON như {"name": "Bút bi", "price": 5000} và Execute.
  3. Thử gửi price bằng -1 để thấy lỗi 422 chi tiết do Pydantic sinh ra.
  4. Gọi GET /products với max_price=10000 để thử bộ lọc.

Hàm đồng bộ hay bất đồng bộ

Bạn có thể khai báo endpoint bằng def hoặc async def. Dùng async def khi bên trong có các lời gọi bất đồng bộ cần await (ví dụ thư viện HTTP hoặc driver cơ sở dữ liệu async). Với thư viện chặn thông thường, dùng def, FastAPI sẽ chạy nó trong luồng riêng để không làm nghẽn vòng lặp sự kiện.

Những bước tiếp theo

  • Thay dict bằng cơ sở dữ liệu thật qua SQLAlchemy hoặc SQLModel.
  • Thêm xác thực OAuth2 với JWT.
  • Tách code thành các router theo nhóm chức năng bằng APIRouter.
  • Viết test với TestClient của FastAPI.
  • Đóng gói bằng Docker để triển khai.

Kết luận

FastAPI cho bạn năng suất cao nhờ kiểu dữ liệu, kiểm tra tự động và tài liệu sinh sẵn. Chỉ trong một file bạn đã có API đầy đủ CRUD, xử lý lỗi và trang thử nghiệm.

Nếu muốn đi sâu vào backend Python và xây dựng API chuyên nghiệp, bạn có thể tham khảo các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment