Shopify vs. Headless CMS: A false binary
September 15th, 2025
Posted on October 10th, 2025 by Stefano Verna and Ronak Ganatra
The DatoCMS JavaScript client is now 100% type-safe! Records were the only part not completely typed before — we’ve taken care of that.
The CLI now perfectly bridges your schema and repo, generating complete TypeScript definitions mapped to your project automatically.
The client also introduces new utilities to simplify complex record management. Functions like duplicateBlockRecord()
, inspectItem()
, or mapBlocksInNonLocalizedFieldValue()
are now ready to use and handle nested blocks at any level.
All record-related docs have been rewritten with new TypeScript examples showcasing the updated helpers and workflows.
The missing piece is finally here. Records are no longer the odd ones out — they’re now fully typed, inferred, and validated by your compiler just like everything else. Say goodbye to unknown
s right where clear types were needed the most. The new JS client is TypeScript from the ground up, with a CLI that generates types directly from your project’s schema.
Now, your code “sees” your real model structure. Every field you define in your schema is mirrored as a TypeScript type for some very specific and juicy autocomplete magic. And if your schema changes, your compiler will be the first to know.
Before this release, the client was already rock-solid for the Content Management API — migrations, schema updates, all well-typed and reliable — but records were the weak spot. Because every model in the CMS is unique, there wasn’t a single “record shape” the compiler could enforce. Fetching and updating records often meant handwritten definitions or falling back to any
, so schema changes could slip through until runtime. That’s the gap we’ve closed.
Now the client is fully type-safe, records included. Every method, every field, every filter — with inference, autocomplete, and compiler checks baked in.
If you’re deep into TypeScript, this finally feels… right.
This is where the CLI comes in, acting as a bridge between your repo and schema.
$ npx datocms schema:generate schema.ts
It introspects your project, grabs your schema, and generates TypeScript definitions for all your models and fields.
Change something in your schema? Run it again. Types update instantly. Aaaand the compiler immediately highlights stale code.
Here’s how it plays out:
📝 Rename a field? Switch description
to summary
in your Blog Post model, and suddenly every post.description
in your code lights up in red like a Christmas tree. Fix it once, move on.
❌ Remove a field? Your compiler calls you out before you even hit save. Not in production, not at runtime — right there in your editor.
⚡ Tweak your schema? Run the command again. Boom — fresh types, all synced up.
No extra steps. No hand-maintained files. Just a one-liner that keeps your schema and your code perfectly in tune.
The client now also ships with dozens of brand-new utilities that make complex record operations far simpler for things that used to take hours to deal with properly.
Functions like duplicateBlockRecord()
, inspectItem()
, or mapBlocksInNonLocalizedFieldValue()
are now ready to use. They manage nested blocks, relations, and localized fields automatically. We're also introducing the new SchemaRepository
class to have an efficient caching layer ready in your code, perfect for scenarios where you need to repeatedly access the same schema information.
If you’ve ever used tools like gql.tada, GraphQL Code Generator, or urql codegen, you know that warm, fuzzy feeling of fully typed queries and autocompleted fields. That’s exactly the kind of developer experience you get here — but for our REST Content Management API.
Your schema drives your code. Filters, order_by
, Structured Text, blocks and Modular Content — they’re all fully typed and checked against your actual models.
Typo in a field name? Won’t compile.
Trying to order by a field that doesn’t exist? Won’t compile.
Querying a key you deleted last Tuesday? Yeah, that won’t compile either.
And type-safety isn’t just for reading data. Creates, updates, and block operations get the same treatment — even in migrations. Just regenerate your types after running them, and you’re good.
No more silent mismatches. No more schema drift. No more runtime surprises.
These are big changes. The docs need to reflect all of it, of course, which is why they've been completely reworked for records. All record-related pages now show examples that combine full typing with the new helpers, so you can see exactly how to read, transform, and update content, with explanations that match how you’ll actually write code today.
Records, filtering, ordering, and Structured Text with blocks are shown exactly as you’ll use them in a more IRL context, with generated types in the imports and field names that match reality.
Dive in to the docs to check out the new examples and start working with the CLI and the new JS Client and let us know how things are for you!
Create a new record (9 new examples)
Update an existing record (11 new examples)
So there we have it. With the new updates, your schema and your code are finally in sync. Your compiler won’t let you ship broken assumptions. And your editor will autocomplete the fields you need without any docs detours. Everything in its right place. Like it was meant to be.
To take full advantage of all this, make sure you update to the latest versions of both the JavaScript client and the CLI. Here’s how to install them (assuming you use DatoCMS):
$ npm install @datocms/cma-client-node @datocms/cma-client-browser @datocms/cli --save-dev
Or globally (for the CLI):
$ npm install -g @datocms/cli
Once upgraded, you’ll be ready to generate types straight from your schema, benefit from strict record typings, and keep your workflows safe and predictable.
Check out other recent stories we've written on our blog