Nanobanana
1184 字
6 分鐘
比較多個主流 OpenAPI SDK Generator
前言
FastAPI 開發大量微服務,時常需要手(A)動(I)撰寫 SDK 給其他服務使用 平時公司內部 SDK 主要需求是 Python and TypeScript,
- Python 硬需求有: >=3.10、Pydantic>=2、httpx,支援 Sync and Async,uv python manager
- TypeScript: zod
聽聞 OpenAPITools/openapi-generator 很久,所以就上手測試了一下
受測者1: OpenAPI-Generator 上手測試
# 取得 FastAPI 的 openapi.json,請事先啟動 servercurl -O http://localhost:8000/openapi.json
# openapi generator 需要 JAVA runtime,直接上 docker# 預設沒 asyncdocker run --rm \ -v "${PWD}:/local" \ openapitools/openapi-generator-cli generate \ -i /local/openapi.json \ -g python \ -o /local/client-python-default \ --additional-properties=packageName=director_credit
# 支援 async 版本 (aiohttp)docker run --rm \ -v "${PWD}:/local" \ openapitools/openapi-generator-cli generate \ -i /local/openapi.json \ -g python \ -o /local/client-python-asyncio \ --additional-properties=packageName=director_credit,library=asyncio
# async - httpxdocker run --rm \ -v "${PWD}:/local" \ openapitools/openapi-generator-cli generate \ -i /local/openapi.json \ -g python \ -o /local/client-python-httpx \ --additional-properties=packageName=director_credit,library=httpx
# 其餘設定看這邊 https://openapi-generator.tech/docs/generators/python產出成果 - httpx
預設有 pydantic, docs and unittest,不錯
小可惜殘留了一點 poetry 2,但 pyproject.toml 遵循 PEP 621,所以也是可以直接用 uv 讀取並建立虛擬環境,這點影響不大
❯ tree ./client-python-httpx -I "__pycache__"./client-python-httpx├── README.md├── director_credit│ ├── __init__.py│ ├── api│ │ ├── __init__.py│ │ └── credit_api.py│ ├── api_client.py│ ├── api_response.py│ ├── configuration.py│ ├── exceptions.py│ ├── models│ │ ├── __init__.py│ │ ├── balance_response.py│ │ ├── deduct_request.py│ │ ├── expires_at.py│ │ ├── gift_credit_request.py│ │ ├── gift_credit_response.py│ │ ├── gift_type.py│ │ ├── http_validation_error.py│ │ ├── location_inner.py│ │ ├── refund_request.py│ │ ├── subscription_request.py│ │ ├── topup_request.py│ │ ├── transaction_response.py│ │ ├── transaction_type.py│ │ └── validation_error.py│ ├── py.typed│ └── rest.py├── docs│ ├── BalanceResponse.md│ ├── CreditApi.md│ ├── DeductRequest.md│ ├── ExpiresAt.md│ ├── GiftCreditRequest.md│ ├── GiftCreditResponse.md│ ├── GiftType.md│ ├── HTTPValidationError.md│ ├── LocationInner.md│ ├── RefundRequest.md│ ├── SubscriptionRequest.md│ ├── TopupRequest.md│ ├── TransactionResponse.md│ ├── TransactionType.md│ └── ValidationError.md├── git_push.sh├── pyproject.toml├── requirements.txt├── setup.cfg├── setup.py├── test│ ├── __init__.py│ ├── test_balance_response.py│ ├── test_credit_api.py│ ├── test_deduct_request.py│ ├── test_expires_at.py│ ├── test_gift_credit_request.py│ ├── test_gift_credit_response.py│ ├── test_gift_type.py│ ├── test_http_validation_error.py│ ├── test_location_inner.py│ ├── test_refund_request.py│ ├── test_subscription_request.py│ ├── test_topup_request.py│ ├── test_transaction_response.py│ ├── test_transaction_type.py│ └── test_validation_error.py├── test-requirements.txt├── tox.ini└── uv.lock
6 directories, 64 files受測者2: Stainless
Stainless and Speakeasy 算是第一梯隊,Fern 第二梯隊(不過他的 cli 體驗真的不賴) 這邊就只整理 Stainless 上來,因為他的比較能夠呈現商業化 SDK generator 目前的頂點,OpenAI 是使用他們家的
安裝跟處理方式照官網文件,生成很省心,直接 sync + async 都給了
Code quality 非常接近手寫的溫度,沒有模版冷冰冰的感覺,然後多了許多邊緣情境的處理 (Streaming, Retry and Pagination),難怪 OpenAI 使用
直接比較生成結果
Stainless
介面非常 Pythonic DX,攤平所有應傳的參數,這對使用 SDK 的用戶來說,非常舒服 Stainless 對 IDE 友善,request > transform
async def deduct( self, *, amount: int, user_id: str, idempotency_key: str, service_name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TransactionResponse:OpenAPI Generator (httpx)
非常 Pydantic,這點變得要使用者要先處理好 Pydantic Object instance,會顯得更加繁瑣
@validate_call async def deduct( self, idempotency_key: StrictStr, deduct_request: DeductRequest, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], Tuple[ Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)] ] ] = None, _request_auth: Optional[Dict[StrictStr, Any]] = None, _content_type: Optional[StrictStr] = None, _headers: Optional[Dict[StrictStr, Any]] = None, _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, ) -> TransactionResponse:接下來是 LLM 查看我測試的幾個解決方案的一些分析(僅供參考)
| 特性 | OpenAPI Generator (HTTPX) | Stainless | 觀點 |
|---|---|---|---|
| 模型純度 | 高 (標準 Pydantic) | 中 (含大量 Helper 邏輯) | 如果您需要將 Model 拿去其他地方用,OpenAPI Gen 的 Model 比較乾淨。 |
| 呼叫彈性 | 顯式 (with_http_info) | 隱式 (.with_raw_response) | 風格偏好不同,OpenAPI Gen 的方式比較傳統但明確。 |
| 依賴性 | 低 (僅 httpx, pydantic) | 高 (依賴 stainless core) | OpenAPI Gen 產出的碼大部分是自己包含的,不依賴像 stainless-python 這樣的外部 Runtime 套件 (除了 httpx/pydantic)。 |
🏆 現代 Python SDK 生成器終極比較
| 維度 | Stainless 👑 | Speakeasy 🥈 | Fern 🥉 | OpenAPI (HTTPX) | Liblab |
|---|---|---|---|---|---|
| 核心技術 | httpx | httpx | httpx | httpx | requests |
| Async 支援 | ✅ 原生 (True Async) | ✅ 原生 (True Async) | ✅ 原生 (True Async) | ✅ 原生 (True Async) | ❌ 模擬 (Thread) |
| Idempotency | ⭐ 自動注入 (UUID) | ⭐ 需寫 Hook | ⭐ 手動傳參 | ❌ 手動傳參 | ⭐ 需寫 Hook |
| Retry 機制 | ⭐ 極致 (Header感知) | ⭐ 需 Config | ⭐ 極致 (Header感知) | ❌ 無內建 | ⭐ 基本 (Chain) |
| 資安 (Auth) | ⭐⭐⭐⭐⭐ (直觀) | ⭐⭐⭐⭐⭐ (強型別) | ⭐⭐⭐⭐⭐ (直觀) | ⭐⭐⭐ (Setter) | ⭐⭐⭐ (Setter) |
| 程式碼風格 | 極簡、教科書級 | 標準、Pydantic | 極簡、Pythonic | 略顯繁瑣 | 複雜 (責任鏈) |
| 開源/商業 | 💰 商業付費 | 💰 商業 (有免費層) | 💰 商業 (有免費層) | 🆓 完全開源 | 💰 商業 |
總結
我之後應該會做的選擇
- OpenAPI Generator (HTTPX):適合喜歡 DIY。它是穩健且標準的開源選擇。
- Stainless / Speakeasy / Fern:適合 SDK 為產品的一部分的團隊。雖然有商業成本,但換來的是更佳的開發者體驗 (DX)。
比較多個主流 OpenAPI SDK Generator
https://geminixiang.xyz/posts/openapi-sdk-generator-comparison/