CMS for Fresh (Deno): Headless Content with Native Fetch
Web-standard fetch, no Node-centric SDK
Fresh is Deno's full-stack web framework — island-based, no build step, server-rendered by default, running on the Deno runtime and Deno Deploy at the edge. It's fast and modern. What it doesn't give you is a place to store content. For a Fresh site with a blog, docs, or marketing pages, you need a CMS feeding it data. This post covers how to connect a headless CMS to Fresh, using Deno's built-in fetch, and why a self-hosted content layer suits the Deno philosophy of owning your runtime.
How a CMS plugs into Fresh
Fresh renders routes on the server (with islands for interactivity). A headless CMS becomes the data source: your route handler calls the CMS API, gets JSON, and Fresh renders it. Deno has fetch built in globally, so there's no import, no client library — the integration is one call in a handler.
// routes/blog/index.tsx
import { Handlers, PageProps } from "$fresh/server.ts";
export const handler: Handlers = {
async GET(_req, ctx) {
const res = await fetch("https://cms.yoursite.com/api/v1/posts?per_page=20");
const json = await res.json();
return ctx.render(json.data);
},
};
export default function BlogPage({ data }: PageProps) {
return (
<ul>
{data.map((p) => <li><a href={`/blog/${p.slug}`}>{p.title}</a></li>)}
</ul>
);
}
The CMS never touches your Deno runtime or your islands. It's a JSON source your handler reads.
Why self-hosted headless fits Deno/Fresh
Deno developers value a clean runtime, web-standard APIs, and control. A SaaS CMS with a Node-shaped SDK and vendor-owned data cuts against that. A self-hosted CMS with a plain REST API fits: web-standard fetch, JSON, no SDK.
UnfoldCMS is one option. It's a self-hosted CMS on Laravel with a REST API at /api/v1/*. There's no npm/SDK to import — which matters in Deno, where you'd rather not pull a Node-centric client. You use the global fetch Deno already provides.
Key fits:
- Web-standard
fetch— no SDK, no npm compatibility shim, just the global Deno API. - REST + JSON — clean response shapes; no GraphQL client to import.
- Self-hosted, pay once — your server, your data, no per-request metering.
Server-rendered vs. edge caching on Deno Deploy
Fresh renders on the server by default, often on Deno Deploy at the edge. Two ways to handle CMS content:
| Approach | How it works | When |
|---|---|---|
| Fetch per request | Handler calls CMS on each request | Content changes often; simplest |
| Cache at the edge | Cache CMS responses on Deno Deploy | High traffic; reduce API calls |
For a content site, edge-cache the CMS responses with a short TTL, and bust the cache when content changes.
Cache busting with webhooks
If you cache CMS data, clear it on publish. UnfoldCMS fires outgoing, HMAC-signed webhooks on publish/update/delete. Point one at a Fresh route that verifies the signature and clears the relevant cache:
Editor publishes
→ CMS sends signed webhook
→ Fresh route verifies HMAC, clears cached posts
→ Next request serves fresh content
The signature ensures only your CMS can trigger the bust.
Tradeoffs to know
- Preview needs wiring. Drafts 404 on the public API; previewing unpublished content means a token-authed route you build.
- No revision history. UnfoldCMS doesn't keep past versions of posts.
- You run two runtimes. Fresh is Deno; the CMS is a PHP app you host separately. You don't write PHP, but you operate the CMS box.
- REST only, no GraphQL. For Fresh handlers, REST +
fetchis the natural fit.
FAQ
Does UnfoldCMS work with Deno's Fresh framework?
Yes. Fresh route handlers use Deno's global fetch to call the CMS REST API and render the JSON. No SDK or npm import needed.
Do I need a special SDK for Deno?
No. There's no client library for any framework. You use the web-standard fetch that Deno provides globally — ideal for avoiding Node-centric dependencies.
Can I cache CMS content on Deno Deploy? Yes. Cache API responses at the edge with a short TTL and bust the cache via a webhook when content changes.
REST or GraphQL?
REST only. For Fresh's server handlers, REST plus fetch is the simplest integration.
Bottom line
Fresh gives you a modern, build-step-free Deno framework; a self-hosted headless CMS gives it content over a plain REST API. No SDK, no npm import — just Deno's global fetch in a route handler. Cache at the edge on Deno Deploy, bust it with a webhook on publish, and you have a content workflow that fits Deno's web-standard, own-your-runtime approach.
See the API in the demo or read the headless CMS complete guide.
Related: What is a content API · Headless CMS for Jamstack · CMS with API: REST vs GraphQL
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: