# FastAPI: biến model của khách thành API có hợp đồng rõ ràng ngay trong một buổi làm việc

> Ở site khách, thứ thuyết phục nhất thường không phải slide mà là một đường link /docs khách bấm thử được ngay, và FastAPI sinh ra đường link đó từ chính code bạn viết.

Bản gốc: https://fdetimes.net/vi/cong-cu/fastapi-boc-mo-hinh-va-du-lieu-khach-thanh-api/

Buổi chiều thứ ba ở site khách, data scientist bên họ đưa bạn một file notebook có model dự đoán đơn hàng giao trễ. Họ hỏi: "Team app có gọi được cái này không?" Câu trả lời tốt nhất trước giờ về là một đường link khách tự bấm thử, có schema rõ ràng và có test đi kèm.

FastAPI được làm ra cho đúng khoảnh khắc đó. Tài liệu chính thức định nghĩa nó là web framework Python để xây API, dựa trên type hint chuẩn của Python. Nó mã nguồn mở theo giấy phép MIT, repo chính thức nằm ở github.com/fastapi/fastapi.

Với một FDE, giá trị nằm ở chỗ một đoạn code sinh ra ba thứ cùng lúc: hợp đồng dữ liệu, tài liệu tương tác và một API test được ngay. Đó là ba thứ khách cần thấy trước khi tin rằng bản demo của bạn có thể đi tiếp vào production.

## Vì sao viết code là có luôn tài liệu?

FastAPI xây trên hai chuẩn mở là OpenAPI (trước đây gọi là Swagger) và JSON Schema. Bạn khai báo kiểu dữ liệu bằng type hint và model Pydantic, framework dịch chúng sang schema. Không có bước "viết tài liệu" riêng để rồi quên cập nhật.

Hệ quả cụ thể: chạy app lên là có trang tài liệu tương tác do Swagger UI cung cấp tại /docs, không cần cấu hình gì thêm. Song song đó, toàn bộ mô tả API nằm ở /openapi.json. Team app của khách có thể dùng file này để sinh SDK hoặc kiểm tra hợp đồng mà không phải đọc code của bạn.

Xuất xứ của công cụ cũng khớp với việc bọc model. Sebastián Ramírez (@tiangolo), người tạo ra FastAPI và cả Typer, SQLModel, kể rằng ông đã xây API với yêu cầu phức tạp nhiều năm, gồm machine learning, hệ phân tán và job bất đồng bộ. FastAPI được xây trên nền Pydantic và Starlette.

## Một endpoint bọc model, từng dòng một

Thử hình dung model giao trễ ở trên. Bảng dữ liệu nội bộ của khách có cả số điện thoại người nhận, thứ tuyệt đối không được lọt sang app bên ngoài. Endpoint tối thiểu trông như sau, với `score_late` là hàm giả định gọi model của khách:

```python
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class ShipmentIn(BaseModel):
    shipment_id: str
    distance_km: float
    warehouse_id: str

class Prediction(BaseModel):
    shipment_id: str
    late_probability: float

@app.post("/predict", response_model=Prediction)
def predict(shipment: ShipmentIn):
    row = load_shipment(shipment.shipment_id)  # includes customer_phone
    return {
        "shipment_id": shipment.shipment_id,
        "late_probability": score_late(shipment),
        "customer_phone": row["customer_phone"],
    }
```

Hàm trả về một dict ba trường, nhưng `Prediction` chỉ khai báo hai. Theo tài liệu, FastAPI dùng Pydantic để lọc bỏ mọi dữ liệu không có trong output model, nên `customer_phone` không bao giờ rời server. Đây chính là cơ chế bạn cần khi bọc dữ liệu thật của khách: một lỗi sơ ý trong handler không biến thành sự cố rò rỉ.

Ở chiều kia, FastAPI cũng kiểm tra chặt. Nếu handler trả về thứ không khớp `Prediction`, chẳng hạn thiếu `late_probability`, FastAPI trả lỗi server thay vì gửi dữ liệu sai đi. Response model còn gắn JSON Schema của đầu ra vào OpenAPI, nên trang /docs mô tả đúng những gì team app sẽ nhận.

**Điểm mấu chốt:** Một API tốt cho khách thà báo lỗi rõ ràng còn hơn âm thầm trả dữ liệu sai.

## Test trong cùng buổi, không cần dựng server

Bản demo chỉ đáng tin khi có test, và đây là chỗ FastAPI giữ chi phí thấp. Bạn chỉ cần cài thêm httpx rồi dùng pytest. `TestClient` xây trên HTTPX nên test chạy ngay trong tiến trình, không phải khởi động server hay mở cổng.

```python
from fastapi.testclient import TestClient
from app import app

client = TestClient(app)

def test_phone_never_leaks():
    r = client.post("/predict", json={
        "shipment_id": "S-001",
        "distance_km": 12.5,
        "warehouse_id": "HCM-1",
    })
    assert r.status_code == 200
    assert "customer_phone" not in r.json()
```

Hàm test là `def` thường, không phải `async def`, kể cả khi endpoint là async. Điều đó giúp bạn viết test ngay trong buổi làm việc, thậm chí ngồi cùng kỹ sư bên khách. Test này nên là thứ đầu tiên bạn viết: nó biến lời hứa "không lộ dữ liệu cá nhân" thành một dòng assert ai cũng đọc được.

## Hai lỗi dễ mắc khi bọc dữ liệu thật

Cơ chế lọc ở trên dựa vào output model; không có model đó thì cũng không còn danh sách trường nào để FastAPI đối chiếu. Hậu quả dễ suy ra: handler trả gì, client có nguy cơ nhận nấy, kể cả `customer_phone`.

Lỗi thứ hai là trả thẳng object ORM hay nguyên một dòng lấy từ bảng của khách. Hôm nay bảng có năm cột vô hại, tháng sau team dữ liệu thêm cột thông tin cá nhân, và nếu không có lớp lọc thì cột mới đi theo ra ngoài mà không ai hay.

Thói quen an toàn là luôn đi qua một model đầu ra riêng, kể cả khi nó trùng gần hết với bảng. Rồi giữ test kiểu `test_phone_never_leaks` trong bộ test, để nếu ai đó lỡ xoá `response_model`, test đỏ trước khi khách nhìn thấy dữ liệu lọt ra.

## Giới hạn cần nói thẳng với khách

FastAPI vẫn chưa lên 1.0. Trang release notes chính thức ghi bản 0.157.0 ngày 2026-10-21, cho thấy dự án vẫn ra bản mới nhưng còn ở giai đoạn 0.x. Lời khuyên thực tế là ghim chính xác phiên bản trong file dependency của mọi bản demo bàn giao, để vài tháng sau khách cài lại vẫn chạy y nguyên.

Giới hạn thứ hai là về phạm vi. FastAPI lo lớp hợp đồng và giao tiếp HTTP, chứ không làm model của khách nhanh hơn hay đúng hơn. Nếu model mất vài giây cho mỗi dự đoán, endpoint đẹp đến mấy cũng chậm chừng đó, và bạn vẫn phải tự giải quyết chuyện vận hành ở môi trường của khách.

## Học gì trước, và ghi gì vào CV

Thứ tự hợp lý là type hint và model Pydantic trước, vì mọi thứ còn lại sinh ra từ đó. Tiếp theo là `response_model`, rồi `TestClient`. Ba mảnh này đủ để bạn đi từ notebook đến API có tài liệu và test trong một buổi chiều.

Nếu một mô tả việc FDE nhắc đến dựng prototype hay tích hợp với hệ thống của khách, đây là kỹ năng đáng đem ra kể. Trên CV, đừng chỉ ghi "FastAPI" trong danh sách kỹ năng.

Một dòng kiểu "bọc model dự đoán của khách thành API có schema OpenAPI, test tự động chặn rò rỉ dữ liệu cá nhân, bàn giao trong một ngày" kể được cả kỹ thuật lẫn cách bạn làm việc với khách.

Lần tới có người hỏi "team app có gọi được cái này không", câu trả lời của bạn nên là một đường link /docs và một test xanh.

**Thử ngay tuần này:**

- Lấy một hàm Python bạn đang có (model, truy vấn SQL), bọc nó thành một endpoint FastAPI và mở /docs để gọi thử.
- Thêm một trường giả kiểu customer_phone vào dữ liệu trả về, khai báo response_model không có trường đó, rồi viết test khẳng định nó không lọt ra ngoài.
- Gửi file /openapi.json của endpoint đó cho một đồng nghiệp và nhờ họ gọi API mà không hỏi bạn câu nào.

## Nguồn

- [FastAPI (official documentation)](https://fastapi.tiangolo.com/)

- [First Steps - FastAPI](https://fastapi.tiangolo.com/tutorial/first-steps/)

- [Response Model - Return Type - FastAPI](https://fastapi.tiangolo.com/tutorial/response-model/)

- [Testing - FastAPI](https://fastapi.tiangolo.com/tutorial/testing/)

- [Sebastián Ramírez (tiangolo.com)](https://tiangolo.com)

- [History, Design and Future - FastAPI](https://fastapi.tiangolo.com/history-design-future/)

- [GitHub - fastapi/fastapi](https://github.com/fastapi/fastapi)

- [Release Notes - FastAPI](https://fastapi.tiangolo.com/release-notes/)
