Back to blog

Học FastAPI: Bắt Đầu Nhanh

pythonfastapiapiweb developmentbackend
Học FastAPI: Bắt Đầu Nhanh

FastAPI đã nhanh chóng trở thành một trong những web framework Python phổ biến nhất, và có lý do chính đáng cho điều đó. Nó kết hợp sự dễ dàng của Flask với sức mạnh của các tính năng Python hiện đại, tài liệu API tự động và hiệu suất đáng kinh ngạc. Nếu bạn đang xây dựng API bằng Python, FastAPI nên ở đầu danh sách của bạn.

FastAPI Là Gì?

FastAPI là một web framework hiện đại, hiệu suất cao để xây dựng API với Python 3.8+ dựa trên type hints chuẩn của Python. Được tạo ra bởi Sebastián Ramírez vào năm 2018, nó đã nhanh chóng thu hút sự chú ý nhờ trải nghiệm developer và hiệu suất.

Tính Năng Chính:

  • Nhanh - Một trong những Python framework nhanh nhất có sẵn, ngang với NodeJS và Go
  • Type hints - Xây dựng trên type hints của Python để hỗ trợ IDE tốt hơn và validation
  • Tài liệu tự động - Tài liệu API tương tác được tạo tự động
  • Python hiện đại - Sử dụng async/await cho code async hiệu suất cao
  • Dựa trên chuẩn - Xây dựng trên OpenAPI và JSON Schema

Tại Sao Chọn FastAPI?

1. Trải Nghiệm Developer Đáng Kinh Ngạc

FastAPI cung cấp autocomplete và validation tuyệt vời ngay lập tức:

from fastapi import FastAPI
from pydantic import BaseModel
 
app = FastAPI()
 
class User(BaseModel):
    name: str
    email: str
    age: int
 
@app.post("/users/")
async def create_user(user: User):
    # Validation và parsing tự động!
    # IDE của bạn biết chính xác các kiểu dữ liệu
    return {"message": f"Created user {user.name}"}

Lợi Ích:

  • Hỗ trợ autocomplete đầy đủ trong IDE
  • Validation request tự động
  • Thông báo lỗi rõ ràng
  • Không cần parsing thủ công

2. Tài Liệu Tương Tác Tự Động

Ngay khi bạn chạy ứng dụng, bạn có tài liệu API đẹp, tương tác tại /docs:

# Chỉ cần chạy ứng dụng
# Truy cập http://localhost:8000/docs
# Xem Swagger UI với tất cả endpoints
# Test API ngay trong trình duyệt!

Bạn cũng có tài liệu ReDoc tại /redoc - hai style tài liệu miễn phí!

3. Hiệu Suất Quan Trọng

FastAPI được xây dựng trên Starlette và Pydantic, làm cho nó trở thành một trong những Python framework nhanh nhất:

# Hỗ trợ async cho concurrency cao
@app.get("/items/{item_id}")
async def read_item(item_id: int):
    # Xử lý hàng nghìn concurrent requests
    result = await fetch_from_database(item_id)
    return result

So sánh hiệu suất:

  • FastAPI: ~60,000 requests/giây
  • Flask: ~3,000 requests/giây
  • Django: ~2,000 requests/giây

Bắt Đầu: Cài Đặt

Thiết lập FastAPI rất đơn giản:

# Tạo virtual environment
python -m venv venv
source venv/bin/activate  # Trên Windows: venv\Scripts\activate
 
# Cài đặt FastAPI và Uvicorn (ASGI server)
pip install "fastapi[standard]"
 
# Hoặc cài đặt riêng
pip install fastapi uvicorn[standard]

Ứng Dụng FastAPI Đầu Tiên

Tạo file main.py:

from fastapi import FastAPI
 
# Tạo FastAPI instance
app = FastAPI()
 
# Định nghĩa endpoint đầu tiên
@app.get("/")
async def root():
    return {"message": "Hello World"}
 
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

Chạy ứng dụng:

# Start development server
uvicorn main:app --reload
 
# API của bạn đang chạy tại http://localhost:8000
# Tài liệu có sẵn tại http://localhost:8000/docs

Đó là tất cả! Bạn có một API hoạt động với validation tự động, tài liệu và type safety.

Path Parameters và Validation

FastAPI làm cho path parameters type-safe và được validate:

from fastapi import FastAPI, Path
from typing import Annotated
 
app = FastAPI()
 
@app.get("/items/{item_id}")
async def read_item(
    item_id: Annotated[int, Path(title="ID của item", ge=1, le=1000)]
):
    return {"item_id": item_id}

Điều gì xảy ra ở đây:

  • item_id phải là integer
  • Phải nằm giữa 1 và 1000 (ge = greater or equal, le = less or equal)
  • Validation và thông báo lỗi tự động
  • Được tài liệu hóa trong docs tương tác

Query Parameters

Query parameters hoạt động cũng elegantly:

from fastapi import FastAPI, Query
from typing import Annotated
 
app = FastAPI()
 
@app.get("/items/")
async def read_items(
    skip: int = 0,
    limit: Annotated[int, Query(le=100)] = 10,
    search: str | None = None
):
    return {
        "skip": skip,
        "limit": limit,
        "search": search
    }
 
# Ví dụ: /items/?skip=20&limit=50&search=python

Request Body với Pydantic Models

Pydantic models cung cấp data validation mạnh mẽ:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
 
app = FastAPI()
 
class User(BaseModel):
    username: str = Field(..., min_length=3, max_length=50)
    email: EmailStr  # Validates email format
    full_name: str | None = None
    age: int = Field(..., ge=0, le=150)
    created_at: datetime = Field(default_factory=datetime.now)
 
@app.post("/users/")
async def create_user(user: User):
    return {
        "message": "User created successfully",
        "user": user
    }

Thử request không hợp lệ này:

{
    "username": "ab",
    "email": "not-an-email",
    "age": 200
}

Bạn sẽ nhận được lỗi validation chi tiết tự động!

Response Models

Kiểm soát chính xác những gì API của bạn trả về:

from pydantic import BaseModel
 
class UserIn(BaseModel):
    username: str
    password: str
    email: str
 
class UserOut(BaseModel):
    username: str
    email: str
 
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn):
    # Password sẽ không có trong response!
    return user

Dependency Injection

FastAPI có hệ thống dependency injection mạnh mẽ:

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
 
security = HTTPBearer()
 
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
    if credentials.credentials != "secret-token":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials"
        )
    return credentials.credentials
 
@app.get("/protected/")
async def protected_route(token: str = Depends(verify_token)):
    return {"message": "You have access!", "token": token}

Ví Dụ Tích Hợp Database

Đây là một ví dụ thực tế với SQLAlchemy:

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
 
# Database setup
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
 
# Model
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True)
    name = Column(String)
 
Base.metadata.create_all(bind=engine)
 
# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
 
# FastAPI app
app = FastAPI()
 
# Pydantic models
from pydantic import BaseModel
 
class UserCreate(BaseModel):
    email: str
    name: str
 
class UserResponse(BaseModel):
    id: int
    email: str
    name: str
 
    class Config:
        from_attributes = True
 
@app.post("/users/", response_model=UserResponse)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
    db_user = User(email=user.email, name=user.name)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user
 
@app.get("/users/{user_id}", response_model=UserResponse)
def read_user(user_id: int, db: Session = Depends(get_db)):
    user = db.query(User).filter(User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user

Background Tasks

FastAPI làm cho background tasks trở nên đơn giản:

from fastapi import BackgroundTasks
 
def send_email(email: str, message: str):
    # Giả lập gửi email
    print(f"Sending email to {email}: {message}")
 
@app.post("/send-notification/")
async def send_notification(
    email: str,
    background_tasks: BackgroundTasks
):
    background_tasks.add_task(send_email, email, "Welcome to our service!")
    return {"message": "Notification will be sent in background"}

Xử Lý Lỗi

Custom exception handlers cho bạn toàn quyền kiểm soát:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
 
class CustomException(Exception):
    def __init__(self, name: str):
        self.name = name
 
app = FastAPI()
 
@app.exception_handler(CustomException)
async def custom_exception_handler(request: Request, exc: CustomException):
    return JSONResponse(
        status_code=418,
        content={"message": f"Oops! {exc.name} did something wrong."}
    )
 
@app.get("/custom/{name}")
async def read_custom(name: str):
    if name == "error":
        raise CustomException(name=name)
    return {"name": name}

Testing API Của Bạn

FastAPI apps dễ dàng test với TestClient:

from fastapi.testclient import TestClient
 
client = TestClient(app)
 
def test_read_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Hello World"}
 
def test_create_user():
    response = client.post(
        "/users/",
        json={
            "username": "testuser",
            "email": "[email protected]",
            "age": 25
        }
    )
    assert response.status_code == 200
    data = response.json()
    assert data["user"]["username"] == "testuser"

Best Practices

1. Cấu Trúc Dự Án

myapi/
├── main.py              # Entry point
├── api/
│   ├── __init__.py
│   ├── users.py         # User routes
│   └── items.py         # Item routes
├── models/
│   ├── __init__.py
│   ├── user.py          # Database models
│   └── item.py
├── schemas/
│   ├── __init__.py
│   ├── user.py          # Pydantic schemas
│   └── item.py
├── services/
│   ├── __init__.py
│   └── user_service.py  # Business logic
└── tests/
    ├── __init__.py
    └── test_users.py

2. Sử Dụng APIRouter Cho Tính Modular

# api/users.py
from fastapi import APIRouter
 
router = APIRouter(prefix="/users", tags=["users"])
 
@router.get("/")
async def list_users():
    return []
 
# main.py
from api import users
 
app = FastAPI()
app.include_router(users.router)

3. Environment Variables

from pydantic_settings import BaseSettings
 
class Settings(BaseSettings):
    database_url: str
    secret_key: str
    api_key: str
    
    class Config:
        env_file = ".env"
 
settings = Settings()

4. Thêm CORS Middleware

from fastapi.middleware.cors import CORSMiddleware
 
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Các Lỗi Phổ Biến Cần Tránh

1. Trộn Lẫn Sync và Async

# ❌ Không tốt - trộn sync và async
@app.get("/users/")
async def get_users():
    users = blocking_database_call()  # Chặn event loop!
    return users
 
# ✅ Tốt - giữ nhất quán
@app.get("/users/")
def get_users():
    users = blocking_database_call()
    return users
 
# ✅ Tốt hơn - sử dụng async đúng cách
@app.get("/users/")
async def get_users():
    users = await async_database_call()
    return users

2. Không Sử Dụng Response Models

# ❌ Không tốt - không kiểm soát response
@app.get("/users/{user_id}")
def get_user(user_id: int):
    user = get_user_from_db(user_id)
    return user  # Có thể bao gồm password!
 
# ✅ Tốt - response model rõ ràng
@app.get("/users/{user_id}", response_model=UserOut)
def get_user(user_id: int):
    user = get_user_from_db(user_id)
    return user  # Password tự động được loại bỏ

Khi Nào Sử Dụng FastAPI vs. Các Framework Khác

Sử dụng FastAPI khi:

  • Xây dựng REST APIs hiện đại
  • Cần validation và documentation tự động
  • Muốn hiệu suất cao với async support
  • Xây dựng microservices
  • Type safety quan trọng

Cân nhắc các lựa chọn khác:

  • Flask: Dự án đơn giản hơn, ecosystem trưởng thành hơn
  • Django: Ứng dụng web đầy đủ tính năng với admin panel
  • Sanic: Tập trung hoàn toàn vào hiệu suất, ít tính năng hơn

Ví Dụ Thực Tế: Todo API

Đây là một ví dụ hoàn chỉnh, sẵn sàng cho production:

from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
from typing import List
from datetime import datetime
import uuid
 
app = FastAPI(title="Todo API", version="1.0.0")
 
# In-memory storage (sử dụng database thật trong production!)
todos = {}
 
class TodoCreate(BaseModel):
    title: str
    description: str | None = None
 
class TodoUpdate(BaseModel):
    title: str | None = None
    description: str | None = None
    completed: bool | None = None
 
class Todo(BaseModel):
    id: str
    title: str
    description: str | None
    completed: bool
    created_at: datetime
    updated_at: datetime
 
@app.post("/todos/", response_model=Todo, status_code=201)
def create_todo(todo: TodoCreate):
    todo_id = str(uuid.uuid4())
    now = datetime.now()
    new_todo = Todo(
        id=todo_id,
        title=todo.title,
        description=todo.description,
        completed=False,
        created_at=now,
        updated_at=now
    )
    todos[todo_id] = new_todo
    return new_todo
 
@app.get("/todos/", response_model=List[Todo])
def list_todos(skip: int = 0, limit: int = 10):
    return list(todos.values())[skip:skip + limit]
 
@app.get("/todos/{todo_id}", response_model=Todo)
def get_todo(todo_id: str):
    if todo_id not in todos:
        raise HTTPException(status_code=404, detail="Todo not found")
    return todos[todo_id]
 
@app.patch("/todos/{todo_id}", response_model=Todo)
def update_todo(todo_id: str, todo_update: TodoUpdate):
    if todo_id not in todos:
        raise HTTPException(status_code=404, detail="Todo not found")
    
    stored_todo = todos[todo_id]
    update_data = todo_update.model_dump(exclude_unset=True)
    updated_todo = stored_todo.model_copy(update=update_data)
    updated_todo.updated_at = datetime.now()
    todos[todo_id] = updated_todo
    return updated_todo
 
@app.delete("/todos/{todo_id}", status_code=204)
def delete_todo(todo_id: str):
    if todo_id not in todos:
        raise HTTPException(status_code=404, detail="Todo not found")
    del todos[todo_id]

Các Bước Tiếp Theo

Bây giờ bạn đã hiểu những điều cơ bản, đây là những gì cần khám phá tiếp theo:

  1. Authentication & Security

    • OAuth2 với JWT tokens
    • API key authentication
    • Rate limiting
  2. Tích Hợp Database

    • SQLAlchemy ORM
    • Tortoise ORM (async)
    • MongoDB với Motor
  3. Tính Năng Nâng Cao

    • Hỗ trợ WebSocket
    • GraphQL với Strawberry
    • Upload và download files
    • Custom middleware
  4. Deployment

    • Docker containerization
    • Deploy lên AWS/GCP/Azure
    • CI/CD pipelines
    • Load balancing và scaling
  5. Testing & Quality

    • Unit testing với pytest
    • Integration testing
    • Load testing với Locust
    • Code coverage

Kết Luận

FastAPI là một bước ngoặt cho phát triển API Python. Nó kết hợp những điều tốt nhất của các tính năng Python hiện đại với trải nghiệm developer xuất sắc, tài liệu tự động và hiệu suất ấn tượng. Cho dù bạn đang xây dựng REST API đơn giản hay kiến trúc microservices phức tạp, FastAPI có các công cụ bạn cần.

Việc framework sử dụng type hints và Pydantic models có nghĩa là ít bug hơn, hỗ trợ IDE tốt hơn và code dễ maintain hơn. Kết hợp với tài liệu tương tác tự động, không có gì ngạc nhiên khi FastAPI đã trở thành một trong những Python frameworks phát triển nhanh nhất.

Bắt đầu xây dựng ứng dụng FastAPI đầu tiên của bạn ngay hôm nay - bạn sẽ ngạc nhiên về việc bạn có thể tạo APIs sẵn sàng cho production nhanh như thế nào với code sạch, type-safe.

Tham Khảo

Chúc bạn code vui vẻ! 🚀

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.