0

FastAPI: dùng Enum cho path parameter — endpoint chỉ nhận giá trị hợp lệ trong 5 phút

Nhiều API có path parameter chỉ nhận vài giá trị cố định — ví dụ trạng thái đơn hàng: pending, shipped, delivered. Thay vì nhận str rồi kiểm tra tay bằng if/else, hãy khai báo tham số dưới dạng Enum kế thừa str và Enum — FastAPI tự từ chối giá trị lạ bằng lỗi 422, tự sinh docs liệt kê các lựa chọn, và code endpoint không còn nhánh xử lý giá trị sai.

Vấn đề

Giả sử viết endpoint liệt kê đơn hàng theo trạng thái. Cách quen thuộc là nhận status kiểu str rồi rẽ nhánh từng giá trị:

@app.get("/orders/{status}")
def list_orders(status: str):
    if status == "pending":
        return {"orders": ["#1001", "#1002"]}
    elif status == "shipped":
        return {"orders": ["#1003"]}
    elif status == "delivered":
        return {"orders": ["#1004", "#1005"]}
    return {"error": "Trạng thái không hợp lệ"}

Cách này có 3 điểm yếu. Thứ nhất, giá trị lạ vẫn đi qua endpoint rồi mới bị từ chối, client nhận JSON lỗi tự chế thay vì lỗi chuẩn của framework. Thứ hai, docs tự sinh chỉ ghi status là chuỗi, người gọi API không biết các giá trị hợp lệ nếu không đọc code. Thứ ba, endpoint nào có tham số dạng danh mục cũng lặp lại khối if/else, càng về sau càng khó bảo trì.

Giải pháp

Khai báo Enum kế thừa str và Enum, rồi dùng nó làm kiểu của tham số:

from enum import Enum

from fastapi import FastAPI

class OrderStatus(str, Enum):
    PENDING = "pending"
    SHIPPED = "shipped"
    DELIVERED = "delivered"

app = FastAPI()

ORDERS = {
    "pending": ["#1001", "#1002"],
    "shipped": ["#1003"],
    "delivered": ["#1004", "#1005"],
}

@app.get("/orders/{status}")
def list_orders(status: OrderStatus):
    return {"status": status.value, "orders": ORDERS[status.value]}

Kế thừa str giúp giá trị Enum vẫn là chuỗi khi trả về JSON, còn kế thừa Enum cho FastAPI biết danh sách giá trị hợp lệ. Trong endpoint, status chắc chắn là 1 trong 3 giá trị nên dùng thẳng status.value mà không cần kiểm tra thêm.

Đã kiểm chứng bằng TestClient: GET /orders/pending trả 200 với đúng danh sách; GET /orders/cancelled trả 422 vì giá trị không nằm trong Enum; GET /orders/PENDING cũng trả 422 vì so sánh phân biệt hoa thường. Trang /docs hiển thị status dạng dropdown để người gọi chỉ việc chọn.

Áp dụng vào dự án của bạn

Mẫu này phù hợp với mọi tham số chỉ nhận tập giá trị cố định: trạng thái đơn hàng, loại tài khoản, ngôn ngữ giao diện, mức ưu tiên. Cùng một Enum dùng lại được cho query parameter và nhiều endpoint, thêm giá trị mới chỉ cần sửa một chỗ. Muốn mô tả rõ hơn trong docs, kết hợp thêm Path(description=…) cho tham số đó.

Câu hỏi thường gặp

Enum khác Literal thế nào?

Literal cũng giúp FastAPI từ chối giá trị lạ nhưng chỉ là gợi ý kiểu gắn tại chỗ. Enum là kiểu có tên: dùng lại được ở nhiều endpoint, có thể mang thêm phương thức, và hiển thị rõ trong docs như một schema riêng.

Dùng được Enum số nguyên không?

Được. Khai báo class Priority(int, Enum) với các giá trị 1, 2, 3 — FastAPI kiểm tra số nguyên và docs hiển thị kiểu số thay vì chuỗi.

Giá trị Enum có phân biệt hoa thường không?

Có. "PENDING" không khớp "pending" nên FastAPI trả 422. Muốn chấp nhận cả hai dạng, tự chuẩn hoá đầu vào trước khi so sánh hoặc định nghĩa thêm giá trị viết hoa trong Enum.

Enum dùng được cho query parameter không?

Được, cách khai báo giống hệt: def search(status: OrderStatus = Query(…)). FastAPI kiểm tra query parameter bằng cùng Enum và hiển thị danh sách lựa chọn trong docs.


Đọc bản tiếng Anh đầy đủ tại: https://linhfish.dev/en/2026/11/11/fastapi-use-enums-for-path-parameters-endpoints-that-only-accept-valid-values-in-5-minutes/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí