Kita 用了哪些技术,以及我刻意没有使用什么
这是“Kita 技术选择”系列的第九篇。前八篇按照真实顺序记录了每层问题;这一篇不再追逐新工具,而是回看整套组合为什么长成现在这样。
如果只列依赖,Kita 看起来是一组常见名词:Next.js、React、TypeScript、Payload、PostgreSQL、Docker、Coolify、Cloudflare R2 和 OpenList。
这种列表无法说明项目。
它没有告诉我为什么需要 Payload 却不需要 NextAuth,为什么选择 PostgreSQL 却没有再加入 Drizzle,为什么使用 OpenList 却拒绝把它放进 Kita Compose,也没有解释 Dev Container 和 R2 分别解决哪一层问题。
Kita 真正的技术栈不是依赖总和,而是一条选择历史。
最初不是从技术栈开始,而是从审美开始
我先知道自己不想做什么样的网站。
我不想让它变成标准 SaaS dashboard,也不想为了展示全栈能力制造用户、支付和社区功能。Kita 应该是带有视觉小说氛围的个人内容站和私人游戏文化档案馆。
这个视觉审美后来对应了一套工程审美:
- 配置应该能被自己解释;
- 开发环境应该跟随仓库;
- 生产环境尽量由自己掌握;
- 数据、文件和外部服务边界清楚;
- 可以借鉴成熟项目,但不复制不需要的全家桶;
- 新技术只有在真实问题出现时才加入。
所以我所说的“找到适合审美的技术,拼出自己的技术栈”,并不是把喜欢的工具全部堆起来。
它更接近:
1 | 遇到真实问题 |
三段视频形成三层原始基座
Kita 最早的工程方向可以追溯到三段视频。
第一段是 Create your own Next.js Starter Template。它让我学习怎样区分 Generator、Starter、SaaS Starter 和 Toolkit,再为格式、检查、编辑器、目录、路由与环境变量逐项选择工具。
第二段是 you should be using dev containers。它让我意识到,源码之外的 Node、pnpm、系统工具和编辑器扩展也可以写进仓库。
第三段是 Coolify Crash Course / Self Host 101。它把 VPS、HTTPS、GitHub 部署、Docker Compose、PostgreSQL、S3-compatible storage 和备份连成一条生产路线。
三段视频分别成为:
| 基座 | 最初回答的问题 | Kita 保留的思想 |
|---|---|---|
| 代码基座 | Next.js 项目怎样开始 | 工具必须对应问题,规则跟随仓库 |
| 开发环境基座 | 项目怎样在新电脑重建 | Dev Container、DIND、编辑器与运行时一体化 |
| 生产运行基座 | 应用怎样离开本地 | Self-host、Coolify、Compose、域名、数据与备份 |
它们没有提供 Kita 的最终答案。Payload、R2、OpenList 和恢复流程,是后来在真实开发中继续增加的层。
应用层:Next.js、React、TypeScript 与定制样式
Next.js 提供 App Router、服务端组件、构建和 standalone 生产输出。React 负责页面和交互组件,TypeScript 保护数据边界,Typed Routes 检查一部分内部链接。
样式上,Kita 使用 Tailwind 处理通用布局,再用 CSS Modules 和全局 CSS 完成更定制的视觉。
这种组合解决的是“普通部分保持高效,辨识度部分保留控制权”。
首页的雨滴效果没有引入 Three.js,而是使用 Canvas 2D 与较小的 WebGL shader;Games 的封面墙借鉴 Vercel Image Gallery 的布局与 lightbox,却没有把它的存储选择和整个模板一起复制。
我会借用成熟交互,但不会让参考项目替我决定 Kita 的产品和视觉。
内容层:Payload、PostgreSQL、Lexical 与 mapper
Payload 负责 Admin、Collections、后台认证、access control、API、上传、migration 和类型生成。PostgreSQL 保存内容、用户、关系和 Media 元数据,Lexical 承载 Games 与 Reviews 的富文本。
公开页面的服务端读取使用 Payload Local API,不为同一个 Node 应用额外制造内部 HTTP 请求。
server getter 集中查询与发布过滤,mapper 再把 Payload document 转成页面稳定的 view model。
这条链路是:
1 | Payload Admin |
它比静态数组复杂,但换来了真实后台、关系内容、migration 和可测试的数据边界。
媒体层:Payload Media、Sharp 与 R2
Games 封面最初是源码枚举,后来是静态路径,最终才成为必填 Payload Media relationship。
本地文件写入 .payload-media,生产对象进入 Cloudflare R2。Sharp 生成 thumbnail 与 display,Next Image 在页面渲染阶段继续做优化。
选择 R2 不是因为对象存储天然高级,而是生产容器本地文件不适合长期内容;图片也不应该每增加一张就修改源码并重新部署。
代价是恢复必须同时考虑 PostgreSQL 元数据和 R2 对象。
开发层:pnpm、Corepack、Dev Container 与 DIND
pnpm、Corepack 和 lockfile 固定包管理器与依赖解析。ESLint、Prettier、TypeScript 和 Vitest分别检查排版、静态规则、类型和关键逻辑。
Dev Container 把 Node 22、系统工具、VS Code 扩展和生命周期命令写进仓库;Docker-in-Docker 在开发容器内部运行 PostgreSQL Compose。
Windows 宿主机只需要 Git、Docker、VS Code 和浏览器。
.next root 污染与 9P I/O 性能问题也说明这套方案有真实代价。Kita 最后通过普通用户守卫和两个 targeted named volume 修正,而不是把容器描述成自动解决一切的黑盒。
生产层:Dockerfile、Compose、Coolify 与 Cloudflare
多阶段 Dockerfile 负责应用镜像,Next standalone 减少运行文件,非 root 用户降低容器权限。
Compose 管理 web、postgres、backup 和数据 volume。Coolify 在 VPS 上管理构建、环境变量、域名和部署,Cloudflare 承担 DNS、HTTPS 相关入口与 R2。
GitHub Actions 和 main Ruleset 在代码进入部署前执行质量门禁,生产 migration 在服务器上推进数据库 schema。
这些工具没有合并成一个万能平台,而是通过 Git、容器、环境变量、网络和 S3-compatible storage 连接。
外部资源层:OpenList 只通过 URL 连接
OpenList 是独立 Coolify Application,拥有自己的管理员、data、存储凭据和发布节奏。
Kita 只在 Games.links 中保存公开 HTTPS URL,不调用内部 API,不共享数据库、volume 和 secret,也不维护 SolidJS 前端 fork。
用户体验上,Games 可以直接进入对应资源目录;系统架构上,OpenList 故障不会阻止 Kita 启动。
这是整套组合里最能体现边界的一项选择:产品关系紧密,不代表部署和数据必须耦合。
可靠性层:CI、migration、backup 与恢复清单
格式、lint、类型、单元测试和生产 build 构成合并门禁。Payload migration 管理生产 schema,backup sidecar 使用 pg_dump --format=custom、pg_restore --list 和 rclone,把 PostgreSQL dump 上传到私有 R2 bucket。
但 Kita 仍然没有完成全部恢复证明。
定时 dump 已经运行,本地源码重建已经验证;生产 PostgreSQL 完整 restore drill、R2 Media 独立恢复和全新 VPS/Coolify 演练仍然待完成。
我把“知道缺什么”也视为架构的一部分。比起用“生产级”概括系统,明确未验证边界更有价值。
我刻意没有采用什么
没有采用的技术并不是不好,只是当前没有对应问题。
| 没有采用 | Kita 当前的判断 |
|---|---|
| Express / Hono / Fastify | Payload 已覆盖当前需要的 Admin、CRUD、Auth、API 和访问控制 |
| Prisma / Drizzle | 内容 schema 与 migration 已由 Payload 管理,不需要第二套事实来源 |
| Auth.js / NextAuth | 没有公开用户登录,Payload Admin auth 已满足私有后台 |
| Redis / 队列 / 微服务 | 没有相应规模、异步任务和独立团队边界 |
| tRPC | Server Components + Payload Local API 已覆盖内部类型化读取 |
| MUI / Ant Design / Chakra / HeroUI | 通用组件风格不符合 Kita 的定制视觉方向 |
| Three.js | 当前雨滴只需要小型 2D shader 层 |
| Vercel Blob | Payload Media + R2 已覆盖生产图片存储 |
| Cloudflare Images | Sharp + R2 已覆盖尺寸生成与对象保存 |
| OpenList API 深度集成 | 公开 URL 已完成任务,API 会增加 token、缓存和故障耦合 |
| OpenList 前端 fork | 官方界面已够用,不值得承担版本配对和长期维护 |
这张表不是永久禁用名单。
如果以后出现公开用户,认证问题会重新成立;如果出现不适合 CMS 的复杂业务表,独立 ORM 可能合理;如果后台需要耗时任务,队列也可能成为真实需求。
现在不采用,只表示我不为想象中的未来提前支付复杂度。
哪些早期判断后来改变了
Kita 并不是沿着一份完美路线图完成的。
- PostgreSQL 从需要手工启动,变成由
pnpm dev自动启动和等待; - 开发环境从不增加 named volume,变成只为
.next与node_modules增加性能 volume; - Games 从 mock、
coverKey和静态路径,走到 Payload Media + R2; - OpenList 从评估项变成独立上线应用;
- 数据库备份从路线图变成定时 sidecar;
- 构建从被 root 缓存阻断,走到用户、所有权和并发守卫;
- 项目从没有测试,走到 mapper、配置和备份脚本的自动检查。
这些变化没有破坏“选择适合自己的技术栈”这条主线,反而证明它需要允许修正。
技术审美不是固执地保留第一次选择,而是在新证据出现时仍然能解释为什么改变。
Kita 最终证明的不是技术数量
Kita 不是高并发商业平台,也不是成熟社区产品。
它真正完成的是一条适合单人维护的工程链路:
1 | 页面和内容模型 |
对我这个最初只是想建立自己 Next.js Starter 的初学者来说,最大的收获不是终于收集齐一套“现代技术栈”。
而是我开始能够说清楚:每个工具是什么时候进入项目的,它解决了什么,我为什么只保留其中一部分,它带来了什么代价,以及什么情况下我会再次替换它。
这就是 Kita 一直以来的发展方式,也是这个系列真正想记录的东西。
如果想继续查看具体配置、执行顺序、故障和回滚,可以从工程案例系列的第一篇开始:
如果还不熟悉 package.json、环境变量、migration、Git、Pull Request 或 CI,可以先从基础系列开始:
如果想看 commit 时间线、真实 503、当前内容工作流和仍未关闭的缺口,可以进入真实开发记录:
系列导航
- 上一篇:从能部署到能恢复:PostgreSQL、R2 与灾难恢复
- 从头阅读:从一个视频开始:我怎样为 Kita 搭起 Next.js 开发基座
- 基础系列:从零读懂 Kita
- 真实开发记录:从 Git 历史开始













