Danh sách bài viết

Bài 1: Vì sao FastAPI thay vì Flask / Django cho AI

So sánh kỹ thuật FastAPI, Flask và Django REST Framework khi expose AI model thành HTTP API: async, type hint, Pydantic v2, OpenAPI tự động và benchmark TechEmpower. Bao gồm cả giới hạn và trường hợp nên dùng framework khác.

27/05/2026
2 lượt xem
1

Mục tiêu bài học

Sau bài này bạn sẽ:

  • Hiểu sự khác biệt kỹ thuật giữa FastAPI, Flask và Django REST Framework khi serve AI model.
  • Biết tại sao async-first quan trọng với AI inference workload.
  • Biết FastAPI sinh OpenAPI schema và docs UI như thế nào.
  • Nhận ra giới hạn của FastAPI và khi nào Flask / Django là lựa chọn hợp lý hơn.
2

Bối cảnh: expose model thành HTTP API

Khi một AI Engineer hoàn thành training hoặc lấy model từ Hugging Face, bước tiếp theo thường là cho phép các service khác — frontend, mobile app, hoặc pipeline nội bộ — gọi đến model qua HTTP. Nhiệm vụ đó thuộc về một web framework.

Có ba framework Python phổ biến nhất cho nhiệm vụ này:

  • Flask — micro-framework, ra đời 2010, WSGI-based, cộng đồng lớn.
  • Django + Django REST Framework (DRF) — full-stack framework, batteries included, ORM, admin, auth sẵn có.
  • FastAPI — ra đời 2018, ASGI-based, type hint native, Pydantic v2, OpenAPI tự động.

Không framework nào là "dành riêng cho AI". Sự khác biệt nằm ở thiết kế kỹ thuật và trade-off mà mỗi framework đưa ra — phần còn lại của bài sẽ phân tích từng điểm.

3

Async-first: ASGI vs WSGI

WSGI và vấn đề blocking I/O

WSGI (Web Server Gateway Interface, PEP 3333) là giao thức đồng bộ: server gọi application callable, application trả về response, rồi server mới xử lý request tiếp theo (hoặc dùng thread/process pool để chạy song song).

Flask chạy trên WSGI. Flask 2.x có hỗ trợ async def view, nhưng về bản chất vẫn chạy trong thread pool thông qua asyncio.run() — không phải event loop thực sự. Mỗi async view vẫn chiếm 1 thread trong worker pool.

Django hỗ trợ ASGI từ phiên bản 3.0 (2019), nhưng phần lớn middleware, ORM (django.db), và third-party app vẫn là sync. Gọi ORM bên trong async def view cần sync_to_async() wrapper để tránh deadlock, gây ra overhead thêm.

FastAPI và ASGI

FastAPI build trên Starlette — một ASGI framework — và chạy trực tiếp trên Python asyncio event loop (Python 3.7+). Đây là native async, không phải thread pool giả lập.

# FastAPI: async def thực sự chạy trên asyncio event loop
from fastapi import FastAPI

app = FastAPI()

@app.post("/predict")
async def predict(payload: dict):
    # I/O-bound: gọi LLM API, vector DB → await không block event loop
    result = await call_llm_api(payload["text"])
    return {"result": result}

Tại sao async quan trọng với AI workload

AI application thường có nhiều I/O-bound operation:

  • Gọi OpenAI / Anthropic / Gemini API qua HTTP — latency 200ms đến vài giây.
  • Query vector database (Pinecone, Qdrant, Weaviate) qua mạng.
  • Đọc file lớn (embedding batch, document chunk).
  • Gọi database để lưu lịch sử conversation.

Với ASGI async, trong khoảng thời gian chờ I/O, event loop có thể xử lý request khác mà không cần thêm thread. Với Flask WSGI, request đó chiếm thread trong suốt thời gian chờ.

Lưu ý: nếu inference chạy locally trên CPU/GPU (ví dụ model.predict() là PyTorch synchronous call), thao tác đó vẫn là CPU-bound và block event loop. Xử lý trường hợp này sẽ được đề cập chi tiết ở Bài 4 (async endpoint).

4

Type hints và Pydantic v2

FastAPI đọc type annotation trực tiếp từ function signature để tự sinh validation và schema. Không cần viết serializer tách rời.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class PredictRequest(BaseModel):
    text: str
    max_tokens: int = 256

class PredictResponse(BaseModel):
    label: str
    confidence: float

@app.post("/predict", response_model=PredictResponse)
async def predict(body: PredictRequest) -> PredictResponse:
    # body đã được validate tự động
    # nếu client gửi text=123 (int), FastAPI trả 422 Unprocessable Entity
    ...

FastAPI dùng Pydantic v2 (từ FastAPI 0.100.0, tháng 7/2023). Pydantic v2 rewrite lõi bằng Rust, validation nhanh hơn Pydantic v1 khoảng 5–50 lần tuỳ kiểu dữ liệu (theo benchmark chính thức của Pydantic).

So sánh với Flask và Django

Flask: không có validation tích hợp. Phải thêm thư viện ngoài như marshmallow, flask-pydantic, hoặc tự viết kiểm tra thủ công:

# Flask: phải tự validate
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/predict", methods=["POST"])
def predict():
    data = request.get_json()
    if not data or "text" not in data:
        return jsonify({"error": "missing text"}), 400
    text = data["text"]
    if not isinstance(text, str):
        return jsonify({"error": "text must be string"}), 400
    ...

Django REST Framework: có Serializer với validation đầy đủ, nhưng phải khai báo tường minh hơn và verbose hơn so với Pydantic type annotation:

# DRF: serializer riêng, khai báo lại từng field
from rest_framework import serializers, views, response

class PredictSerializer(serializers.Serializer):
    text = serializers.CharField()
    max_tokens = serializers.IntegerField(default=256)

class PredictView(views.APIView):
    def post(self, request):
        s = PredictSerializer(data=request.data)
        s.is_valid(raise_exception=True)
        ...
5

OpenAPI / Swagger tự động

FastAPI sinh OpenAPI 3.x schema từ code và cung cấp hai UI mặc định:

  • /docs — Swagger UI, giao diện test endpoint trực tiếp trên trình duyệt.
  • /redoc — ReDoc, tài liệu dạng đọc.
  • /openapi.json — raw JSON schema, dùng để generate client SDK hoặc import vào Postman.

Không cần cài thêm gì. Schema được cập nhật tự động khi thêm hoặc sửa endpoint.

Với Flask, để có Swagger UI phải cài thêm flasgger hoặc flask-smorest và viết docstring theo định dạng riêng. Với Django, drf-spectacular hoặc drf-yasg là phổ biến nhưng yêu cầu cấu hình và annotation thêm.

Đối với AI API, OpenAPI tự động có giá trị thực tế: team frontend có thể test endpoint inference mà không cần hỏi backend, QA có thể viết test case từ schema, và client SDK có thể sinh từ /openapi.json.

6

Performance: TechEmpower benchmark

TechEmpower Web Framework Benchmarks là bộ benchmark độc lập, đo throughput của framework qua nhiều test type. Từ Round 22 trở đi, FastAPI / Starlette nằm trong nhóm trên của các framework Python, đạt throughput tương đương Node.js Express ở các bài test JSON serialization và plaintext response.

Flask và Django chậm hơn khoảng 2–5 lần so với FastAPI/Starlette cho cùng workload JSON serialization trong benchmark này.

Quan trọng: con số TechEmpower đo framework overhead trong điều kiện lý tưởng (không có business logic). Trong AI application thực tế, bottleneck gần như luôn là inference time — thời gian model chạy — không phải framework. Một request gọi GPT-4o tốn 500ms–2s; framework overhead ở mức vài millisecond không phải yếu tố quyết định throughput.

Benchmark framework có giá trị hơn khi bạn chạy nhiều request nhỏ đồng thời và cần event loop xử lý I/O hiệu quả — trường hợp AI API gateway gom nhiều call về LLM.

7

Dependency injection với Depends()

FastAPI có hệ thống dependency injection tích hợp qua Depends(). Dependency được khai báo là function bình thường (hoặc async), và FastAPI tự resolve khi endpoint được gọi:

from fastapi import FastAPI, Depends, Header, HTTPException

app = FastAPI()

async def verify_api_key(x_api_key: str = Header(...)):
    if x_api_key != "secret":
        raise HTTPException(status_code=403, detail="Invalid API key")
    return x_api_key

@app.post("/predict")
async def predict(
    body: dict,
    api_key: str = Depends(verify_api_key),  # inject dependency
):
    ...

Dependency có thể lồng nhau (một dependency gọi dependency khác), và FastAPI cache kết quả trong scope của request. Điều này phù hợp để inject shared resource như database session, HTTP client pool, hoặc model instance đã load.

Flask không có dependency injection tích hợp — phải tự implement hoặc dùng thư viện như flask-injector. Django có DI ở mức nhất định qua middleware và class-based view, nhưng không linh hoạt bằng Depends() của FastAPI.

8

Khi nào FastAPI là lựa chọn phù hợp

FastAPI phù hợp khi:

  • Serve model PyTorch / Transformers / scikit-learn qua HTTP — bắt đầu từ API không có code legacy, không cần ORM phức tạp, không cần admin UI.
  • Streaming response cho LLM — FastAPI hỗ trợ StreamingResponse với async generator, phù hợp để stream token-by-token từ LLM API về client.
  • Tích hợp với async librarieshttpx (async HTTP client), asyncpg (PostgreSQL), motor (MongoDB async), redis-py async mode — tất cả đều work tốt trong FastAPI async context.
  • Team cần tài liệu API tự động — Swagger UI tại /docs không cần cấu hình thêm.
  • Project mới hoàn toàn — không bị ràng buộc bởi codebase cũ.
9

Khi nào nên cân nhắc Flask hoặc Django

FastAPI không phải lựa chọn duy nhất và đôi khi không phải lựa chọn tốt nhất:

Cân nhắc Django + DRF khi

  • Codebase Django đã có sẵn với auth, admin, ORM, permissions — thêm endpoint AI bằng DRF nhanh hơn nhiều so với migrate toàn bộ sang FastAPI.
  • Cần Django admin để quản lý dữ liệu training, label, feedback — không framework nào thay thế được Django admin sẵn có.
  • Team đã thạo Django và không có đủ thời gian học FastAPI + SQLAlchemy.

Cân nhắc Flask khi

  • Cần prototype nhanh với logic đơn giản, không cần validation phức tạp.
  • Team đã có Flask codebase ổn định và AI chỉ là một endpoint nhỏ thêm vào.
  • Tích hợp với hệ sinh thái Flask extension đặc thù (ví dụ một số extension Flask chưa có port sang FastAPI).

Ba framework đều deploy được trên cùng infrastructure (Docker, Kubernetes, cloud platform). Quyết định nên dựa trên context codebase và team, không phải trên lý thuyết "framework nào tốt hơn cho AI".

10

Giới hạn của FastAPI

FastAPI không giải quyết tất cả:

Không có ORM tích hợp

FastAPI không đi kèm ORM. Bạn phải tự chọn: SQLAlchemy (async với asyncpg), Tortoise ORM, hoặc SQLModel (do tác giả FastAPI viết, dùng Pydantic + SQLAlchemy). Điều này cho phép linh hoạt hơn, nhưng cũng có nghĩa là phải tự thiết lập.

Không có admin UI

Không có gì tương đương Django admin sẵn có. Có thư viện bên thứ ba như sqladmin (SQLAlchemy-based), nhưng phải cài đặt và cấu hình riêng.

Ecosystem plugin nhỏ hơn

Flask và Django có hàng nghìn extension tích hợp sẵn (rate limiting, OAuth, session, cache). FastAPI có middleware Starlette và một số package bên thứ ba, nhưng lựa chọn ít hơn và một số chưa có documentation tốt.

Async code dễ deadlock khi lẫn sync blocking

Đây là gotcha thường gặp nhất. Nếu gọi một hàm blocking (ví dụ model.predict() là synchronous PyTorch call, hoặc time.sleep()) trực tiếp bên trong async def endpoint mà không dùng asyncio.run_in_executor(), event loop sẽ bị block và các request khác phải chờ. FastAPI có run_in_threadpool utility để xử lý trường hợp này — sẽ được đề cập ở Bài 4.

# WRONG: blocking call trực tiếp trong async def
@app.post("/predict")
async def predict(body: PredictRequest):
    result = model.predict(body.text)  # sync blocking, block event loop!
    return result

# CORRECT: chạy blocking code trong thread pool
import asyncio
from fastapi.concurrency import run_in_threadpool

@app.post("/predict")
async def predict(body: PredictRequest):
    result = await run_in_threadpool(model.predict, body.text)
    return result
11

Tóm tắt so sánh

Tiêu chí FastAPI Flask Django + DRF
Server protocol ASGI (asyncio native) WSGI (async qua thread pool) WSGI + ASGI từ 3.0 (ORM vẫn sync)
Validation input Pydantic v2, tự động từ type hint Marshmallow / thủ công DRF Serializer (verbose hơn)
OpenAPI schema Tự sinh, /docs và /redoc sẵn Cần flasgger / flask-smorest Cần drf-spectacular / drf-yasg
Dependency injection Depends(), tích hợp sẵn Không có, tự implement Middleware, CBV (ít linh hoạt hơn)
ORM Không có, tự chọn Không có, tự chọn Django ORM sẵn có
Admin UI Không có, cần sqladmin Flask-Admin (bên thứ ba) Django Admin sẵn có
Streaming response StreamingResponse + async generator Generator response, không native async StreamingHttpResponse, không native async
Ecosystem Starlette middleware, đang tăng Lớn, trưởng thành Rất lớn, batteries included
12

Bài tiếp theo

Bài 2: Cài đặt FastAPI và endpoint đầu tiên — cài đặt môi trường, viết và chạy endpoint inference đầu tiên với Uvicorn.