How I Fixed the Hashnode GraphQL API Stale Cache Bug (Stellate CDN)

Published: (February 23, 2026 at 12:29 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Cover image for How I Fixed the Hashnode GraphQL API Stale Cache Bug (Stellate CDN)

If you’re using Hashnode’s GraphQL API to fetch your blog posts for a custom frontend (like a React or Next.js portfolio), you’ve probably run into this incredibly frustrating issue: You publish a new post on Hashnode, but it doesn’t show up on your website.

You check the API payload, and it’s serving a stale list of posts. The new post is completely missing.

I spent hours debugging this, trying every cache‑busting trick in the book. Here’s what didn’t work, why it failed, and the actual simple solution.

The Setup

My portfolio runs on React (Vite) and uses Hashnode as a headless CMS. I fetch posts using fetch with the official Hashnode GraphQL endpoint: https://gql.hashnode.com.

const res = await fetch('https://gql.hashnode.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ query: POSTS_QUERY, variables }),
});

When I added a new post, my local dev server still only showed the old posts.

The Failed Attempts

Hashnode uses Stellate, a powerful GraphQL Edge CDN. Stellate sits between your frontend and Hashnode’s database, caching responses to make the API blazing fast. However, its caching mechanism is exceptionally aggressive.

Here are the standard tricks I tried to bypass the cache, all of which failed:

  • Setting standard HTTP headers: Adding Cache-Control: no-cache and Pragma: no-cache to the fetch call. Stellate ignores these from the client.
  • URL Query Params: Appending a timestamp ?_t=${Date.now()} to the endpoint URL. Since this is a POST request, Stellate keys the cache off the request body, not just the URL.
  • GraphQL extensions: Adding extensions: { cacheKey: Date.now() } to the JSON body. Stellate normalization strips this out.
  • Unknown GraphQL Variables: Injecting _cacheBust: Date.now() into the variables object. Stellate strips unknown variables.
  • GraphQL Comments: Injecting # ${Date.now()} directly into the query string. Stellate parses and normalizes the query string, stripping comments before hashing the cache key.

No matter what I did, inspecting the network tab always revealed the same mocking response header: gcdn-cache: HIT.

The Real Issue: Stellate Needs the id Field

The root cause isn’t that Stellate is ignoring your cache‑busting hacks; it’s that Stellate doesn’t know the data is stale.

Stellate automatically invalidates its cache when backend data changes (mutations). But to do this effectively across complex GraphQL graphs, it relies on a core concept: tracking Node IDs.

If your query doesn’t ask for the id of the entities it’s fetching, Stellate cannot trace the cached data back to the actual database records. When Hashnode updates its database, Stellate’s purge mechanism fires, but if your cached query didn’t include IDs, Stellate doesn’t know to invalidate that specific query.

This is a well‑known issue internally at Hashnode — they even created an ESLint plugin (require-id-when-available) for their own engineers to prevent it!

The Solution

The fix is almost disappointingly simple. You must include the id field in all your GraphQL queries and fragments related to Hashnode.

Original query

query Publication($host: String!, $first: Int!) {
  publication(host: $host) {
    posts(first: $first) {
      edges {
        node {
          slug
          title
          brief
          publishedAt
        }
      }
    }
  }
}

Updated query (with id)

query Publication($host: String!, $first: Int!) {
  publication(host: $host) {
    posts(first: $first) {
      edges {
        node {
          id  # <-- THE MAGIC FIX
          slug
          title
          brief
          publishedAt
        }
      }
    }
  }
}

Make sure to add id wherever you query entities: posts, post, seriesList, etc.

Once I updated my queries and refreshed, the CDN properly registered the unique records. New posts now invalidate the cache automatically, and the API returns fresh data immediately upon publishing.

Takeaway: When working with GraphQL APIs behind Edge CDNs like Stellate, always fetch the id. It’s not just good practice; it’s the anchor for their entire caching strategy.

0 views
Back to Blog

Related posts

Read more »