SKILL.md
DatoCMS Content Delivery API Skill
Expert at querying DatoCMS CDA (read-only GraphQL) using @datocms/cda-client. Follow steps in order.
Step 1: Detect Context
If context already established, skip broad detection. Re-inspect only when needed.
Examine project setup:
- Read
package.jsonfor@datocms/cda-client. Not installed?npm install @datocms/cda-client
- Find existing
executeQueryorrawExecuteQueryimports to understand usage patterns.
- Check
.env,.env.localfor DatoCMS API token:
- DATOCMSCDATOKEN - DATOCMSREADONLYTOKEN - DATOCMSAPITOKEN - NEXTPUBLICDATOCMSCDATOKEN
- Check framework (Next.js, Astro, Remix, Nuxt, SvelteKit) to determine server vs client queries. Don't expose tokens to browser unless using public read-only token.
- Check type generation setup:
- gql.tada: gql.tada in dependencies + initGraphQLTada call (typically lib/datocms/graphql.ts) - graphql-codegen: @graphql-codegen/cli in devDependencies + graphql.config.ts - Context only — match existing setup. Don't suggest setting up type generation.
CDA only needs read-only token. If DATOCMSAPITOKEN is also used for CMA, better suggesting a separate read-only token for CDA.
Step 2: Understand Task
Classify task:
| Category | Examples | | - | - | | Basic querying | Fetch by slug/ID, single-instance, list collections | | Filtering | Field filters, AND/OR, meta filters, deep filtering | | Pagination & ordering | Paginate, sort, tree/hierarchical | | Localization | Localized fields, fallback, all-locale values | | Modular content | Block fields with fragments, nested blocks | | Structured text | DAST value/blocks/links, render | | Images & media | Responsive images, imgix, placeholders, focal, video | | SEO & meta | _seoMetaTags, favicons, globalSeo, OG tags | | Draft/preview & caching | Draft mode, strict mode, cache tags, CDN, Content Link | | Type generation | gql.tada, graphql-codegen, schema types, typed queries |
Clear request? Proceed directly.
Step 3: Load References
Read relevant references from references/. Always load core client reference, then only relevant files.
Always load:
references/client-and-config.md— client setup, options, error handling, limits, scalars
Load per category:
| Task | Reference | | - | - | | Basic (records, collections, meta) | references/querying-basics.md | | Filtering (fields, AND/OR, deep, uploads) | references/filtering.md | | Pagination & ordering (first/skip, auto, trees) | references/pagination-and-ordering.md | | Localization | references/localization.md | | Modular content (blocks, fragments) | references/modular-content.md | | Structured text (DAST, render) | references/structured-text.md | | Images & media (responsiveImage, video) | references/images-and-videos.md | | SEO & meta | references/seo-and-meta.md | | Draft/preview, cache, environments, Content Link | references/draft-caching-environments.md | | Type generation (gql.tada, graphql-codegen, types) | references/type-generation.md | | gql.tada fragment discipline (masking, composition, page query) | references/fragment-patterns.md |
Cross-cutting:
- Filtering localized →
references/localization.md - Structured text with modular content →
references/modular-content.md - Images in blocks →
references/images-and-videos.md - Paginating filtered collection →
references/pagination-and-ordering.md - Complex nesting →
references/pagination-and-ordering.mdfor complexity costs - Writing/extending fragments in a
gql.tadaproject →references/fragment-patterns.md
Step 4: Mandatory Rules to Generate Code
Client Usage
- Default:
executeQueryfrom@datocms/cda-client(or repo's existing wrapper around it) - Use
buildRequestHeaders()/buildRequestInit()for frameworkfetchintegration, tagging, custom plumbing - Use
executeQueryWithAutoPaginationto fetch 500+ records - Use
rawExecuteQueryonly if response headers are needed (cache tags) - Store API token in env variable — never hardcode
GraphQL Queries
- Write as template literal strings (unless project uses
TypedDocumentNode/gql.tada) - Use GraphQL variables for all dynamic values — no string interpolation
- Request only needed fields — don't over-fetch
- Use DatoCMS custom scalars in declarations (
$first: IntType,$id: ItemId) - Prepend template string with
/ GraphQL /(comment-tagged templates) for syntax highlighting
const query = /* GraphQL */ `query { ... }`
Structured Text
- Query all relevant sub-fields (
value,blocks,links,inlineBlocks) — omitting causes silent data loss
Error Handling
- Catch
ApiErrorfrom@datocms/cda-clientat appropriate boundaries - No custom retry logic —
autoRetryhandles rate limits
TypeScript
- Follow strictness: no
as unknown as, no unnecessaryas - Let TypeScript infer types
- Use
import type { ... }for type-only imports
Step 5: Verify
Before presenting final code:
- Token — env variable, read permissions
- Error handling —
ApiErrorcaught at boundaries - Pagination — 500+ records? use
executeQueryWithAutoPagination - Draft mode —
includeDraftsintentional (not exposing unpublished in prod) excludeInvalid— recommend for stable schemas. Changing schema? usefilter: { _isValid: { eq: true } }instead- Type safety — no
asto silence errors - Imports — CDA from
@datocms/cda-client; keep generated GraphQL helpers if type-gen wired - Variables — all dynamic via GraphQL variables, no interpolation
- Structured text — all relevant sub-fields included
- Fetch integration — framework-native
fetch? usebuildRequestHeaders()/buildRequestInit() - Type generation — gql.tada or graphql-codegen? use project's
graphql()function, check scalar mappings - gql.tada fragment discipline — masked-by-default,
readFragment()at boundary, imports mirror spreads (seereferences/fragment-patterns.md)
Cross-Skill Routing
This skill covers reading via GraphQL CDA. Route to companion skill for:
| Condition | Route to | | - | - | | Mutating content, schema/uploads/webhooks, scripts (including REST queries) | datocms-cma | | Draft mode endpoints, Web Previews, Content Link, subscriptions, cache tags | datocms-frontend-integrations | | Building plugin | datocms-plugin |