Litestar vs FastAPI:一場被低估的 Python Web 框架之爭
如果你在 2024 年問一個 Python 開發者「要寫 API 該用什麼框架」,十個有九個會回答 FastAPI。這個答案在多數場景下沒錯——FastAPI 的生態系、文件品質、社群規模都是壓倒性的優勢。
但如果你再問一個問題:「你有看過 Litestar 嗎?」
多數人會搖頭。少數聽過的人可能知道它「以前叫 Starlite」,然後就沒了。這篇文章要做的,就是把 Litestar 攤開來,從原始碼層級告訴你它和 FastAPI 的差異在哪裡,以及為什麼它值得你花時間認真評估。
為什麼現在寫這篇
市面上已經有不少 Litestar vs FastAPI 的比較文章。但絕大多數都停留在「列功能表」的層次——誰有 CSRF middleware、誰有 rate limiting,然後得出一個安全的結論:「FastAPI 適合快速開發,Litestar 適合追求性能」。
這種結論幾乎沒有資訊量。
我花了時間把 Litestar 的原始碼(v3.0.0b0,約 46,000 行 Python)完整讀過一遍,也對照了 FastAPI 的架構。這篇文章會從以下幾個角度切入:
- 路由引擎的資料結構差異(radix trie vs 線性掃描)
- 依賴注入的拓撲排序與平行解析
- 驗證層的根本性架構差異(msgspec vs Pydantic)
- DTO 系統——Litestar 最被低估的生產力工具
- Controller 模式——Django 開發者會覺得回家的設計
- 測試——沒有任何一篇比較文章認真討論過的主題
- 社群真實聲音——來自 Hacker News 和 Reddit 的討論
歷史脈絡:從 Starlite 到 Litestar
這段歷史對理解兩個框架的設計哲學至關重要。
Litestar 的前身叫 Starlite,最初就是一個「更好的 FastAPI」計畫。創始團隊認為 FastAPI 的某些設計決策——特別是對 Starlette 的深度耦合和 Pydantic 的綁定——限制了框架的天花板。
2023 年,專案更名為 Litestar,不只是換名字,而是宣示要走出 FastAPI 的影子,成為獨立的框架。到了 2026 年的 v3.0.0 beta,你可以看到這個野心的成果:它不再只是「FastAPI 的替代方案」,而是一個有自己完整設計語言的框架。
路由引擎:Radix Trie vs 線性匹配
這是第一個值得深入的技術差異。
FastAPI 的路由:Starlette 的線性掃描
FastAPI 的路由完全委託給 Starlette。Starlette 的 Router 維護一個路由列表,每次請求進來時,從頭到尾逐一比對,直到找到匹配的路由。
# Starlette 內部邏輯(簡化)
for route in self.routes:
match, child_scope = route.matches(scope)
if match == Match.FULL:
return child_scope
這意味著路由查找的時間複雜度是 O(n),其中 n 是註冊的路由數量。對於 10-20 條路由的小專案,這完全不是問題。但當你的 API 成長到 200、500 條路由時,每個請求都要做線性掃描就開始成為可量測的開銷。
Litestar 的路由:Radix Trie(基數字典樹)
Litestar 在 litestar/_asgi/routing_trie/ 實作了完整的 radix trie。讓我們看看它是怎麼運作的:
# litestar/_asgi/routing_trie/types.py
@dataclass
class RouteTrieNode:
children: dict[str | PathParameterSentinel, RouteTrieNode]
path_parameters: set[PathParameterDefinition]
asgi_handlers: dict[str, ASGIHandlerTuple] # method -> handler
is_mount: bool
is_static: bool
路由在啟動時透過 add_route_to_trie() 一次性建構成樹狀結構。請求進來時,traverse_route_map() 將路徑按 / 分割,沿著樹的分支往下走:
# 簡化的遍歷邏輯
def traverse_route_map(path: str, trie: RouteTrieNode):
current = trie
for segment in path.split("/"):
if segment in current.children:
current = current.children[segment]
elif PathParameterSentinel in current.children:
current = current.children[PathParameterSentinel]
# 提取路徑參數
時間複雜度是 O(k),其中 k 是路徑的段數(通常 2-5),和總路由數完全無關。
這在實務上有差嗎?
對小專案:幾乎沒差。路由查找在整個請求生命週期中佔的比例微乎其微。
對大型 API(數百條路由):有可量測的差異。更重要的是,radix trie 的查找時間是恆定的——不管你的 API 從 50 條路由成長到 500 條,路由查找的延遲不會增加。這對可預測性很重要。
James Bennett 在他的文章中提出了一個精闢的觀點:框架的擴展有兩個維度——scale up(處理更多流量)和 scale out(處理更大的程式碼庫)。Radix trie 同時改善了這兩個維度。
依賴注入:設計哲學的分歧點
這是兩個框架最根本的設計差異之一,也是最常被膚淺比較的主題。
FastAPI:Depends() 行內宣告
from fastapi import Depends
async def get_db():
db = Database()
try:
yield db
finally:
await db.close()
@app.get("/users")
async def get_users(db: Database = Depends(get_db)):
return await db.fetch_users()
FastAPI 的 DI 是行內宣告的——你在函式簽名裡用 Depends() 指定依賴。這很直覺,但有幾個根本性的限制:
- 沒有真正的 DI 容器:每個依賴都是獨立解析的,沒有全域的依賴圖概念
- 循環引用問題:當你的
Depends(get_db)散佈在幾十個檔案中,Python 的 import 機制會開始出問題。James Bennett 和多位社群開發者都報告過這個痛點 - 解析是順序的:FastAPI 的依賴解析本質上是順序執行的
Litestar:Provide() 階層式宣告
from litestar import Litestar, get, Controller
from litestar.di import Provide
async def get_db() -> AsyncGenerator[Database, None]:
db = Database()
try:
yield db
finally:
await db.close()
async def get_cache() -> Cache:
return Cache()
class UserController(Controller):
path = "/users"
dependencies = {"repo": Provide(get_user_repo)}
@get()
async def list_users(self, db: Database, repo: UserRepo) -> list[User]:
return await repo.list(db)
app = Litestar(
route_handlers=[UserController],
dependencies={
"db": Provide(get_db),
"cache": Provide(get_cache),
}
)
這裡的核心差異不只是語法,而是設計模型:
- 階層繼承:依賴可以在 App → Router → Controller → Handler 任何一層宣告,子層自動繼承父層的依賴
- 名稱綁定:依賴透過名稱(字典的 key)綁定到參數,不是透過型別或行內標注
- 解耦宣告與使用:你在 handler 裡只寫
db: Database,不需要知道它是怎麼來的
平行依賴解析:拓撲排序
這是最值得深入的技術細節。在 litestar/_kwargs/dependencies.py 中,Litestar 實作了一個真正的拓撲排序演算法來建立依賴解析批次:
def create_dependency_batches(dependencies: dict) -> list[set[str]]:
"""將依賴圖排序為可平行解析的批次"""
# 1. 遞迴建立依賴圖
# 2. 找出所有沒有子依賴的節點 → 第一批
# 3. 移除已解析的批次,重複
# 結果:每個 set 裡的依賴可以同時解析
接著在 KwargsModel 中,每個批次使用 anyio.create_task_group 同時解析:
async with create_task_group() as task_group:
for dependency in batch:
task_group.start_soon(resolve_dependency, dependency)
這意味著如果你有三個互不依賴的資料來源(資料庫、快取、外部 API),Litestar 會同時發起這三個請求,而不是一個接一個。
在實際生產環境中,假設每個依賴的 I/O 延遲是 5ms,三個依賴:
- FastAPI:15ms(順序)
- Litestar:5ms(平行)
這個差異在高併發場景下會被放大。
驗證層:msgspec vs Pydantic
這是 Litestar 最具爭議性、也最具技術深度的選擇。
FastAPI:Pydantic 是唯一選項
FastAPI 和 Pydantic 是深度耦合的。你的請求驗證、回應序列化、OpenAPI schema 生成全部依賴 Pydantic。這不是可以輕易替換的——它是 FastAPI 的核心假設。
Pydantic v2 確實比 v1 快了很多(底層用 Rust 重寫),但它仍然是一個通用型的資料驗證庫,不是專門為 web 框架的請求/回應週期優化的。
Litestar:msgspec 為核心,Pydantic 為選項
Litestar 做了一個大膽的選擇:用 msgspec 作為核心驗證和序列化引擎。
msgspec 是什麼?它是一個用 C 實作的高性能序列化/驗證庫,支援 JSON、MessagePack、TOML、YAML。在基準測試中:
- JSON 序列化比 Pydantic v2 快 3-6 倍
- JSON 反序列化比 Pydantic v2 快 2-4 倍
- 記憶體使用量更低
但真正有趣的是 Litestar 怎麼用它。在 litestar/_signature/model.py 中:
class SignatureModel:
"""在啟動時動態建立 msgspec.Struct 子類別"""
@classmethod
def create(cls, fn, ...) -> type[SignatureModel]:
# 解析函式簽名
# 為每個參數建立 msgspec 欄位定義
# 用 defstruct() 動態生成 Struct 子類別
struct_type = defstruct(
name=f"SignatureModel_{fn.__name__}",
fields=fields,
)
return struct_type
這段程式碼在應用啟動時就為每個 handler 預先編譯好了驗證邏輯。請求進來時,驗證的開銷趨近於零——因為所有的型別檢查邏輯在啟動階段就已經被編譯成了最佳化的 C 程式碼。
多後端支援:真正的選擇自由
但 Litestar 的野心不止於此。它支援四種驗證後端:
| 後端 | 使用方式 | 適用場景 |
|---|---|---|
| msgspec | 預設,零設定 | 追求性能 |
| Pydantic v2 | PydanticPlugin | 現有 Pydantic 模型遷移 |
| attrs | AttrsSchemaPlugin | 已有 attrs 程式碼 |
| dataclasses | 內建支援 | 標準庫優先 |
你可以在同一個應用中混用不同的驗證後端。你的 domain model 用 dataclasses,API 層用 msgspec,歷史遺留的模組用 Pydantic——Litestar 都能處理。
FastAPI 做不到這一點。如果你的 domain model 不是 Pydantic BaseModel,你必須手動轉換。
DTO 系統:Litestar 最被低估的功能
如果你只記住這篇文章的一個重點,應該是這個。
FastAPI 的痛點:Model 爆炸
用 FastAPI 做 CRUD API 時,你需要為每個實體手動建立多個 Pydantic model:
# FastAPI 的常見模式
class UserBase(BaseModel):
name: str
email: str
class UserCreate(UserBase):
password: str
class UserRead(UserBase):
id: int
created_at: datetime
class UserUpdate(BaseModel):
name: str | None = None
email: str | None = None
一個實體,四個 model。十個實體,四十個 model。而且它們之間的同步是手動的——當你在 User ORM model 加了一個欄位,你需要記得更新所有相關的 Pydantic model。
Litestar 的 DTO:從 ORM 自動生成
from litestar.dto import DTOConfig, DataclassDTO
from litestar.contrib.sqlalchemy.dto import SQLAlchemyDTO
class UserDTO(SQLAlchemyDTO[User]):
config = DTOConfig(
exclude={"password", "internal_notes"},
rename_strategy="camel",
max_nested_depth=2,
)
class UserCreateDTO(SQLAlchemyDTO[User]):
config = DTOConfig(
include={"name", "email", "password"},
)
class UserUpdateDTO(SQLAlchemyDTO[User]):
config = DTOConfig(
include={"name", "email"},
partial=True, # 所有欄位自動變為 Optional
)
@post("/users", dto=UserCreateDTO, return_dto=UserDTO)
async def create_user(data: User) -> User:
# data 已經是驗證過的 User 實例
# 回傳時自動套用 UserDTO 的過濾和重命名
return await repo.create(data)
注意幾個關鍵差異:
- 單一真相來源:所有 DTO 都從同一個
Usermodel 生成,不需要手動同步 - 宣告式設定:
exclude、include、partial、rename_strategy都是設定,不是程式碼 - 輸入/輸出分離:
dto控制輸入驗證,return_dto控制輸出格式 - 自動 partial:
partial=True讓所有欄位變成 Optional,不需要手動寫UserUpdate - Code generation 後端:實驗性的
experimental_codegen_backend=True用程式碼生成進一步加速
James Bennett 在他的文章中稱 DTO 系統是他最想看到其他框架採用的功能。我完全同意。這是真正的生產力提升,不是語法糖。
Controller 模式:給 Django 開發者的回家之路
FastAPI 的理念是函式優先——每個 handler 是一個獨立的函式,用 APIRouter 分組。這對小專案很好,但當 API 成長時,你會遇到一些問題:
- 共享狀態難以管理:同一組 handler 需要共享的依賴、guard、middleware 只能透過重複宣告或全域變數
- 組織困難:200 個函式散落在各處,沒有自然的分組機制
- 重構成本高:想把一組 handler 從一個 router 移到另一個,需要逐一修改
Litestar 的 Controller 解決了這些問題:
from litestar import Controller, get, post, put, delete
from litestar.di import Provide
class ArticleController(Controller):
path = "/articles"
dependencies = {"repo": Provide(get_article_repo)}
guards = [require_auth]
tags = ["articles"]
@get()
async def list_articles(self, repo: ArticleRepo) -> list[Article]:
return await repo.list()
@get("/{article_id:int}")
async def get_article(self, repo: ArticleRepo, article_id: int) -> Article:
return await repo.get(article_id)
@post()
async def create_article(self, repo: ArticleRepo, data: Article) -> Article:
return await repo.create(data)
@put("/{article_id:int}")
async def update_article(
self, repo: ArticleRepo, article_id: int, data: Article
) -> Article:
return await repo.update(article_id, data)
@delete("/{article_id:int}")
async def delete_article(self, repo: ArticleRepo, article_id: int) -> None:
await repo.delete(article_id)
Controller 的設計不是 FastAPI 的 APIRouter 加上 class 包裝而已。它是 Litestar 階層配置模型的一部分:
dependencies在 Controller 層宣告,所有 handler 自動繼承guards套用到整個 Controllertags自動套用到 OpenAPI 文件dto/return_dto可以在 Controller 層設定預設值
如果你從 Django 的 ViewSet 或 Flask 的 MethodView 過來,這個模型會讓你覺得回家了。
測試:被所有比較文章忽略的主題
我檢查了市面上 10 篇 Litestar vs FastAPI 的文章,沒有任何一篇認真討論測試。這讓人不解,因為測試體驗是框架選擇中最重要的因素之一。
FastAPI 的測試
FastAPI 使用 Starlette 的 TestClient(基於 httpx):
from fastapi.testclient import TestClient
def test_read_users():
client = TestClient(app)
response = client.get("/users")
assert response.status_code == 200
# 依賴覆寫
def test_with_mock_db():
def mock_get_db():
return FakeDB()
app.dependency_overrides[get_db] = mock_get_db
client = TestClient(app)
response = client.get("/users")
# 記得清理
app.dependency_overrides.clear()
dependency_overrides 是一個全域字典,這意味著:
- 它不是 thread-safe 的(平行跑測試要小心)
- 你需要手動清理
- 覆寫粒度只有「這個函式 → 那個函式」
Litestar 的測試
Litestar 提供了專門建構的測試工具:
from litestar.testing import create_test_client
def test_list_users():
with create_test_client(
route_handlers=[UserController],
dependencies={"db": Provide(lambda: FakeDB())},
) as client:
response = client.get("/users")
assert response.status_code == 200
幾個關鍵差異:
create_test_client接受所有 Litestar 建構參數:你可以在測試中完整設定 app,包括 dependencies、middleware、guards、plugins。不需要 monkey-patch 全域狀態- 內建 WebSocket 測試:
WebSocketTestSession不需要額外安裝 - Session 資料輔助工具:
_get_session_data、_set_session_data用於測試 session 相關邏輯 - RequestFactory:建立 mock Request 物件做單元測試
- Out-of-process 測試:
subprocess_async_client用於端對端測試
最大的差異在於測試隔離。FastAPI 的 dependency_overrides 修改全域狀態,Litestar 的 create_test_client 創建完全獨立的 app 實例。這在 pytest-xdist 等平行測試場景下尤為重要。
內建功能矩陣:一張表說清楚
與其像其他文章一樣逐一列舉,不如看看全貌:
| 功能 | Litestar | FastAPI |
|---|---|---|
| CORS | 內建 | Via Starlette |
| CSRF 保護 | 內建 | 需第三方 |
| Rate Limiting | 內建(支援 Store 後端) | 需第三方(slowapi 等) |
| 壓縮(gzip/brotli/zstd) | 內建 | Via Starlette(僅 gzip) |
| Session 管理 | 內建(memory/file/Redis/Valkey) | Via Starlette(僅 cookie) |
| Response 快取 | 內建(支援 Store 後端) | 需第三方 |
| 認證框架 | 內建(JWT/Session/自訂) | 需第三方 |
| WebSocket Pub/Sub | 內建 Channels(Redis/Postgres/Memory) | 需自行實作 |
| CLI | 內建(litestar run、litestar routes) | 需第三方(uvicorn CLI) |
| Key-Value Store | 內建抽象層(Memory/File/Redis/Valkey) | 無 |
| MessagePack | 原生支援 | 需第三方 |
| SSE | 內建 Response 型別 | 需 sse-starlette |
| Repository Pattern | 內建 | 無 |
這張表帶出一個重要的架構哲學差異:
FastAPI 是「小核心 + 生態系」——框架本身只做路由、DI、驗證,其他都交給第三方。好處是靈活,壞處是你需要自己拼裝,而且每個第三方庫的品質、維護狀態、API 風格都不同。
Litestar 是「batteries included」——像 Django 一樣把常用功能內建。好處是統一的 API 風格和保證的相容性,壞處是框架本身更重、學習曲線更陡。
Guards 系統:宣告式授權
這個功能在現有文章中幾乎沒有被討論過,但它是 Litestar 在實際專案中非常實用的設計。
from litestar import ASGIConnection, BaseRouteHandler
from litestar.exceptions import NotAuthorizedException
async def require_admin(
connection: ASGIConnection, _: BaseRouteHandler
) -> None:
if not connection.user or not connection.user.is_admin:
raise NotAuthorizedException()
async def require_verified_email(
connection: ASGIConnection, _: BaseRouteHandler
) -> None:
if not connection.user.email_verified:
raise NotAuthorizedException(detail="Email not verified")
# 在任何層級套用
app = Litestar(
route_handlers=[...],
guards=[require_auth], # 全域:所有路由都需要認證
)
class AdminController(Controller):
path = "/admin"
guards = [require_admin] # Controller 層:所有 admin 路由需要管理員權限
@get("/settings")
async def get_settings(self) -> Settings: ...
@post("/dangerous-action", guards=[require_verified_email])
async def dangerous(self) -> None: # handler 層:額外要求 email 驗證
...
Guards 可以疊加——handler 層的 guard 不會覆蓋 controller 或 app 層的 guard,而是全部都要通過。這讓授權邏輯可以用組合的方式建構,而不是在每個 handler 裡寫 if-else。
在 FastAPI 中,你通常用 Depends() 實作類似的功能,但它混淆了「取得資料的依賴」和「檢查權限的守衛」這兩個不同的概念。
Channels:WebSocket Pub/Sub 的內建解答
如果你需要做即時通訊、通知推送、或任何 WebSocket 廣播功能,Litestar 的 Channels 系統是一個殺手級功能。
from litestar.channels import ChannelsPlugin
from litestar.channels.backends.redis import RedisChannelsBackend
channels = ChannelsPlugin(
backend=RedisChannelsBackend(url="redis://localhost"),
channels=["notifications", "chat"],
)
@post("/notify")
async def send_notification(
channels: ChannelsPlugin, data: Notification
) -> None:
await channels.publish(data.dict(), channels=["notifications"])
app = Litestar(
route_handlers=[send_notification],
plugins=[channels],
)
Channels 支援多種後端:
MemoryChannelsBackend:單進程,適合開發RedisChannelsBackend:Redis Pub/Sub,適合生產AsyncPgChannelsBackend:PostgreSQL LISTEN/NOTIFYPsycopgChannelsBackend:Psycopg3 後端
FastAPI 沒有任何對應的內建功能。你需要自己實作 WebSocket 管理、channel 訂閱、後端整合。
社群觀點:真實的聲音
Hacker News 的冷靜分析
在 HN 的 Litestar 討論串中,一位開發者提出了一個值得注意的觀點:
「Async Python 框架在高請求率下,會把 I/O-bound 的問題變成 CPU-bound 的問題。」
這不是在說 Litestar(或 FastAPI)的 async 不好,而是在提醒:Python 的 async 不是銀彈。GIL(即使在 Python 3.13 有了 free-threaded 模式)仍然是 CPU 密集型操作的瓶頸。當你的 API 在高併發下需要做大量 JSON 序列化/反序列化時,這就變成了 CPU 問題。
這恰好是 Litestar 選擇 msgspec(C 實作)的原因之一——它把序列化的 CPU 開銷降到最低。
社群常見的遷移動機
從各論壇整理的遷移原因:
從 FastAPI 遷移到 Litestar 的原因:
- 專案變大後,
Depends()的循環引用問題變得難以管理 - 需要 class-based views 來組織程式碼
- 想擺脫 Pydantic 的效能開銷(特別是高流量場景)
- 需要內建的 rate limiting 和 CSRF 保護
- FastAPI 的維護速度受質疑——核心開發者基本只有 Tiangolo 一人
留在 FastAPI 的原因:
- 生態系壓倒性優勢——幾乎所有的教程、範例、第三方整合都是 FastAPI
- 文件品質極高
- 招聘市場上「會 FastAPI」是常見需求,「會 Litestar」幾乎不存在
- Litestar 的文件和錯誤訊息仍有改善空間
- 不想冒險在小眾框架上——畢竟 FastAPI 背後有大量生產驗證
Litestar 團隊自己也在 HN 討論中承認文件是他們的「痛點」,正在積極改善。
Litestar v3:下一步往哪走
Litestar 3.0.0 目前是 beta 階段,帶來了幾個重要的方向性決策:
- 放棄 Python 3.9 和 3.10——最低要求 Python 3.11。這讓框架能用
ExceptionGroup、TaskGroup、更好的 typing 等現代 Python 功能 - 放棄 Pydantic v1 支援——全面轉向 Pydantic v2(如果你還在用 v1,這是個強訊號)
- 全新日誌系統——完整重寫,更符合結構化日誌的最佳實踐
- 新的
ASGIMiddleware基礎類別——用handle()方法取代舊的AbstractMiddleware,支援靜態路徑排除模式 - Python 3.14 官方支援
v3 的方向很明確:加碼 msgspec、擁抱現代 Python、簡化遺留相容層。如果你現在要開始一個新專案,選 Litestar 意味著你在押注一個正在往正確方向走的框架。
什麼時候選哪個
讓我給出一個比「FastAPI 適合快速開發」更有用的建議:
選 FastAPI,如果你:
- 團隊的 Python 經驗參差不齊——FastAPI 的學習曲線確實更平緩,文件也更好
- 需要大量第三方整合——OAuth providers、AI/ML 框架(LangChain、LlamaIndex 等)幾乎都優先支援 FastAPI
- 已經有大量 Pydantic models——遷移成本不值得
- 專案不大(< 50 條路由)——FastAPI 和 Litestar 在這個規模下的差異微乎其微
- 需要招人——「FastAPI experience」是可搜尋的關鍵字,「Litestar experience」不是
選 Litestar,如果你:
- 在建構一個會持續成長的 API——Controller、階層式 DI、Guard 這些設計在 200+ 路由的專案中會明顯感受到價值
- 對性能有硬指標——msgspec + radix trie + 平行依賴解析的組合在高併發場景下有實質的優勢
- 需要 WebSocket 即時功能——Channels 系統省去大量自建成本
- 從 Django 過來——Controller 模式、batteries included 的哲學會讓你覺得更熟悉
- 想要更好的測試體驗——
create_test_client的隔離性設計在大型專案中是真正的生產力提升 - 願意投資在一個較小的生態系——用更少的第三方依賴,換取更統一的框架體驗
不要選 Litestar,如果你:
- 只是因為它「更快」——沒有經過你自己業務場景的 benchmark,「3-6x」只是一個數字
- 覺得 FastAPI 不好——FastAPI 是一個傑出的框架,Litestar 是一個不同的傑出框架
- 團隊沒有餘裕學新工具——Litestar 的概念(DTO、Guard、Controller、Store)比 FastAPI 多,學習成本是真實的
結語
Python web 框架的格局正在改變。FastAPI 在 2020 年之後幾乎是唯一被認真討論的 async 選擇,但 Litestar 正在證明還有另一種路可以走。
這兩個框架之間的選擇不是「好 vs 壞」,而是設計哲學的分歧:
- FastAPI 選擇了簡約和生態系——核心小而精,讓社群填補其餘部分
- Litestar 選擇了完備和一致性——把 web 應用需要的功能內建,確保它們能無縫配合
對我來說,Litestar 最讓人印象深刻的不是任何單一功能,而是它在原始碼層級的設計品質。Radix trie 路由、拓撲排序的依賴解析、SignatureModel 的啟動時編譯——這些都不是「夠用就好」的實作,而是經過深思熟慮的工程決策。
如果你正在選框架,花一個下午的時間讀讀 Litestar 的原始碼。不是文件,是原始碼。你可能會對一個「小眾框架」的工程品質感到驚訝。