DEV Community

Cover image for Your content model is a schema whether you wrote one or not
Samer Alsayegh for Draftbase

Posted on Originally published at draftbase.co AI-assisted

Your content model is a schema whether you wrote one or not

You already have a content model. It's just scattered across a Notion doc, a Figma file, and whatever the person who built the editor form remembered that day. Nobody wrote it down as a schema, so nothing reads it, and nothing keeps the form, the validation, and your TypeScript interfaces pointing at the same fields.

Schema-driven content modeling means one schema definition drives three things: the editor UI, the write-time validation, and the generated TypeScript types. Change a field once and all three follow. That's the whole pitch. Draftbase calls these schemas templates and ships a draftbase-sync CLI that turns one into a .d.ts — one interface per template, required fields non-optional, everything else marked ?.

That optional marker is doing more work than it looks like. A hand-written interface makes everything optional because nobody wants to be wrong. A generated one only marks a field optional if the schema says so. That's the difference between a type that catches an undefined bug and a type that's decoration.

Here's the part most codegen posts skip: a generated .d.ts is a snapshot, not a guarantee. It describes the schema as of the last time you ran the CLI. Someone adds a required field in the dashboard on Wednesday, your build ran Monday, and TypeScript is now confidently green about a shape that no longer exists. Every UI-first CMS with codegen has this gap — Contentful, Draftbase, all of them. Code-first schemas (Payload, Sanity) swap it for a different tax: a deploy every time a content lead wants a field changed.

Two fixes, both cheap. Run codegen in CI, not just on a laptop — a type-check against last week's schema is theatre. And parse anything the generator hands you as unknown (that's how Draftbase types json fields, on purpose — no generator can honestly type a free-form blob) at the fetch boundary. Zod, a hand-written guard, whatever's already in the project. Three lines there beats a crash in a render tree.

Types vanish at runtime. The schema they came from doesn't have to.

Full breakdown — code-first vs UI-first schemas, the field-type-to-TypeScript mapping, and when a typed content API is overkill for a one-author blog: https://draftbase.co/content-modeling/guide/schema-driven-content-modeling

More on this in r/draftbase_cms: https://reddit.com/r/draftbase_cms

Top comments (0)