Founders & Indie Hackers: Stop Putting All Your MVP Content in a Database
Source: Dev.to
Building OCR Trips: A Lightweight MVP Approach
I’m building OCR Trips – an obstacle‑course‑race trip planner that helps users:
- Discover races
- Plan trips
- Budget flights, hotels, and race fees in one place
The “app” part (accounts and trip plans) runs on Supabase. 🏄 But for the MVP content, I didn’t want everything living in a database.
The Original Idea
Users would manually add races they know. The goal was a lightweight trip planner where you plug in your next OCR and we help with flights, hotels, and budget.
Expanding the Scope
What if someone doesn’t have a race yet and needs help finding one?
I added an Obstacle Course Race Calendar – a public directory where anyone can:
- Browse obstacle‑course races by country and city
- See nearby airports and rough travel ideas
- Check typical weather for race dates
- Filter by brand, distance, etc.
Why Not Use a Supabase Table?
My first thought: “Easy. Add a races table in Supabase and build an admin UI.”
My second thought: Do I really want to put stuff in Supabase?
The Flat‑File Solution
Instead of a races table, I stored the data as MDX files:
content/races/
├── spartan/
│ └── 2026/
│ ├── paris.mdx
│ └── london.mdx
├── tough-mudder/
│ └── 2025/
│ └── atlanta.mdx
└── ... 200+ files
Each file contains front‑matter + MDX (brand, date, location, distances, coordinates, terrain, description).
Velite reads those files at build time, validates them, and provides a typed dataset that can be imported in Next.js.
Benefits of Skipping the Database (for now)
- No migrations whenever race fields change
- No custom admin panel for content editing
- No seed scripts for dev/staging/prod
- No schema drift between environments
- No runtime bugs like “DB not reachable” for pages that could be static
For an MVP, that’s a lot of surface area to avoid.
Workflow with Flat Files
- Edit a
.mdxfile - Commit the change
- Vercel rebuilds →
/racesis up to date
Deployment becomes publishing.
How Velite Makes Files Feel “Database‑Like”
At build time it:
- Validates content against a Zod/TS schema
- Generates TypeScript types
- Outputs an array that can be imported and filtered
import { races } from "@/content/generated";
const frenchSpartan = races.filter(
(race) =>
race.brand === "spartan" &&
race.country === "France"
);
You still get:
- Filters
- Searching
- Typed fields
- Autocomplete in the editor
Scaling Beyond Files
“But what if I outgrow this?”
Starting with files isn’t a trap. If /races ever needs:
- Non‑technical editors
- Live updates without deploys
- Super complex querying
…you can:
- Create a races table in Supabase
- Write a one‑off script that reads the MDX files and inserts them into the DB
- Switch from
import { races }toawait db.query.races
The early choice (files) doesn’t block the later choice (database); it just lets you ship faster now.
Advice for Founders & Indie Hackers
Before you:
- Model everything in SQL
- Build an admin UI
- Wire up forms to manage “content”
Ask yourself:
- Will users actually write to this?
- Does it change constantly?
- Do I need querying tools for it right now?
If the answer is “not really,” try this stack:
- Files in Git
- A schema layer (Velite or similar)
- Static generation
You can always migrate to a database later, but you might not have to.