- 后端
- API设计
【免费下载链接】SpaceX-API
:rocket: Open Source REST API for SpaceX launch, rocket, core, capsule, starlink, launchpad, and landing pad data.
本文基于 SpaceX-API 开源仓库中的 launches/v4/query.md 文档,深入讲解如何通过POST /v4/launches/query端点对发射(Launch)数据进行 MongoDB 查询、条件过滤、排序分页与关联数据填充(populate)。读完后你将能够编写从简单到复杂的查询请求体、理解分页返回结构与关联字段的填充机制,并结合源码掌握其底层实现原理与缓存行为。
接口概览:方法、URL 与鉴权
/v4/launches/query是 SpaceX-API 中面向发射数据的高阶查询端点,与返回全部数据的GET /v4/launches不同,它允许开发者通过请求体(Body)自定义过滤、排序、分页与字段选择,避免全量拉取后在客户端再做筛选。
| 项目 | 值 |
|---|---|
| Method | POST |
| URL | https://api.spacexdata.com/v4/launches/query |
| Auth required | False |
| Body | JSON,包含query与options两个字段 |
请求体默认结构如下:
{ "query": {}, "options": {} }query:任何合法的 MongoDBfind()查询文档,用于过滤发射记录;options:分页与输出控制选项,由mongoose-paginate-v2插件解析。
在仓库源码中,该路由定义于 routes/launches/v4/index.js,其核心逻辑为:
router.post('/query', cache(20), async (ctx) => { const { query = {}, options = {} } = await transformQuery(ctx.request.body); try { const result = await Launch.paginate(query, options); ctx.status = 200; ctx.body = await transformResponse(result); } catch (error) { ctx.throw(400, error.message); } });可以看到:请求体经transformQuery预处理后,直接交给Launch.paginate(query, options)执行,因此query与options的语义完全对应mongoose-paginate-v2的参数约定。查询无需任何鉴权;仓库中auth中间件仅用于创建、更新、删除等破坏性路由(见 routes/launches/v4/index.js)。
关于完整的查询与分页语法,请参阅仓库根目录下的 docs/queries.md 通用指南,下文将结合发射数据的特有字段展开具体用法。
深入 query:基于 MongoDB find() 的过滤语法
query字段接受任意合法的 MongoDB 查询操作符,因此你可以在发射数据上使用全部标准查询操作符,例如:
- 比较操作符:
$eq、$ne、$gt、$gte、$lt、$lte、$in、$nin; - 逻辑操作符:
$and、$or、$not、$nor; - 元素与数组操作符:
$exists、$size、$all、$elemMatch; - 文本搜索:
$text(配合$search、$language等子操作符)。
示例一:按日期区间过滤
发射文档中的date_utc以 ISO 8601 字符串存储,比较操作符可直接作用于日期字符串。以下查询返回 2017-06-22 至 2017-06-25 之间的所有发射:
{ "query": { "date_utc": { "$gte": "2017-06-22T00:00:00.000Z", "$lte": "2017-06-25T00:00:00.000Z" } } }注意:日期必须使用 ISO 8601 格式,操作符才能正确进行字典序比较。
示例二:全文搜索
$text会搜索集合中所有文本索引。仓库的 Launch 模型在 models/launches.js 中对name与details建立了文本索引:
const index = { name: 'text', details: 'text', }; launchSchema.index(index);因此以下查询会同时匹配名称与详情描述中的关键字crs:
{ "query": { "$text": { "$search": "crs" } } }示例三:查找下一次即将发射
组合upcoming: true过滤与按flight_number升序排序,取limit: 1即可得到下一次发射:
{ "query": { "upcoming": true }, "options": { "limit": 1, "sort": { "flight_number": "asc" } } }示例四:复杂组合查询
以下查询综合运用了日期区间、$or逻辑、$in枚举过滤与排序限制:
{ "query": { "date_utc": { "$gte": "2017-06-22T00:00:00.000Z", "$lte": "2017-06-25T00:00:00.000Z" }, "$or": [ { "flight_number": { "$gt": 30 } }, { "tbd": true } ], "date_precision": { "$in": [ "month", "day" ] } }, "options": { "sort": { "flight_number": "asc" }, "limit": 50 } }该查询的含义:在指定日期区间内,飞行编号大于 30或日期待定(tbd: true),并且date_precision为month或day的发射记录,按flight_number升序排列,最多返回 50 条。
可查询字段速查(来自 Launch Schema)
query中可用的字段即 docs/launches/v4/schema.md 中定义的 Launch Schema,常用过滤字段包括:
| 字段 | 类型 | 说明 |
|---|---|---|
flight_number | Number | 发射编号 |
name | String(唯一) | 发射任务名称 |
date_utc | String | ISO 8601 UTC 日期 |
date_unix | Number | UNIX 时间戳(秒) |
date_local | String | 本地时间(含时区偏移) |
date_precision | String | 日期精度,枚举值half、quarter、year、month、day、hour |
static_fire_date_utc | String | 静态点火日期 |
static_fire_date_unix | Number | 静态点火 UNIX 时间戳 |
tbd | Boolean | 日期是否待定 |
net | Boolean | 日期是否为"不早于" |
window | Number | 发射窗口(秒) |
rocket | UUID | 关联火箭 ID |
success | Boolean | 是否成功 |
upcoming | Boolean | 是否即将发射 |
details | String | 任务详情 |
fairings | Object | 整流罩信息 |
crew | UUID[] | 乘组成员 ID |
ships | UUID[] | 关联船只 ID |
capsules | UUID[] | 关联龙飞船 ID |
payloads | UUID[] | 关联有效载荷 ID |
launchpad | UUID | 发射场 ID |
cores | Object[] | 一级芯级详情 |
links | Object | 媒体链接集合 |
对应的完整字段定义见源码 models/launches.js。
掌握 options:排序、分页与字段选择
options由mongoose-paginate-v2解析,支持以下常用配置项:
| 选项 | 类型 | 说明 |
|---|---|---|
select | Object / String | 指定返回哪些字段,默认返回全部字段 |
sort | Object / String | 排序规则,如{ "flight_number": "asc" } |
offset | Number | 跳过条数(与page二选一) |
page | Number | 页码(从 1 开始) |
limit | Number | 每页条数 |
pagination | Boolean | 设为false时返回全部文档,不附加 limit 条件(默认true) |
populate | Array / Object / String | 需要填充的关联路径 |
其中populate是本 API 最强大的能力之一,见下文专节。
分页返回结构解析
成功响应为200 OK,返回mongoose-paginate-v2标准分页结构。以docs/launches/v4/query.md中的响应为例:
{ "docs": [ { "fairings": { "reused": false, "recovery_attempt": true, "recovered": false, "ships": ["5ea6ed2e080df4000697c908"] }, "links": { "patch": { "small": "https://images2.imgbox.com/02/51/7NLaBm8c_o.png", "large": "https://images2.imgbox.com/69/f5/04lBXd2F_o.png" }, "reddit": { "campaign": "https://www.reddit.com/r/spacex/comments/73ttkd/koreasat_5a_launch_campaign_thread/", "launch": "https://www.reddit.com/r/spacex/comments/79iuvb/rspacex_koreasat_5a_official_launch_discussion/", "media": "https://www.reddit.com/r/spacex/comments/79lmdu/rspacex_koreasat5a_media_thread_videos_images/", "recovery": null }, "flickr": { "small": [], "original": [ "https://farm5.staticflickr.com/4477/38056454431_a5f40f9fd7_o.jpg", "https://farm5.staticflickr.com/4455/26280153979_b8016a829f_o.jpg", "https://farm5.staticflickr.com/4459/38056455051_79ef2b949a_o.jpg", "https://farm5.staticflickr.com/4466/26280153539_ecbc2b3fa9_o.jpg", "https://farm5.staticflickr.com/4482/26280154209_bf08d76361_o.jpg", "https://farm5.staticflickr.com/4493/38056455211_a4565a9cee_o.jpg" ] }, "presskit": "http://www.spacex.com/sites/spacex/files/koreasat5apresskit.pdf", "webcast": "https://www.youtube.com/watch?v=RUjH14vhLxA", "youtube_id": "RUjH14vhLxA", "article": "https://spaceflightnow.com/2017/10/30/spacex-launches-and-lands-third-rocket-in-three-weeks/", "wikipedia": "https://en.wikipedia.org/wiki/Koreasat_5A" }, "static_fire_date_utc": "2017-10-26T16:00:00.000Z", "static_fire_date_unix": 1509033600, "tdb": false, "net": false, "window": 8640, "rocket": "5e9d0d95eda69973a809d1ec", "success": true, "failures": [], "details": "KoreaSat 5A is a Ku-band satellite ...", "crew": [], "ships": [ "5ea6ed2f080df4000697c90d", "5ea6ed2e080df4000697c908", "5ea6ed30080df4000697c913" ], "capsules": [], "payloads": ["5eb0e4c5b6c3bb0006eeb217"], "launchpad": "5e9e4502f509094188566f88", "auto_update": true, "flight_number": 50, "name": "KoreaSat 5A", "date_utc": "2017-10-30T19:34:00.000Z", "date_unix": 1509392040, "date_local": "2017-10-30T15:34:00-04:00", "date_precision": "hour", "upcoming": false, "cores": [ { "core": "5e9e28a4f359185cc03b2651", "flight": 1, "gridfins": true, "legs": true, "reused": false, "landing_attempt": true, "landing_success": true, "landing_type": "ASDS", "landpad": "5e9e3032383ecb6bb234e7ca" } ], "id": "5eb87d0dffd86e000604b35b" } ], "totalDocs": 109, "limit": 10, "totalPages": 11, "page": 5, "pagingCounter": 41, "hasPrevPage": true, "hasNextPage": true, "prevPage": 4, "nextPage": 6 }分页元数据字段含义如下:
| 字段 | 说明 |
|---|---|
docs | 当前页数据数组 |
totalDocs | 符合条件的总文档数 |
offset | 当前页跳过条数 |
limit | 每页条数 |
totalPages | 总页数 |
page | 当前页码 |
pagingCounter | 当前页首条记录的全局序号(本例(page-1)*limit+1 = 41) |
hasPrevPage/hasNextPage | 是否有上一页 / 下一页 |
prevPage/nextPage | 上一页 / 下一页页码(不存在时为null) |
默认分页返回结构(未指定任何 options)为:
{ "docs": [], "totalDocs": 0, "offset": 0, "limit": 10, "totalPages": 1, "page": 1, "pagingCounter": 1, "hasPrevPage": false, "hasNextPage": false, "prevPage": null, "nextPage": null }注意响应中的日期语义:date_utc为 UTC 时间,date_local为带时区偏移的本地时间,date_precision表示日期精度。例如当date_precision为month时,日期仅精确到月份级别(相关 FAQ 见 docs/README.md)。
使用 populate 关联填充:把 UUID 变成完整文档
发射文档中的rocket、payloads、cores.core、launchpad、ships、capsules等字段默认以 UUID 形式存储,指向其他集合中的文档。例如:
{ "payloads": [ "5eb0e4c6b6c3bb0006eeb21e" ] }populate选项可以"替换"这些 UUID 为对应的完整文档,实现一次请求获取完整的关联数据链。
基础填充
向https://api.spacexdata.com/v4/launches/query发送以下请求体,即可将每个发射的payloads替换为完整的有效载荷对象:
{ "query": {}, "options": { "populate": [ "payloads" ] } }返回结果中payloads变为对象数组:
{ "payloads": [ { "dragon": { "capsule": null, "mass_returned_kg": null, "mass_returned_lbs": null, "flight_time_sec": null, "manifest": null, "water_landing": null, "land_landing": null }, "name": "Tintin A & B", "type": "Satellite", "reused": false, "launch": "5eb87d14ffd86e000604b361", "customers": ["SpaceX"], "norad_ids": [43216, 43217], "nationalities": ["United States"], "manufacturers": ["SpaceX"], "mass_kg": 800, "mass_lbs": 1763.7, "orbit": "SSO", "reference_system": "geocentric", "regime": "low-earth", "longitude": null, "semi_major_axis_km": 6737.42, "eccentricity": 0.0012995, "periapsis_km": 350.53, "apoapsis_km": 368.04, "inclination_deg": 97.4444, "period_min": 91.727, "lifespan_years": 1, "epoch": "2020-06-13T13:46:31.000Z", "mean_motion": 15.69864906, "raan": 176.6734, "arg_of_pericenter": 174.2326, "mean_anomaly": 185.9087, "id": "5eb0e4c6b6c3bb0006eeb21e" } ] }填充时选择字段
populate支持对象形式,通过path指定关联路径、select限制返回字段。例如只关心有效载荷名称:
{ "options": { "populate": [ { "path": "payloads", "select": { "name": 1 } } ] } }返回结果:
{ "payloads": [ { "name": "Tintin A & B", "id": "5eb0e4c6b6c3bb0006eeb21e" } ] }嵌套填充
populate可以递归嵌套,例如先填充payloads,再填充每个有效载荷对象内部的launch字段:
{ "options": { "populate": [ { "path": "payloads", "populate": [ { "path": "launch" } ] } ] } }crew 字段的特殊处理
v4 的 Launch 模型中,crew是对象数组,每个元素形如{ "crew": UUID, "role": String }(见 models/launches.js),而 v5 中则直接是 UUID 数组。为了保证 v4 与 v5 返回结构一致,仓库做了两层处理:
- 请求侧的
transformQuery在 populate 时自动将crew路径改写为crew.crew(见 routes/launches/v4/_transform-query.js); - 响应侧的
transformResponse会将填充后的crew数组重新构建为纯 UUID 数组(见 routes/launches/v4/_transform-response.js)。
这意味着即使你在populate中填写"crew",也会被自动映射到正确路径;而返回时crew字段始终以数组形式呈现,无需关心内部结构差异。
错误响应与排查
400 Bad Request:当query或options语法非法(例如 MongoDB 操作符拼写错误、字段名不存在、日期格式不合法)时,接口返回400,响应体中直接携带 Mongoose/MongoDB 的报错信息与修正建议:
Code:400 Bad Request
Content: Mongoose error is shown, with suggestions to fix the query.
从源码看,路由内所有查询异常都会被捕获并以ctx.throw(400, error.message)抛出(routes/launches/v4/index.js),因此响应体的错误信息即底层数据库的原始错误文本,可直接据此修正查询。
性能与缓存行为
/v4/launches/query路由应用了cache(20)中间件(routes/launches/v4/index.js),即发射数据的查询结果会被缓存 20 秒。从 middleware/cache.js 的源码可以看出:
- 缓存基于 Redis,键由
HTTP 方法 + URL + 请求体 JSON经 BLAKE3 哈希生成,因此相同请求体的重复请求会命中缓存; - 响应头
spacex-api-cache: HIT表示命中缓存,MISS表示未命中; - 当
NODE_ENV非production或 Redis 不可用时,中间件自动降级为直接放行,不影响接口可用性; - 生产环境下,
POST与GET请求都在可缓存方法白名单内。
相关缓存说明见 docs/README.md。
与其他端点配合使用
/v4/launches/query与同模块其他端点互补:
GET /v4/launches:返回全部发射,按flight_number升序;GET /v4/launches/:id:按 ID 返回单条发射;GET /v4/launches/past:返回历史发射(upcoming: false);GET /v4/launches/upcoming:返回即将发射(upcoming: true);GET /v4/launches/latest:返回最近一次历史发射;GET /v4/launches/next:返回下一次发射;
以上路由的完整定义与实现见 routes/launches/v4/index.js。当内置便捷端点无法满足筛选需求时,即可改用POST /v4/launches/query组合任意条件。对应文档还包括 docs/launches/v4/all.md、docs/launches/v4/one.md、docs/launches/v4/past.md、docs/launches/v4/upcoming.md、docs/launches/v4/latest.md 与 docs/launches/v4/next.md。
小结
本文围绕POST /v4/launches/query完整讲解了请求结构、MongoDB 过滤语法、分页与排序选项、分页响应结构、关联填充技巧及错误处理。总结要点如下:
query接受任意 MongoDBfind()查询,日期需使用 ISO 8601 格式,文本搜索依赖name/details的文本索引;options支持select、sort、page、offset、limit、pagination、populate;- 返回结构为
mongoose-paginate-v2标准分页结构,字段包括docs、totalDocs、totalPages等; - 关联字段默认返回 UUID,通过
populate可替换为完整文档,支持字段选择与嵌套填充; - 查询结果缓存 20 秒,相同请求体可命中 Redis 缓存;
- 语法错误返回
400,错误体为数据库原始提示,可直接据此修正。
如需进一步了解通用查询语法,可继续阅读 docs/queries.md;若想深入 Launch 数据模型,可查看 docs/launches/v4/schema.md 与源码 models/launches.js。
- 后端
- API设计
【免费下载链接】SpaceX-API
:rocket: Open Source REST API for SpaceX launch, rocket, core, capsule, starlink, launchpad, and landing pad data.
相关推荐
SpaceX-API 船舶数据查询接口实战:Ships v4 /query 端点全解析
SpaceX API 船舶数据查询接口实战:Ships v4 /query 端点全解析 本篇技术指南围绕 SpaceX API 开源仓库( gh_mirrors
后端API设计Hermes WebUI Workspace Git 控制:默认只读、可安全开启的浏览器端 Git 操作设计
Hermes WebUI Workspace Git 控制:默认只读、可安全开启的浏览器端 Git 操作设计 Workspace Git 控制(Workspac
后端API设计Wagtail 信号(Signals)完全指南:从 published、page_move 到工作流事件的内容系统解耦机制
Wagtail 信号(Signals)完全指南:从 published、page_move 到工作流事件的内容系统解耦机制 本文基于 Wagtail 官方文档
后端API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考