7.1 一个接口不够用了 转载

来源:https://github.com/datawhalechina/vibe-vibe

本节目标:理解当应用从"能跑"走向"能用"时,接口设计会遇到哪些真实问题,以及该怎么跟 AI 描述这些需求。

嵌套 vs 扁平:两种数据组织方式

假设前端需要电影信息和导演信息。后端可以用两种方式返回:

嵌套结构

把关联数据"嵌"在主对象里,一次返回所有信息:

嵌套响应长什么样?展开看看
{
  "id": 1,
  "title": "千与千寻",
  "year": 2001,
  "director": {
    "id": 5,
    "name": "宫崎骏",
    "nationality": "日本"
  },
  "tags": ["动画", "奇幻", "冒险"],
  "rating": {
    "average": 9.4,
    "count": 2156
  }
}

前端直接用 movie.director.name 就能拿到导演名字,用 movie.rating.average 就能拿到评分,不用再发第二个、第三个请求。所有数据一次到位。

扁平结构

只返回关联数据的 ID,前端需要的话自己再查:

扁平响应长什么样?展开看看
{
  "id": 1,
  "title": "千与千寻",
  "year": 2001,
  "directorId": 5,
  "tagIds": [1, 3, 7],
  "averageRating": 9.4
}

前端拿到 directorId: 5 后,如果要显示导演名字,还得再调 GET /api/directors/5。如果要显示标签名字,还得拿着 tagIds 去查标签表。

怎么选?

这不是非此即彼的选择,而是看场景:

场景 推荐结构 理由
电影详情页 嵌套 页面需要展示完整信息,一次查完省得前端多跑几趟
电影列表页 扁平(或轻度嵌套) 列表只需要标题、年份、海报,带上完整导演信息和所有标签是浪费带宽
管理后台的表格 扁平 表格每行只显示关键字段,点击某行再加载详情
搜索结果 扁平 + 少量嵌套 搜索结果需要显示标题和评分,但不需要完整的导演履历

一个实用的判断标准:前端拿到数据后,还需不需要再发请求才能渲染页面? 如果需要,说明你的接口返回的数据不够,应该嵌套更多关联数据。如果前端拿到数据就能直接渲染,说明刚刚好。

小明把电影详情接口改成了嵌套结构,一个请求返回所有信息。页面从"零件拼装"变成了"一次成型",加载速度快了,体验也好了。

跟 AI 说的时候,直接描述你的需求就行:

"电影详情接口需要同时返回导演信息、标签列表和平均评分,用嵌套结构,一个请求返回所有数据。电影列表接口只返回标题、年份、海报 URL 和平均评分。"

想按标签筛选、按评分排序

分页搞定了,小明又有了新需求。

他的朋友来试用,说:"你这个电影列表能不能只看动画片?我不想在 500 部电影里一页一页翻着找。"另一个朋友说:"能不能按评分从高到低排?我想看你评分最高的电影。"

小明纠结了:是给每种筛选条件都建一个新接口?GET /api/movies/animation 返回动画片,GET /api/movies/top-rated 返回高分电影?那如果又要按标签筛选又要按评分排序呢?再建一个 GET /api/movies/animation/top-rated?排列组合下来,接口数量会爆炸。

老师傅说:"不用建新接口。一个列表接口,用查询参数组合就行。"

查询参数的组合艺术

GET /api/movies?tag=动画&sort=rating&order=desc&page=1&limit=20

这一个请求就表达了:"给我标签是'动画'的电影,按评分从高到低排,第 1 页,每页 20 条。"

查询参数的好处是可以自由组合,就像乐高积木:

  • 只想排序不想筛选?GET /api/movies?sort=rating&order=desc
  • 只想筛选不想排序?GET /api/movies?tag=动画
  • 想同时按多个标签筛选?GET /api/movies?tag=动画&tag=日本
  • 什么都不传?GET /api/movies 返回默认排序的全部数据(带分页)

接口只有一个,前端根据用户的操作拼不同的参数就行。用户在筛选栏选了"动画",前端加上 tag=动画;用户点了"按评分排序",前端加上 sort=rating&order=desc。接口代码不用改,所有组合都自动支持。

常见的查询参数设计

参数 用途 示例
page / limit 分页 ?page=2&limit=20
sort / order 排序 ?sort=rating&order=desc
tag / genre 按分类筛选 ?tag=科幻
year 按年份筛选 ?year=2024?yearFrom=2020&yearTo=2024
q / search 关键词搜索 ?q=千与千寻
minRating 最低评分 ?minRating=8

这些参数都应该是可选的。不传就用默认值——默认不筛选、默认按创建时间倒序、默认第 1 页每页 20 条。这样既灵活又不会破坏已有的调用方式。

⚠️ 搜索和筛选是两回事

筛选(Filter) 是精确匹配——"标签等于动画"、"年份等于 2024"。搜索(Search) 是模糊匹配——"标题里包含'千与千寻'"。两者可以组合使用,但实现方式不同。筛选用数据库的 WHERE 条件就行,搜索可能需要全文索引。

对于小明的电影库,简单的 LIKE '%关键词%' 搜索就够了。如果数据量大到几十万条,可能需要 PostgreSQL 的全文搜索功能——但那是后面的事,先跑起来再优化。

跟 AI 说:

"电影列表接口支持以下可选查询参数:tag(按标签过滤)、year(按年份过滤)、q(按标题搜索)、sort(排序字段,支持 rating/year/createdAt)、order(asc 或 desc)。所有参数都是可选的,不传就返回默认排序的全部数据。"

跟 AI 沟通的 Prompt 模板

把上面学到的概念组合起来,你可以用一段话描述一个完整的接口需求:

从零设计接口时:

"帮我设计电影列表和详情两个接口。列表接口支持分页(offset/limit,默认每页 20 条)、按标签过滤、按评分或年份排序,响应里带上总数和总页数。详情接口返回电影信息,嵌套导演信息、标签列表和平均评分。统一用 { success, data, error } 格式返回。"

给已有接口加功能时:

"现有的 GET /api/movies 只返回全部数据,帮我加上分页和过滤功能。分页用 query 参数 page 和 limit,过滤支持按 tag 和 year 筛选,排序支持 sort 和 order 参数。所有新参数都是可选的——不传这些参数时行为跟之前一样,保持向后兼容。"

让 AI 自查时:

"检查一下现有的电影列表接口,有没有以下问题:1)是否支持分页?2)查询参数是否都做了类型校验(比如 page 必须是正整数)?3)排序字段是否限制了允许的值(防止用户传入任意列名)?"


ℹ️ 下一步

接口能查能筛了,但上线后会遇到新问题——有人提交空数据、重复点击、服务器突然 500。去 当接口出了问题 看看怎么应对。

最后编辑:Alex 2026-03-05 11:39:51