CMS for Express: Add Content to a Node App Without Building One

Skip building a content backend in your Node app

August 2, 2026 · 5 min read
CMS for Express: Add Content to a Node App Without Building One

Express is the default Node.js web framework — unopinionated, minimal, everywhere. It gives you routing and middleware and nothing else, which is the point. For content, though, that "nothing else" means no CMS, no admin, no content model. An Express app that needs a blog or marketing pages either grows a hand-built content backend or connects a headless CMS over an API. This post covers the headless route: how to wire a CMS to Express and why a self-hosted content layer fits a Node stack.

Build it in Express, or connect a headless CMS?

Build your own: a Mongoose/Prisma model, an admin UI, and templates or a front end. Full control in JavaScript, but you own every content feature — media, SEO, scheduling, publishing workflow. That's a lot to build and keep running.

Headless CMS: content in a dedicated service with a ready-made admin and API. Express fetches it. You skip building the editor; non-developers get a real tool; Express stays your application server.

For a blog or marketing pages edited by writers, headless saves weeks. For content tightly bound to app logic, a built-in model may fit.

Fetching CMS content in Express

Node has fetch built in (v18+). No library needed:

import express from 'express';
const app = express();
const CMS = 'https://cms.yoursite.com/api/v1';

app.get('/blog', async (req, res) => {
  const r = await fetch(`${CMS}/posts?per_page=20`);
  const { data: posts } = await r.json();
  res.render('blog/index', { posts });
});

app.get('/blog/:slug', async (req, res) => {
  const r = await fetch(`${CMS}/posts/${req.params.slug}`);
  if (r.status === 404) return res.status(404).render('404');
  const { data: post } = await r.json();
  res.render('blog/detail', { post });
});

Cache responses (a simple in-memory or Redis layer) so you're not calling the API on every request. The CMS returns a { success, message, data } envelope, and drafts return 404 — unpublished content never leaks.

Why self-hosted headless fits Express

Express developers run their own Node infrastructure and value control. A SaaS CMS with metered API calls and vendor-owned data cuts against that. A self-hosted CMS with a plain REST API keeps content under your control.

UnfoldCMS is one option. It's a self-hosted CMS on Laravel with a REST API at /api/v1/*. It's PHP, not Node — but headless means the CMS is an HTTP service, so your Express code only sees JSON. Run it on your own server alongside your Node app.

Key fits:

  • REST + JSON — exactly what Node's fetch and your view layer want. No GraphQL client needed.
  • No SDK — nothing added to package.json; use the built-in fetch.
  • Self-hosted, pay once — no per-request billing, content in your own database.

Cache busting with webhooks

Cache CMS responses and clear them when content changes. UnfoldCMS fires outgoing, HMAC-signed webhooks on publish/update/delete. Add an Express route that verifies the signature and clears the cache:

app.post('/webhooks/cms', express.json(), (req, res) => {
  if (!verifyHmac(req)) return res.sendStatus(403);
  cache.flush();               // bust cached posts
  res.json({ ok: true });
});

An editor publishes, the CMS pings Express, the stale cache clears immediately. The HMAC signature ensures only your CMS can trigger it.

Express content options compared

Option Language Editor for non-devs You maintain
Hand-built (Mongoose/Prisma) JS ⚠️ Data-shaped Everything
Strapi (Node CMS) JS ✅ Full admin The CMS + app
UnfoldCMS (headless) PHP (API) ✅ Full admin Just the fetch

A Node-native CMS like Strapi keeps everything in JavaScript — good if you never want a second runtime, at the cost of running and maintaining it. UnfoldCMS is PHP, so it's a different runtime, but content-feature maintenance is zero on your side.

Tradeoffs to weigh

  • A second runtime. UnfoldCMS is a PHP app you host. You don't write PHP, but you operate it. If your team runs only Node, Strapi may fit better despite its heavier admin.
  • A network hop. Content comes from an API call; caching hides the cost.
  • No revision history. UnfoldCMS doesn't keep past versions of posts.
  • REST only, no GraphQL.
  • Preview needs wiring. Drafts 404 on the public API.

FAQ

Does Express have a built-in CMS? No. Express is a minimal framework with no CMS or content model. You build one (with a database ORM) or connect a headless CMS via its API.

Can a Node app use a PHP-based CMS? Yes. In a headless setup the CMS is an HTTP service. Express calls it with the built-in fetch and never touches PHP — the CMS's language is irrelevant.

Should I use Strapi or a PHP-based CMS with Express? Strapi if you want everything in Node and don't mind running it. UnfoldCMS if you're fine with a PHP runtime and want zero content-feature maintenance and a one-time cost.

How do I keep it fast? Cache CMS responses and bust the cache via a webhook when the CMS fires a publish event.

Bottom line

Express gives you routing and nothing else, so content means building a backend or going headless. A self-hosted headless CMS hands Express clean JSON over the built-in fetch, gives non-developers a real editor, and keeps Express as your application server. It's a second runtime (PHP) to operate — Strapi keeps you in Node if that matters more — but either way you skip reimplementing publishing, media, and SEO.

See the demo or read what is a content API.

Related: CMS for Next.js · What is a content API · CMS vs custom build

Free & Open Source

Own your CMS. No subscriptions.

Unfold CMS is free to download and self-host. Built on Laravel + React, full source code included.

Share this post:

Discussion

Comments (0)

Leave a Comment

Please log in to leave a comment.

Don't have an account? Register here

No comments yet. Be the first to share your thoughts!

Keep Reading

Related Posts

Back to all posts