← 中文资源

简体中文版本

构建带游标分页的 MongoDB REST API

一个完整的 MongoDB REST API:文档校验、索引查询、游标分页、乐观更新,以及针对真实数据库的集成测试。

阅读英文原文

项目概述

本项目实现基于文档库的 REST API:以严格 schema 校验写操作、用游标分页保证稳定、并以条件更新避免丢失更新。偏移分页是列表端点里的隐性缺陷——翻页时插入一条就会重复或跳漏——游标基于稳定键即可消除这类问题。

架构与关键决策

路由器把端点映射到仓储函数,仓储是唯一接触集合的模块,使访问规则可脱离 HTTP 测试;写入一律经 JSON schema 校验并拒绝未知字段,在边界处校验比事后修复更便宜。读取以排序键加文档 id 构造游标,使页边界不随插入移动;乐观更新以版本或匹配条件为准,基于过期读的写入返回冲突而非覆盖较新值。

实施与测试

每个测试文件使用独立数据库;在页间插入文档断言分页不重不漏;陈旧更新断言返回冲突;非法文档断言被拒;断言仓储投影不会泄漏后加字段。

限制与后续

  • 仅单集合 CRUD,不含聚合与跨集合连接。
  • 游标分页只支持索引覆盖的排序。
  • 无既有文档的迁移工具,schema 变更需另行回填。

代码示例

pnpm init
pnpm add mongodb express ajv
pnpm add -D typescript tsx vitest
mongodb-rest-api/
├── src/router.ts
├── src/repository.ts
├── src/validate.ts
├── src/pagination.ts
├── tests/api.test.ts
└── package.json
export const findByCursor = (db, { after, limit }) =>
  db.collection('items').find(after ? { _id: { $gt: after } } : {}).sort({ _id: 1 }).limit(limit + 1).toArray();
const valid = ajv.validate(schema, body);
if (!valid) return res.status(400).json({ errors: ajv.errors });
const next = rows.length > limit ? rows[limit - 1]._id : null;
await db.collection('items').createIndex({ _id: 1, createdAt: -1 });
const updated = await db.collection('items').findOneAndUpdate(
  { _id, version }, { $set: patch, $inc: { version: 1 } }, { returnDocument: 'after' });
if (!updated) return res.status(409).json({ error: 'stale_version' });
pnpm vitest run