N
HeadlessCoreHeadless Core
Back to Home

GraphQL Reference

Endpoint: {WORDPRESS_URL}/graphql

Requires WPGraphQL plugin. Explore interactively with GraphiQL at the same URL (when enabled).

All fetchers live in nextjs-starter-kit/src/lib/graphql.ts.


Theme settings

query GetThemeSettings {
  themeSettings {
    headlessRedirectUrl
    enableHeadlessMode
    logoUrl
    copyrightText
    homeLayout
    primaryColor
    accentColor
    seoDescription
    defaultShareImage
    googleAnalyticsId
    maintenanceMode
    headerStyle
    footerStyle
    socialLinks {
      facebook
      twitter
      instagram
      linkedin
      github
    }
    license {
      valid
      tier
      customer
      expiresAt
    }
    navigationMenu {
      label
      url
      path
    }
    portfolioSkills {
      category
      items
    }
    portfolioTimeline {
      year
      role
      company
      description
    }
    siteProfile {
      tagline
      heroTitle
      heroHighlight
      heroSubtitle
      heroCtaLabel
      heroCtaUrl
      contactEmail
      contactPhone
      portfolioBio
      portfolioAvatar
    }
  }
}

Cache tag: theme-settings


Blog posts

All posts

query GetPosts {
  posts(first: 10, where: { status: PUBLISH }) {
    nodes {
      id
      title
      slug
      excerpt
      content
      date
      categories { nodes { name slug } }
      author { node { name avatar { url } } }
      featuredImage { node { sourceUrl } }
    }
  }
}

Paginated posts (with search)

Used by getBlogPostsPaginated():

query GetPostsPaginated($first: Int!, $offset: Int, $search: String, $categoryName: String) {
  posts(
    first: $first
    offset: $offset
    where: {
      status: PUBLISH
      search: $search
      categoryName: $categoryName
    }
  ) {
    pageInfo {
      offsetPagination {
        total
        hasMore
      }
    }
    nodes {
      id
      title
      slug
      excerpt
      date
      categories { nodes { name slug } }
      featuredImage { node { sourceUrl } }
    }
  }
}

SEO fields (seoTitle, seoDescription, seoImage)

Available on Post and Page. Auto-integrates with Yoast SEO or RankMath when either is active (checked via WPSEO_VERSION/ RANK_MATH_VERSION) — their own postmeta is read directly (_yoast_wpseo_title/_yoast_wpseo_metadesc/_yoast_wpseo_opengraph-image, or rank_math_title/rank_math_description/rank_math_facebook_image), so editors keep using whichever SEO plugin they already have and nothing needs re-entering. Falls back to this theme's own lightweight "Headless SEO" meta box (shown in wp-admin only when neither plugin is active) if neither is installed. See inc/cms-features.php.

query GetPostSeo {
  postBy(slug: "hello-world") {
    seoTitle
    seoDescription
    seoImage
  }
}

The Next.js side already reads these with a fallback chain (post.seoTitle || post.title, etc.) in src/app/blog/[slug]/page.tsx.

Categories

query GetCategories {
  categories(first: 50, where: { hideEmpty: true }) {
    nodes {
      name
      slug
      count
    }
  }
}

Cache tags: posts, post-{slug}


Corporate services

query GetServices {
  services(first: 20, where: { orderby: { field: MENU_ORDER, order: ASC } }) {
    nodes {
      id
      title
      content
      iconName
    }
  }
}

Cache tag: services


Case studies

query GetCaseStudies {
  portfolioItems(first: 20) {
    nodes {
      id
      title
      content
      itemCategory
      featuredImage { node { sourceUrl } }
    }
  }
}

Cache tag: portfolio-items


Portfolio projects

query GetProjects {
  projects(first: 20) {
    nodes {
      id
      title
      content
      projectCategory
      projectTags
      githubUrl
      liveUrl
      featuredImage { node { sourceUrl } }
    }
  }
}

Cache tag: projects


Testimonials

query GetTestimonials {
  testimonials(first: 20, where: { orderby: { field: MENU_ORDER, order: ASC } }) {
    nodes {
      id
      title
      content
      authorRole
      authorCompany
      rating
    }
  }
}

Cache tag: testimonials


Team members

query GetTeamMembers {
  teamMembers(first: 20, where: { orderby: { field: MENU_ORDER, order: ASC } }) {
    nodes {
      id
      title
      content
      memberRole
      featuredImage { node { sourceUrl } }
    }
  }
}

Cache tag: team-members


FAQ items

query GetFaqs {
  faqs(first: 50, where: { orderby: { field: MENU_ORDER, order: ASC } }) {
    nodes {
      id
      title
      content
    }
  }
}

Cache tag: faqs


Pricing plans

query GetPricingPlans {
  pricingPlans(first: 10, where: { orderby: { field: MENU_ORDER, order: ASC } }) {
    nodes {
      id
      title
      content
      planPrice
      planPeriod
      planFeatures
      planHighlighted
    }
  }
}

Cache tag: pricing-plans


Shop products (Headless Products CPT)

query GetHeadlessProducts {
  headlessProducts(first: 20, where: { orderby: { field: MENU_ORDER, order: ASC } }) {
    nodes {
      id
      title
      content
      productPrice
      productSalePrice
      productSku
      featuredImage { node { sourceUrl } }
    }
  }
}

Cache tag: headless-products

WooCommerce fallback

When no headless products exist, getShopProducts() queries WooCommerce products (requires WPGraphQL for WooCommerce):

query GetWooProducts {
  products(first: 20) {
    nodes {
      id
      databaseId
      name
      slug
      description
      ... on SimpleProduct {
        price
        regularPrice
        salePrice
        image { sourceUrl }
      }
    }
  }
}

databaseId (the raw numeric WooCommerce product ID) is fetched specifically for real checkout via the WooCommerce Store API — id alone is an opaque WPGraphQL global ID and can't be used with /wp-json/wc/store/v1/cart/add-item.


Dynamic pages

query GetPageByUri($uri: String!) {
  nodeByUri(uri: $uri) {
    __typename
    ... on Page {
      title
      content
      slug
    }
    ... on Post {
      title
      content
      slug
      date
    }
  }
}

Cache tag: page-{uri}


Contact form mutation

mutation SubmitContact($name: String!, $email: String!, $message: String!) {
  submitContactForm(input: { name: $name, email: $email, message: $message }) {
    success
    error
  }
}

On success:

  1. Submission saved to Contact Logs CPT
  2. Optional webhook POST to Slack/Discord
  3. wp_mail() to admin email

License delivery mutation

Called only by the Stripe webhook route (/api/stripe-webhook) — never from client code. See Stripe Licensing.

mutation DeliverLicenseKey($email: String!, $licenseKey: String!, $secret: String!) {
  deliverLicenseKey(input: { email: $email, licenseKey: $licenseKey, secret: $secret }) {
    success
    error
  }
}

secret must match headlesscore_license_delivery_secret (wp-admin → Headless Core → Settings → Licensing) — without a matching secret the mutation refuses to send, since GraphQL mutations have no built-in per-mutation auth in this schema. On success, emails the key to email via wp_mail().


AI content tool mutations

Called from the Gutenberg editor sidebar (assets/blocks/ai-tools.js) — see AI Content Tools. Both require a valid pro license and current_user_can('edit_posts') for the authenticated caller (license alone doesn't gate these, unlike other pro features, since they sit on the public GraphQL endpoint and could otherwise be called anonymously to burn API credits).

mutation GenerateSeoDescription($postId: Int!) {
  generateSeoDescription(input: { postId: $postId }) {
    description
    error
  }
}

mutation GenerateImageAltText($mediaId: Int!) {
  generateImageAltText(input: { mediaId: $mediaId }) {
    altText
    error
  }
}

Neither mutation persists its result itself — the editor JS writes description into the post excerpt and altText into the media item via WordPress core's own POST /wp/v2/media/{id} REST endpoint.


Cache tags summary

Tag Invalidated when
theme-settings Theme options saved
posts Any post published
post-{slug} Specific post
page-{uri} Specific page
services Service CPT updated
portfolio-items Case study updated
projects Project updated
testimonials Testimonial updated
team-members Team member updated
faqs FAQ updated
pricing-plans Pricing plan updated
headless-products Shop product updated

Extending the schema

  1. Register CPT or option in functions.php or inc/extended-cpts.php
  2. Add register_graphql_field() for custom meta
  3. Add fetcher in nextjs-starter-kit/src/lib/graphql.ts
  4. Update mockData.ts for offline dev
  5. Document in Content Model

Draft / preview queries

When draftMode() is enabled, fetchers:

  • Set cache: "no-store"
  • Pass Authorization: Bearer {WORDPRESS_AUTH_TOKEN}
  • Omit status: PUBLISH filter

Generate JWT via WPGraphQL login mutation or JWT Authentication plugin settings.