Founders & Indie Hackers: Stop Putting All Your MVP Content in a Database

Published: (December 3, 2025 at 05:43 PM EST)
3 min read
Source: Dev.to

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

  1. Edit a .mdx file
  2. Commit the change
  3. Vercel rebuilds → /races is 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:

  1. Create a races table in Supabase
  2. Write a one‑off script that reads the MDX files and inserts them into the DB
  3. Switch from import { races } to await 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.

Back to Blog

Related posts

Read more »

Day 1278 : No Longer Am

Professional Not a bad day. Had a couple of back‑to‑back meetings. I ran my idea by my manager for the project I'm working on and they agreed, so I started put...