创始人 & Indie Hackers:停止将所有 MVP 内容放入数据库
Source: Dev.to
构建 OCR Trips:轻量级 MVP 方法
我正在构建 OCR Trips —— 一个障碍赛旅行规划器,帮助用户:
- 发现赛事
- 规划行程
- 在同一地点预算航班、酒店和赛事费用
“应用”部分(账户和旅行计划)运行在 Supabase 上。🏄 但对于 MVP 内容,我不想把所有东西都放在数据库里。
最初的想法
用户会手动添加他们知道的赛事。目标是一个轻量级的旅行规划器,你只需输入下一个 OCR,我们就帮助安排航班、酒店和预算。
扩展范围
如果有人还没有赛事,需要帮助寻找怎么办?
我添加了一个 障碍赛日历——一个公开目录,任何人都可以:
- 按国家和城市浏览障碍赛
- 查看附近机场和大致的旅行方案
- 查看赛事日期的典型天气
- 按品牌、距离等过滤
为什么不使用 Supabase 表?
我的第一个想法:“很简单。就在 Supabase 中添加一个 races 表并构建管理界面。”
我的第二个想法:我真的想把东西放进 Supabase 吗?
平面文件解决方案
我没有使用 races 表,而是把数据存放在 MDX 文件中:
content/races/
├── spartan/
│ └── 2026/
│ ├── paris.mdx
│ └── london.mdx
├── tough-mudder/
│ └── 2025/
│ └── atlanta.mdx
└── ... 200+ files
每个文件包含 front‑matter + MDX(品牌、日期、地点、距离、坐标、地形、描述)。
Velite 在构建时读取这些文件,进行验证,并提供可在 Next.js 中导入的类型化数据集。
暂时跳过数据库的好处
- 当赛事字段变化时无需迁移
- 不需要自定义后台面板来编辑内容
- 不需要为开发/预发布/生产编写种子脚本
- 环境之间不会出现模式漂移
- 对于可以静态化的页面,避免出现 “数据库不可达” 等运行时错误
对于 MVP 来说,这可以避免大量的潜在问题。
使用平面文件的工作流
- 编辑
.mdx文件 - 提交更改
- Vercel 重新构建 →
/races自动保持最新
部署变成发布。
Velite 如何让文件拥有“数据库般”的感觉
在构建时它:
- 根据 Zod/TS schema 验证内容
- 生成 TypeScript 类型
- 输出一个可以导入并过滤的数组
import { races } from "@/content/generated";
const frenchSpartan = races.filter(
(race) =>
race.brand === "spartan" &&
race.country === "France"
);
你仍然可以获得:
- 过滤
- 搜索
- 类型化字段
- 编辑器自动补全
超越文件的扩展
“但是如果我不再适用这种方式怎么办?”
从文件开始并不是陷阱。如果 /races 将来需要:
- 非技术编辑者
- 无需部署的实时更新
- 超复杂的查询
…你可以:
- 在 Supabase 中创建 races 表
- 编写一次性脚本读取 MDX 文件并写入数据库
- 将
import { races }替换为await db.query.races
早期的选择(文件)并不会阻碍后期的选择(数据库);它只是让你现在更快地交付。
给创始人和独立黑客的建议
在你:
- 在 SQL 中建模所有内容
- 构建管理 UI
- 为“内容”接入表单
之前,先自问:
- 用户真的会向这里写入数据吗?
- 内容会经常变化吗?
- 我现在真的需要查询工具吗?
如果答案是 “并不真的”,可以尝试以下技术栈:
- Git 中的文件
- 一个 schema 层(Velite 或类似工具)
- 静态生成
你随时可以迁移到数据库,但可能根本不需要。