@studio-fes/layer-craft

Nuxt layer for Craft CMS integration with GraphQL and advanced caching capabilities. Extends `@studio-fes/layer-base`.
7,897
Install
npm install @studio-fes/layer-craft
Latest Version:0.5.2
License:MIT
Last Updated:Oct 20, 2026
Links: npm
Maintainer: plcdnl

@studio-fes/layer-craft

Nuxt layer for Craft CMS integration with GraphQL and advanced caching capabilities. Extends @studio-fes/layer-base.

Features

๐Ÿ”Œ GraphQL Integration

  • nuxt-graphql-middleware: Full-featured GraphQL client
  • Automatic Query Imports: Auto-import .gql files from queries/ directories
  • Type-Safe: Full TypeScript support with generated types
  • Schema Download: Automatic GraphQL schema download in development

โšก Caching

  • nuxt-multi-cache: Multi-level caching system
  • Smart Cache Tags: Automatic cache tag generation
  • Server-side Caching: Optimize performance with intelligent caching

๐ŸŽจ Craft-Specific Features

  • CraftImage Component: Optimized image handling for Craft assets
  • SEO Composables: Easy SEO management with useCraftSEO
  • Org Schema Composables: schema.org / Nuxt SEO structured data from the Craft Toolkit plugin's "Org Schema" panel and field, via useCraftOrgSchemaIdentity and useCraftOrgSchema
  • Entry Composables: Simplified entry fetching with useCraftEntry
  • Preview Mode: Built-in Craft preview token support

Installation

npm install @studio-fes/layer-craft

This will automatically include @studio-fes/layer-base and its features.

Usage

Extend this layer in your nuxt.config.ts:

export default defineNuxtConfig({
  extends: ['@studio-fes/layer-craft'],
  
  graphqlMiddleware: {
    downloadSchema: 'dev-only',
  }
})

Environment Variables

# obbligatoria, letta al build: da qui il layer ricava l'endpoint GraphQL (`<url>/graphql`)
# e il dominio ammesso per le immagini con ipx (`image.domains`)
NUXT_PUBLIC_BACKEND_URL=https://your-craft-site.com
NUXT_CACHE_ENABLED=true
# opzionale: cache dati su Redis (runtime)
NUXT_REDIS_URL=redis://:password@host:6379/0
# opzionale: header CDN per Cloudflare
NUXT_CDN_CACHE_ENABLED=true

NUXT_GRAPHQL_MIDDLEWARE_GRAPHQL_ENDPOINT e NUXT_PUBLIC_GRAPHQL_ENDPOINT non servono: l'endpoint viene da NUXT_PUBLIC_BACKEND_URL. Per le variabili delle immagini e NUXT_PUBLIC_BASE_URL vedi il README di @studio-fes/layer-base.

Cache

  • Driver: con NUXT_REDIS_URL la cache dati usa Redis; altrimenti il disco (.nuxt-multi-cache) se NUXT_CACHE_ENABLED=true (letta al build, a runtime NUXT_MULTI_CACHE_ENABLED); altrimenti nessuna cache.

  • CDN (Cloudflare): spento di default. Si attiva con NUXT_CDN_CACHE_ENABLED=true (runtime) oppure nel progetto:

    export default defineNuxtConfig({
      runtimeConfig: { cdnCacheEnabled: true },
    })
    

    Con la cache dati attiva, le query emettono CDN-Cache-Control: max-age=300, stale-while-revalidate=86400 e Cache-Tag, che il plugin graphql-cdn-headers porta sulla pagina e sul suo _payload.json; anteprime, errori e 404 sono private. Cache-Tag รจ separato da virgole, come vuole Cloudflare (plugin server/plugins/cloudflare-cache-tags.ts). Il resto รจ fuori dal layer, in Craft (craft-toolkit โ†’ Toolkit โ†’ Cloudflare): lรฌ si accende il purge per tag, Check edge cache verifica header, proxy e Cache Rule, e Create Cache Rule crea la regola via API (serve un token con "Zone โ†’ Cache Rules โ†’ Edit"; altrimenti la regola si crea a mano sul dashboard Cloudflare). Senza proxy Cloudflare gli header vengono ignorati.

  • server/multiCache.serverOptions.ts va nel progetto: nuxt-multi-cache non lo cerca nei layer. Basta riesportare quello del layer:

    export { default } from '@studio-fes/layer-craft/server/multiCache.serverOptions'
    
  • Le API di purge (multiCache.api, con il token) restano nella config del progetto.

Components

<CraftImage>

Optimized image component for Craft CMS assets with transforms.

<template>
  <CraftImage 
    :image="entry.image" 
    :transform="{ width: 800, height: 600 }" 
    alt="Image description"
  />
</template>

Composables

useCraftEntry()

Fetch Craft entries with caching and error handling.

const { data: entry, error } = await useCraftEntry({
  slug: route.params.slug,
  section: 'pages'
})

useCraftSEO()

Generate SEO metadata from Craft entry data.

const { title, description, image } = useCraftSEO(entry)

useHead({
  title: title,
  meta: [
    { name: 'description', content: description }
  ]
})

useCraftOrgSchemaIdentity()

Registers the site-wide schema.org identity from the Craft Toolkit plugin's Org Schema CP panel (orgSchema(site: ...) query) โ€” Organization, LocalBusiness, MedicalClinic, Person, etc. Typically called once, from an app-level plugin:

// app/plugins/org-schema.server.ts
export default defineNuxtPlugin({
  name: 'org-schema',
  enforce: 'pre',
  dependsOn: ['nuxt-graphql-middleware-provide-state'],
  async setup() {
    const { data } = await useGraphqlQuery('OrgSchema', { site: 'default' })
    useCraftOrgSchemaIdentity(data.orgSchema)
  },
})

For a LocalBusiness-like identity with multiple addresses (a multi-clinic/multi-branch business), this registers one schema.org node per address โ€” matching Google's guidance for multi-location businesses โ€” each with its own priceRange/openingHours.

useCraftOrgSchema()

Registers an entry's Org Schema field pieces (Article, Product, Event, MedicalClinic, ...) as schema.org nodes:

const page = await useCraftEntry<PageEntryFragment>({ slug: [route.params.slug] })
useCraftSEO(page)
useCraftOrgSchema(page)

This expects the entry query to select the field's json property (the piece pre-merged with its @type, ready to parse):

fragment OrgSchemaFieldPieces on ToolkitOrgSchemaFieldPiece {
  json
}

fragment PageEntry on pageDefault_Entry {
  ...EntryInterfaceType
  orgSchema {
    ...OrgSchemaFieldPieces
  }
}

GraphQL Queries

Place your .gql files in the queries/ directory:

# queries/Entry.gql
query Entry($slug: [String]) {
  entry(slug: $slug) {
    id
    title
    ... on pages_page_Entry {
      content
      seoTitle
      seoDescription
    }
  }
}

Use them in your components:

const { data } = await useAsyncQuery(Entry, {
  slug: route.params.slug
})

Server Utils

createCacheTags()

Generates the cache tags (data cache + CDN Cache-Tag) for every cacheable GraphQL query. By default it adds:

  • the operation name (Entry, Entries, โ€ฆ)
  • entry:<id> for every entry returned
  • archive:<sectionHandle|typeHandle> for every entry returned by Entries
Extending the tags: cacheTags.serverOptions.ts

Don't override the util: create server/cacheTags.serverOptions.ts in the project (same convention as graphqlMiddleware.serverOptions.ts) and return the extra tags from extendTags.

// server/cacheTags.serverOptions.ts
export default defineCacheTagsServerOptions({
  extendTags({ operationName, result }) {
    const entry = result?.data?.entry

    // archive pages: tagged with the archived sections, so they get purged
    // together with the `Entries` queries of the same sections
    if (operationName === 'Entry' && entry?.__typename === 'pageArchive_Entry') {
      return [entry.archiveType].flat().filter(Boolean).map((type: any) => `archive:${type?.handle ?? type}`)
    }
  },
})

extendTags receives { operationName, result, tags, event }, can be async and can also remove tags by mutating tags. Files found in every layer are applied in order (layers first, project last). Restart the dev server after creating the file.

Preview Mode

The layer includes automatic support for Craft's preview tokens. Preview requests are handled automatically through the preview plugin.

Configuration

The layer extends the base configuration with:

{
  modules: [
    'nuxt-graphql-middleware',
    'nuxt-multi-cache'
  ],
  graphqlMiddleware: {
    serverApiPrefix: '/gql',
    clientCache: {
      enabled: true
    },
    autoImportPatterns: ['./queries/**/*.gql']
  }
}

Development

# Install dependencies
pnpm install

# Develop with playground
pnpm dev

# Download GraphQL schema
# Set graphqlEndpoint in nuxt.config.ts, then run:
pnpm dev

# Build
pnpm build

# Lint
pnpm lint

Example Usage

See the Craft starter for a complete implementation.

Extending

You can further customize this layer:

export default defineNuxtConfig({
  extends: ['@studio-fes/layer-craft'],
  
  graphqlMiddleware: {
    downloadSchema: 'dev-only',
    clientCache: {
      enabled: true,
      maxAge: 1000 * 60 * 5 // 5 minutes
    }
  },
  
  runtimeConfig: {
    multiCacheEnabled: true
  }
})

License

MIT

Related Packages

@studio-fes/layer-strapi

Create Nuxt extendable layer with this GitHub template.

7,092
@studio-fes/iubenda

Nuxt module for iubenda Cookie Solution โ€” locale-aware config, script loading vi...

1,180
@studio-fes/layer-base

Base Nuxt layer with shared utilities, components, and configurations for buildi...

8,749
@studio-fes/create-fes-app

Interactive wizard to scaffold a new project from the studio fes Nuxt layers sta...

2,277

Version History

Version Released License
0.5.2 MIT
0.5.1 MIT
0.5.0 MIT
0.4.11 MIT
0.4.10 MIT