Futuristic edge computing server room with glowing world map

SonicJS: The Headless CMS That Lives on the Edge

The Orange Cat
The Orange Cat

Most headless CMS platforms still live in the old world: a Node.js process sitting in a single data center, waking up from cold sleep every time someone requests a page. SonicJS takes a fundamentally different approach. Built from the ground up for Cloudflare Workers, it runs your entire CMS at the edge across 300+ locations worldwide. The result is API response times measured in tens of milliseconds rather than seconds, with effectively zero cold starts. If you are already building on the Cloudflare ecosystem, SonicJS slots in like a missing puzzle piece.

Why the Edge Changes Everything

Traditional CMS platforms like Strapi or Payload run on a central server. A visitor in Tokyo hitting your API in Virginia adds hundreds of milliseconds of latency before the CMS even starts processing. SonicJS sidesteps this entirely by deploying your CMS code to every Cloudflare edge node. Your content API responds from whichever location is physically closest to the user.

The numbers tell the story: cold starts of 0-5ms compared to 500-2000ms for traditional Node.js CMS platforms, and API response times of 15-50ms. SonicJS achieves this through a three-tier caching architecture that checks memory first, then Cloudflare KV, and finally falls back to D1 (SQLite at the edge) only when necessary.

Under the Hood

SonicJS is built on a lean but powerful stack. Hono.js handles routing and request processing, Drizzle ORM provides type-safe database queries against Cloudflare D1, and R2 stores media files. The admin interface uses HTMX for dynamic interactions without shipping a heavy JavaScript framework to the browser. The entire thing is written in TypeScript with strict type safety throughout.

Installation

Getting started requires the CLI scaffold:

npx create-sonicjs@latest my-cms

Or if you prefer adding the core package to an existing Cloudflare Workers project:

npm install @sonicjs-cms/core
yarn add @sonicjs-cms/core

Spinning Up Your First Content API

Defining a Content Schema

SonicJS takes a configuration-over-UI approach. You define your content types in TypeScript, and the CMS generates the admin interface and API endpoints from those definitions.

import { defineContentType } from "@sonicjs-cms/core";

const blogPost = defineContentType({
  name: "blog-post",
  fields: {
    title: { type: "text", required: true },
    body: { type: "richtext" },
    publishedAt: { type: "date" },
    featured: { type: "boolean", default: false },
    category: {
      type: "select",
      options: ["Engineering", "Design", "Product"],
    },
  },
});

This gives you a fully typed content model. The admin panel picks up the field definitions automatically, rendering the appropriate editors for each type.

Wiring Up the Worker

Your Cloudflare Worker entry point initializes SonicJS with your content types and bindings:

import { createSonicApp } from "@sonicjs-cms/core";
import { blogPost } from "./schemas/blog-post";

export interface Env {
  DB: D1Database;
  CACHE: KVNamespace;
  STORAGE: R2Bucket;
}

const app = createSonicApp({
  contentTypes: [blogPost],
});

export default {
  fetch: app.fetch,
};

Run wrangler dev and you have a local development server with hot reload, a working admin panel, and a REST API for your content.

Querying Content

The generated API follows RESTful conventions. Fetching content from a frontend application is straightforward:

interface BlogPost {
  id: string;
  title: string;
  body: string;
  publishedAt: string;
  featured: boolean;
  category: string;
}

async function getFeaturedPosts(): Promise<BlogPost[]> {
  const response = await fetch(
    "https://my-cms.workers.dev/api/blog-post?featured=true&sort=-publishedAt"
  );
  const data = await response.json();
  return data.items;
}

Going Deeper at the Edge

The Three-Tier Cache

SonicJS does not just rely on D1 for every request. Its caching system is what makes those sub-50ms response times possible. Understanding how it works lets you tune performance for your specific use case.

import { createSonicApp, cacheConfig } from "@sonicjs-cms/core";

const app = createSonicApp({
  contentTypes: [blogPost],
  cache: cacheConfig({
    memory: {
      maxItems: 500,
      ttl: 60, // seconds
    },
    kv: {
      ttl: 3600, // one hour in KV
    },
    // D1 is the source of truth, always fresh
  }),
});

The first request for a piece of content hits D1. The response gets stored in both KV and in-memory cache. Subsequent requests from the same edge location resolve from memory in under 1ms. Requests from other locations hit KV first, which is globally distributed but slightly slower than memory. Content mutations automatically invalidate the relevant cache entries.

Content Workflows and Scheduling

For teams that need editorial control, SonicJS provides a workflow system that moves content through stages:

const article = defineContentType({
  name: "article",
  workflow: {
    stages: ["draft", "review", "published", "archived"],
    defaultStage: "draft",
  },
  scheduling: {
    enabled: true, // allows setting future publish/unpublish dates
  },
  versioning: {
    enabled: true,
    maxVersions: 10,
  },
  fields: {
    title: { type: "text", required: true },
    content: { type: "richtext" },
    author: { type: "text" },
  },
});

Content editors can move articles through the pipeline in the admin panel. The scheduling engine runs on the Worker itself, checking for scheduled transitions on each request with minimal overhead. Version history lets editors restore any previous state of a document.

Authentication and Access Control

SonicJS supports multiple authentication methods out of the box. You can configure password-based auth, one-time passwords, or magic links depending on your audience:

const app = createSonicApp({
  contentTypes: [article],
  auth: {
    methods: ["password", "magic-link"],
    roles: {
      editor: {
        permissions: ["read", "create", "update"],
        contentTypes: ["article"],
      },
      admin: {
        permissions: ["read", "create", "update", "delete"],
        contentTypes: ["*"],
      },
    },
  },
});

Role-based access control restricts what each user can do at the content type level. The auth layer integrates with the admin panel so editors only see the content and actions they have access to.

The Cloudflare Tradeoff

SonicJS is purpose-built for Cloudflare Workers. That is both its greatest strength and its most significant constraint. You get remarkable performance and a generous free tier (100,000 requests per day, 5GB of D1 storage), but you are locked into the Cloudflare ecosystem. There is no option to deploy on AWS Lambda, Vercel, or a traditional VPS. If your infrastructure lives elsewhere, SonicJS is not the right choice.

The community is also still growing. With around 1,500 GitHub stars and modest npm download numbers, you will not find the same depth of tutorials, plugins, and Stack Overflow answers as you would with Strapi or Payload. What you will find is an actively maintained project with rapid release cycles and a maintainer who ships multiple updates per month.

Conclusion

SonicJS fills a gap that no other production-ready CMS currently occupies: a fully-featured headless CMS that runs natively on Cloudflare Workers. For teams already invested in the Cloudflare stack, it eliminates the awkward middleman of running a traditional CMS server alongside their edge infrastructure. The three-tier caching, content workflows, and TypeScript-first design make it more than a proof of concept. If sub-100ms content delivery matters to your project and Cloudflare is your platform of choice, @sonicjs-cms/core deserves a serious look.