Xây dựng GraphQL API hiệu năng cao với Python và Strawberry

Python tutorial - IT technology blog
Python tutorial - IT technology blog

Tại sao nên chọn GraphQL và Strawberry thay vì REST?

Nếu từng làm REST API cho các dự án thực tế, chắc hẳn bạn đã gặp cảnh Frontend cần hiển thị tên user kèm 3 bài viết mới nhất. Với REST, bạn thường phải gọi 2 API riêng biệt hoặc sửa endpoint /user để nhồi nhét thêm dữ liệu bài viết. Cách đầu tiên gây tốn network round-trip, còn cách thứ hai khiến API trở nên cồng kềnh, khó bảo trì.

GraphQL giải quyết triệt để vấn đề này bằng cách cho phép client tự định nghĩa dữ liệu họ thực sự cần. Trước đây, việc triển khai GraphQL trong Python khá rườm rà với Graphene. Tuy nhiên, khi chuyển sang Strawberry, mình cảm thấy dễ thở hơn hẳn. Thư viện này tận dụng tối đa Type Hints của Python hiện đại, giúp code sạch, dễ debug và hỗ trợ cực tốt cho các IDE như VS Code hay PyCharm.

Trong một dự án dashboard xử lý khoảng 2.000 request mỗi giây, việc kết hợp Strawberry và FastAPI đã giúp mình rút ngắn 30% thời gian phát triển. Thay vì ngồi định nghĩa hàng chục endpoint REST nhỏ lẻ, mình chỉ cần tập trung vào Schema. Hiệu năng của hệ thống cũng được đảm bảo nhờ cơ chế async/await đồng bộ từ cả hai thư viện.

Cài đặt môi trường

Bạn nên sử dụng Python 3.9 trở lên để tận dụng tốt nhất các tính năng về kiểu dữ liệu. Chúng ta sẽ cài đặt FastAPI làm framework web và Strawberry phiên bản hỗ trợ FastAPI.

# Khởi tạo môi trường ảo
python -m venv venv
source venv/bin/activate

# Cài đặt bộ thư viện
pip install "strawberry-graphql[fastapi]" uvicorn fastapi

Mình cài thêm uvicorn để làm ASGI server. Đây là lựa chọn tiêu chuẩn để chạy các ứng dụng Python async trong môi trường production hiện nay.

Thiết kế Schema: Tư duy theo Type

Trong thế giới GraphQL, Schema là thành phần quan trọng nhất. Thay vì loay hoay với các URL, chúng ta sẽ định nghĩa các “Type”. Giả sử bạn đang làm ứng dụng quản lý sách, hãy bắt đầu bằng decorator @strawberry.type.

import strawberry
from typing import List, Optional

@strawberry.type
class Book:
    id: int
    title: str
    author: str
    price: float

# Mock data
BOOKS_DB = [
    Book(id=1, title="Lập trình Python", author="Admin IT", price=150.0),
    Book(id=2, title="GraphQL cơ bản", author="Strawberry Fan", price=200.0),
]

@strawberry.type
class Query:
    @strawberry.field
    def books(self) -> List[Book]:
        return BOOKS_DB

    @strawberry.field
    def book_by_id(self, id: int) -> Optional[Book]:
        return next((book for book in BOOKS_DB if book.id == id), None)

Điểm cộng lớn của Strawberry là sử dụng chính Type Hints thuần túy của Python. Bạn không cần học cú pháp khai báo kiểu dữ liệu mới. Nếu bạn đã quen dùng Pydantic, việc tiếp cận Strawberry sẽ chỉ mất vài phút.

Tích hợp vào FastAPI

Sau khi xong Schema, bạn cần đưa nó lên một endpoint để client truy cập. Strawberry cung cấp GraphQLRouter giúp việc tích hợp vào FastAPI trở nên cực kỳ đơn giản.

from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter

# Khởi tạo Schema
schema = strawberry.Schema(query=Query)
graphql_app = GraphQLRouter(schema)

app = FastAPI()
app.include_router(graphql_app, prefix="/graphql")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

Chạy file và truy cập http://localhost:8000/graphql, bạn sẽ thấy giao diện GraphiQL. Tại đây, bạn có thể test query trực tiếp mà không cần dùng Postman hay Insomnia.

Xử lý bài toán N+1 để tối ưu tốc độ

Lỗi N+1 là “sát thủ” thầm lặng tiêu diệt hiệu năng GraphQL. Ví dụ: Khi lấy danh sách 10 cuốn sách, nếu mỗi cuốn bạn lại gọi DB một lần để lấy tên tác giả, hệ thống sẽ thực thi tổng cộng 11 truy vấn. Điều này cực kỳ lãng phí tài nguyên.

Giải pháp tối ưu nhất là sử dụng DataLoader. Cơ chế này sẽ gom tất cả ID cần tìm và thực hiện duy nhất một truy vấn SQL với điều kiện WHERE id IN (...).

from strawberry.dataloader import DataLoader

async def load_authors(keys: List[int]) -> List[str]:
    # Chỉ chạy 1 truy vấn duy nhất cho tất cả keys
    author_map = {1: "Nguyễn Văn A", 2: "Trần Thị B"}
    return [author_map.get(key, "Unknown") for key in keys]

author_loader = DataLoader(load_fn=load_authors)

@strawberry.type
class BookWithLoader:
    id: int
    author_id: int
    
    @strawberry.field
    async def author_name(self) -> str:
        return await author_loader.load(self.author_id)

Áp dụng DataLoader giúp mình giảm latency một trang báo cáo từ 5 giây xuống còn chưa đầy 200ms. Đây là kỹ thuật bắt buộc phải biết nếu bạn muốn làm việc với dữ liệu lớn.

Giám sát và vận hành

Làm sao để biết API hoạt động ổn định? Với FastAPI, bạn nên thêm middleware để log thời gian thực thi của từng request. Strawberry cũng hỗ trợ Extensions để tích hợp Apollo Tracing hoặc Sentry.

Đừng bao giờ đẩy API lên production mà thiếu công cụ giám sát. Việc theo dõi xem query nào đang ngốn tài nguyên sẽ giúp bạn tối ưu hệ thống kịp thời. Sự kết hợp giữa tính chặt chẽ của Strawberry và tốc độ của FastAPI sẽ tạo nên một hệ thống backend mạnh mẽ, khiến đội Frontend làm việc hiệu quả hơn rất nhiều.

Share: