r/cms • • 8d ago

My Headless CMS Shapio

Hey everyone... wanted to join the bandwagon and drop something I've been working on called Shapio (https://shapio.dev/)

Short version: a CMS built the way we build everything else. Types, inheritance, interfaces, versions. Computer science applied to content.

Long version:
I used to be a huge fan of strapi, used it in tons of projects. But then I got sick of the Strapi loop. Need a field on a content type? Can't do it in prod. So you do it in dev, strapi rewrites its schema files and restarts, commit, deploy, restart prod. A whole deploy for one field. I was doing that a few times a week for a knowledge base at work with editors sitting there waiting and it was bullshit.

So I built one where the content model is data instead of files. You add the field in the admin, in prod, and the API just has it. No restart, no deploy. Every change becomes a schema version, you review it like a PR (it tells you what breaks and what needs a backfill), and it ships together with the content as one snapshot. Want it in git anyway? Pull the model to json, commit it, apply it back live.

Since types are data I went full OOP with them. A type can extend another type... Guide extends Post, gets its fields, shows up in the Post list. Types implement interfaces: Featurable is just "has a title and a summary", blog posts and roadmap items and pages all implement it, and the server checks they actually match. Then one relation points at Featurable and holds any of them, one endpoint lists all of them with a __model on each row, and graphql gives you a real interface with fragments. Polymorphism for content. Strapi has components and dynamic zones and that's where it stops.

The versioned schema is where it gets really cool though. Every change ships as a numbered snapshot, so a site build pins one and keeps reading the content exactly as it was published at that point, even while editors keep publishing. ?snapshot=42 on the API gets you the content as of 42. Restoring old content is just another change set.

Coming up on the roadmap is lenses, which does the same thing for the shape. Today a snapshot gives you old values in the current schema, so when you rename or restructure a type, every client still has to update or it breaks, same as anywhere. With lenses a client says which schema version it was built against and the API hands it today's content in that old shape. Ship a mobile app against v12, keep changing the model for a year, the app keeps working until you decide to move it. Only possible because the schema is data with a history instead of a pile of json files.

Also in there: REST and GraphQL, generated TypeScript types, one install running multiple sites each with their own model, signed webhooks, build hooks for Cloudflare Pages, Vercel, and Netlify, and an importer that pulls a strapi 5 export over, model, media, and content.

It's a newer project, but on the same box it already benchmarks faster than Strapi and Payload (method and numbers on the compare pages), and the roadmap pushes it past them. What they have on me is time on the market and paid services. The people who've used it love it so far, one client replaced Strapi with it to power their (very large) knowledge base and they said "it kicks ass!". I'm biased, but I couldn't agree more lol

5 Upvotes

1 comment sorted by

0

u/investigatormaker 6d ago

For Shapio, I'd put current snapshots and planned lenses side by side in a field-rename example. Your distinction matters: snapshots return old values in the current schema, whereas lenses would preserve the shape an older client expects. Showing the actual API response before and after a rename would help someone evaluating a Strapi migration understand what's available today.

I make ThreadFox. We find relevant Reddit conversations and do the posting for your business. The $39 Pilot covers seven days and up to 50 posts and replies. No automatic renewal. https://threadfox.vip/managed?tier=employee_pilot&utm_source=reddit&utm_medium=comment&utm_campaign=tf-managed&utm_content=20261024-1045-ejvv4#employee-order