Payload、PostgreSQL、Collection、CRUD 和 Adapter 的关系
这是“从零读懂 Kita”系列的第六篇。上一篇解释配置怎样进入不同环境;这一篇解释 Kita 的内容究竟怎样进入数据库和页面。
Payload 不是数据库
这是我一开始最容易混淆的地方。
1 | Payload |
Payload 提供 Admin、Collection、认证、访问控制、API、上传、类型生成和 migration;PostgreSQL 保存用户、内容、关系和 Media 元数据。
如果停止 Payload,数据仍在 PostgreSQL 中;但失去 Payload 后,我需要自己理解表结构、认证和业务规则。
CMS 是什么
CMS 是 Content Management System,内容管理系统。
对 Kita 来说,它解决:
- 我怎样登录私有后台;
- 怎样创建 Games、Reviews、Tools;
- 字段哪些必填;
- 谁可以读写;
- 富文本怎样编辑;
- 图片怎样上传;
- 数据怎样提供给前台。
它不是“数据库可视化工具”这么简单,而是把内容规则与管理界面连接起来。
Collection 是什么
Payload Collection 可以理解成一类内容的定义,例如:
1 | Users |
Games Collection 定义:
- title;
- slug;
- developer;
- publicationStatus;
- summary 与 rich text body;
- cover Media relationship;
- tags 与 links;
- 公开访问过滤。
Collection 不是单条游戏,它是“游戏内容应该具备什么结构”的 schema。
Document 是什么
Collection 中的一条实际记录叫 document。
1 | Games Collection |
在 PostgreSQL 中,它们最终会映射到表、列和关联记录;在 Payload API 中,它们表现为带字段的 TypeScript/JSON 对象。
CRUD 是什么
CRUD 是四类基本数据操作:
1 | Create 创建 |
Payload Admin 让管理员执行这些操作,Payload API 让服务端代码执行这些操作,access control 决定当前请求是否有权限。
拥有 CRUD 不等于所有人都应该拥有全部权限。例如 Kita 的 Media:公开访客可以 Read,只有登录管理员可以 Create、Update、Delete。
Relationship 是什么
Games 不直接保存一整张图片,而是通过 cover 关联一个 Media document。
1 | Games.cover |
PostgreSQL 使用外键保护这种关系。Payload Local API 可以按指定 depth 展开关联 document。
relationship 只有 ID 和完整 document 是两种不同运行状态,mapper 需要明确处理。
Database Adapter 做什么
Payload 支持不同数据库,Adapter 是 Payload 与具体数据库之间的翻译层。
根据 Payload Database 文档,Adapter 把 Payload 内部的数据结构转换成数据库原生结构。
Kita 配置:
1 | db: postgresAdapter({ |
它负责连接 PostgreSQL,并让 Payload 的 Collection、关系和 migration 落到关系数据库。
Adapter 不是另一个数据库,DATABASE_URI 也不是 Payload 自己的数据格式。
Admin 与公开站点怎样共存
Kita 把 Payload 集成在同一个 Next.js 应用:
1 | /admin |
后台负责写入,公开页面主要在服务端通过 Local API 读取。
这不是前后端两个仓库,也没有独立 Express server。
Local API 是什么
Next.js 与 Payload 在同一个 Node 进程中时,服务端可以直接调用 Payload:
1 | server getter |
不需要先向自己的 REST URL 发 HTTP 请求。
优点:
- 少一层网络和序列化;
- 不需要服务端硬编码 base URL;
- 类型和 Payload 配置可以直接使用;
- 查询集中在 server-only 代码。
REST 仍然存在,只是当前公开服务端页面不需要绕远路。
完整数据流
写入:
1 | 管理员 |
读取:
1 | app route |
mapper 把 CMS document 转成 UI 真正需要的模型,让组件不必理解 Payload 内部字段与可空关系。
Generated Types 是什么
Payload 根据 Collection 生成:
1 | src/payload/payload-types.ts |
修改 Collection 后运行:
1 | pnpm payload:types |
这个文件由工具维护,不应该手工添加业务字段。类型生成让 server getter、mapper 和测试知道 document 当前形状。
为什么 Kita 没有 Prisma、Drizzle 或 Express
Payload 已提供:
- Admin;
- Auth;
- CRUD;
- access control;
- PostgreSQL adapter;
- schema/migration;
- Local/REST/GraphQL API;
- upload;
- generated types。
再为同一批内容增加第二套 ORM 或后端,会产生重复 schema 与职责。
如果以后出现不适合 CMS 的独立业务数据,再重新评估,不提前支付复杂度。
Seed 是什么
Seed 是为了开发或测试快速写入一组已知数据的脚本。
它不是 production migration,也不应该在生产自动运行。
Kita 要求:
1 | NODE_ENV 不是 production |
才允许 seed route 工作。使用结束后恢复 false。
常见误解
- Payload 不等于 PostgreSQL;
- Collection 不等于单条 document;
- TypeScript type 不会自动修改数据库表;
- Local API 不代表数据存在内存中;
- Admin 能打开不代表公开权限正确;
- seed 不是备份,也不是 migration;
- 删除 Docker database volume 不等于“重新生成类型”。
下一篇专门解释最容易让初学者害怕的 migration:为什么修改 Collection 后,生产数据库不能只靠重新启动自动跟上。
















