/ blog/why-i-built-my-own-cms-not-contentful
blog / why-i-built-my-own-cms-not-contentful / overview.md

Why I Built My Own CMS Instead of Using Contentful or Sanity

Headless CMS platforms are powerful, but they abstract away the database, limit your architecture, and introduce vendor lock-in. Here is a build-in-public look at why writing a bespoke CMS on Supabase was the best engineering decision I made.

The Allure of Headless

When I started designing this blogging platform, my first instinct was to reach for a Headless CMS. Contentful, Sanity, and Strapi are the industry darlings right now. They promise a polished admin UI out of the box, a global CDN, and a GraphQL or REST API to fetch your data.

It sounds perfect. You focus on the frontend, they handle the data layer.

But as I began architecting the platform—which required scheduled publishing, intricate related-post algorithms based on tag overlap, and inline security analytics—the cracks in the Headless CMS model started to show.

1. The Loss of SQL

The biggest tradeoff of a SaaS Headless CMS is that you lose direct access to your database.

I am a backend engineer. I think in relational data, indexes, and complex joins.

When I wanted to build a "Related Posts" engine, doing it in Postgres is trivial:

SELECT p.*, 
       (SELECT count(*) FROM unnest(p.tags) t WHERE t = ANY($1)) as overlap_score
FROM blog_posts p
WHERE p.id != $2 AND p.published = true
ORDER BY overlap_score DESC, published_at DESC
LIMIT 3;

Try doing that in a Headless CMS API. You can't. You are forced to either:

  1. Fetch all posts to the Node.js server and compute the overlap in memory (terrible for scaling).
  2. Sync the CMS data into your own Postgres database via Webhooks, just so you can query it properly.

If I have to sync data to Postgres just to run advanced queries, why am I paying for a CMS in the first place?

2. Vendor Lock-in and API Limits

Sanity and Contentful are fantastic products, but they are walled gardens. If they raise pricing, deprecate an API version, or suffer an outage, you are entirely at their mercy.

By building on top of Supabase (PostgreSQL), my data is mine. I can dump the SQL at any time. I can move it to AWS RDS, Heroku, or a Raspberry Pi.

3. The Custom Editor Experience

I needed a very specific writing workflow. I wanted a dual-pane Markdown editor that felt like VS Code, with real-time SEO scoring, character limits, and secure, inline image uploads direct to Cloudflare R2.

Most Headless CMS platforms allow UI plugins, but building a React plugin for Sanity Studio felt like learning a proprietary framework just to build a text area.

By building my own admin dashboard in Next.js, I had total control. I integrated @uiw/react-md-editor, hooked it up to a Server Action for R2 uploads, and built exactly the workflow I wanted in a matter of days.

4. Total Typesafe End-to-End

With a bespoke CMS built on Next.js and Supabase, I can use tools like supabase-js to generate TypeScript types directly from my SQL schema.

supabase gen types typescript --local > src/types/database.ts

My database schema, my server actions, and my React components all share the exact same type definitions. If I drop a column from the blog_posts table, my Next.js build fails immediately. Achieving this level of type safety with a third-party CMS often requires brittle code-generation steps and massive GraphQL schemas.

The Architecture I Settled On

Instead of a Headless SaaS, I built a "Bespoke Monolith" utilizing modern cloud primitives:

  1. Database & Auth: Supabase (PostgreSQL + GoTrue)
  2. Framework: Next.js App Router
  3. Storage: Cloudflare R2 (S3 compatible, zero egress fees)
  4. Hosting: Vercel

The Admin Panel is simply a protected route group ((admin)/...) within the Next.js app. It shares the same components, design system, and tailwind configuration as the public site.

Was it worth it?

Building a CMS takes time. You have to handle authentication, RBAC, form validation, and asset storage yourself.

But as an engineer, owning your stack is liberating. I can add a new feature—like a custom engagement analytics dashboard—in a few hours, writing pure SQL and React, without ever having to read documentation on how to configure a Headless CMS schema migration.

If you are a non-technical founder, use Contentful. If you are an engineer building your own platform, build it yourself. You'll learn more, and you'll own your data.

Tags

build-in-publiccmsarchitecturenextjs
0
0