构建 GraphQL 视频发现 API
当我们的视频数据库增长到数十万条记录时,我们的 API 成为了瓶颈。
单个页面需要五次不同的 REST 调用才能显示热门视频、频道详情、标签、播放量和推荐内容。在网络连接较慢的情况下,移动端用户仅加载一个页面就必须发送 30 个请求。
GraphQL 解决了这个问题。客户端只需通过一个请求即可获取其所需的精确数据。
我使用 Strawberry 和 FastAPI 构建了一个生产级别的视频发现 API。以下是我的实现过程。
为什么选择 Strawberry?
我测试了 Graphene 和 Ariadne,但 Strawberry 凭借以下原因脱颖而出:
• 类型提示即 Schema。你编写 Python 代码,Strawberry 会自动构建 GraphQL 类型,无需手动同步。 • 原生支持异步。这使得 API 能够处理大量请求而不会阻塞事件循环。 • 与 FastAPI 无缝集成。GraphQL 端点可以直接与现有的 REST 路由并存。 • 内置 DataLoader。该工具开箱即用,能够解决 N+1 问题。
数据栈
数据存储在由 PHP 网站管理的 SQLite 数据库中。我使用 SQLite FTS5 来实现快速全文搜索。
Python 服务将该数据库作为只读层进行读取。这确保了只有一个单一事实来源(Single Source of Truth)。
为了提升搜索体验,我将 FTS5 的相关性评分与流行度信号相结合。这可以防止单个爆火视频淹没所有其他搜索结果。
解决 N+1 问题
GraphQL 中的一个常见陷阱是 N+1 问题。如果你获取了 20 个视频,然后又为每个视频获取其频道信息,一个简单的 API 将会执行 21 次数据库查询。
DataLoader 通过批处理来解决这个问题。它在一个周期内收集所有频道 ID,并运行单个查询来获取它们。这使我们的查询时间从 40ms 降至 5ms 以下。
关键设计选择
• 不透明 ID (Opaque IDs):我使用字符串作为 ID。这允许在不破坏客户端应用的情况下进行未来的更改。 • 有意的可空性 (Intentional Nullability):我定义了哪些字段可以为空。这有助于客户端处理缺失数据而不会导致崩溃。 • 只读 Schema:GraphQL API 不处理写入操作。这消除了许多安全方面的烦恼。 • 边缘缓存 (Edge Caching):GraphQL 默认使用 POST 请求,这很难进行缓存。我使用自动持久化查询 (APQ) 来实现通过 Cloudflare 进行缓存。
如果你正苦于自定义端点过多或查询参数过于复杂,这个技术栈是一个非常好的选择。
