Dự án tổng kết: xây dựng API quản lý sách bằng FastAPI, kiểm tra dữ liệu bằng Pydantic, lưu vào SQLite với SQLModel, tự sinh tài liệu và viết kiểm thử. Đây là nền tảng để làm backend cho web và ứng dụng di động (Flutter).
- Tạo REST API với FastAPI
- Kiểm tra dữ liệu vào ra bằng Pydantic
- Lưu dữ liệu với SQLModel + SQLite
- Kiểm thử API và chuẩn bị triển khai
- FastAPI
- Framework web Python hiện đại, nhanh, dựa trên type hints.
- Pydantic
- Thư viện kiểm tra dữ liệu theo kiểu.
- ORM
- Ánh xạ bảng CSDL thành lớp Python.
- OpenAPI
- Chuẩn mô tả API; FastAPI tự sinh trang /docs.
Khái niệm và lý thuyết
FastAPI tận dụng type hints (Bài 13): khai báo kiểu tham số là đủ để FastAPI tự kiểm tra dữ liệu, chuyển đổi JSON, sinh tài liệu tương tác. Hỗ trợ cả hàm thường và async def (Bài 19).
Kiến trúc REST cho tài nguyên “sách”: GET /sach (danh sách), GET /sach/{id}, POST /sach (thêm), PATCH /sach/{id} (sửa), DELETE /sach/{id}.
Khởi tạo
uv init api-sach && cd api-sach
uv add "fastapi[standard]" sqlmodel
uv add --dev pytestmain.py
from fastapi import FastAPI, HTTPException, Depends
from sqlmodel import SQLModel, Field, Session, create_engine, select
class SachTao(SQLModel):
ten: str = Field(min_length=1, max_length=200)
tac_gia: str
nam: int = Field(ge=1900, le=2100)
class Sach(SachTao, table=True):
id: int | None = Field(default=None, primary_key=True)
engine = create_engine("sqlite:///sach.db")
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as s:
yield s
app = FastAPI(title="API Sách - Tự Học Tin")
@app.get("/sach", response_model=list[Sach])
def danh_sach(tu_khoa: str = "", s: Session = Depends(get_session)):
q = select(Sach).where(Sach.ten.contains(tu_khoa))
return s.exec(q).all()
@app.get("/sach/{id}", response_model=Sach)
def chi_tiet(id: int, s: Session = Depends(get_session)):
sach = s.get(Sach, id)
if not sach:
raise HTTPException(404, "Không tìm thấy sách")
return sach
@app.post("/sach", response_model=Sach, status_code=201)
def them(du_lieu: SachTao, s: Session = Depends(get_session)):
sach = Sach.model_validate(du_lieu)
s.add(sach); s.commit(); s.refresh(sach)
return sach
@app.delete("/sach/{id}", status_code=204)
def xoa(id: int, s: Session = Depends(get_session)):
sach = chi_tiet(id, s)
s.delete(sach); s.commit()uv run fastapi dev main.py
# Mở http://127.0.0.1:8000/docs để thử API ngay trên trình duyệtKiểm thử API
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_them_va_lay():
r = client.post("/sach", json={"ten": "Học Python", "tac_gia": "TBU", "nam": 2026})
assert r.status_code == 201
id = r.json()["id"]
assert client.get(f"/sach/{id}").json()["ten"] == "Học Python"
def test_du_lieu_sai():
assert client.post("/sach", json={"ten": "", "tac_gia": "A", "nam": 1800}).status_code == 422Triển khai
- Chạy production:
uv run fastapi run main.py(Uvicorn). - Đóng gói Docker hoặc triển khai lên Render, Railway, Fly.io, máy chủ VPS.
- Thay SQLite bằng PostgreSQL khi nhiều người dùng: chỉ đổi chuỗi kết nối.
- Bật CORS (CORSMiddleware) khi front-end JavaScript/Flutter gọi từ tên miền khác.
Ứng dụng Flutter (loạt bài Flutter, Bài 9) hoặc trang JavaScript (Bài 7) có thể gọi trực tiếp API này: bạn đã có một hệ thống full-stack hoàn chỉnh.
Không đưa tệp sach.db, khóa bí mật lên GitHub. Thêm xác thực (OAuth2/JWT của FastAPI) trước khi cho phép thêm/xóa trên Internet.
- Hoàn thiện API, thêm PATCH /sach/{id} để sửa từng phần.
- Thêm bảng TheLoai và quan hệ một-nhiều với Sach.
- Viết đủ test cho mọi endpoint, chạy pytest.
- Viết trang HTML + JavaScript hiển thị danh sách sách từ API.
- FastAPI dựa trên type hints, tự kiểm tra dữ liệu và sinh /docs.
- Pydantic/SQLModel mô tả dữ liệu và bảng CSDL.
- TestClient kiểm thử API không cần chạy máy chủ.
- Đây là nền backend cho web và di động.