GraphQL caching problems, one at a time
Caching GraphQL at the edge is easy until the data changes. Each section below takes one problem: what goes wrong, and what GraphPilot does about it, with a short clip and the docs page that goes deeper.
When a query doesn't ask for id
The problem
A page asks only for a book's title and price. The cached answer then holds no key for the book, so a purge for that book has nothing to match, and the old price stays until the entry expires.
With GraphPilot
GraphPilot adds the missing key fields to the request it sends to your origin, tags the cached answer with them, and removes them again before your client sees the response. The next purge for the book finds the answer.
Mutations clear what they change
The problem
With lifetimes alone, a cached answer stays stale until it expires. The usual way out is to flush everything on every write, and the origin pays for it.
With GraphPilot
A mutation sent through GraphPilot purges the cached responses that contained the objects it returned, by their keys, at every edge location, with no purge call. Everything else stays cached. Data changed outside GraphPilot is purged by key through the header, the API or the CLI.
Data changed somewhere else
The problem
An import job or a CMS changes data without going through your GraphQL API, so no mutation passes the cache and nothing tells it what changed.
With GraphPilot
Name the key in a Surrogate-Key header on your origin's response, call the purge API or gpilot purge from a webhook or a cron job, or purge in the Cockpit. A purge can name a surrogate key, a cache key, a query hash or everything.
Caching that reads your schema
The problem
Hand-written cache rules grow into a second schema you have to keep in sync: a type gets renamed in the code, a new type has no rule yet, and nobody notices until a purge misses.
With GraphPilot
GraphQL was built around a schema from day one, and GraphPilot reads caching from it. A field of type ID or named id is a key on its own; register your own scalar, such as ISBN, with @surrogateKey on the scalar or one line in gpilot.toml, and every field of that type is a key too. @surrogateKey also marks a key field, a composite key, a value objects share, or a static group you purge by name. Put a key on an interface or a union once, and every type that has the field carries it. @cacheControl sets the lifetime next to it. The rules ship, get reviewed and change with your code.
Caching rules per team
The problem
In a supergraph, several teams own the types. A central list of cache rules belongs to none of them, every change waits for whoever owns the list, and it drifts as the subgraphs change.
With GraphPilot
Your subgraphs already declare their entity keys with @key. GraphPilot reads them from the supergraph you deploy, so they become cache keys without a second list. Extra keys or cache hints written in a subgraph need @composeDirective so composition keeps them. A mutation purges what it returned, whichever subgraph resolved it.
GraphQL and REST, one cache
The problem
Few APIs are only GraphQL: product pages come from REST, search and images from plain HTTP. A cache that understands only GraphQL passes those requests through to your origin, every time.
With GraphPilot
GraphPilot caches both in the same service. For a plain HTTP request, your origin decides with the headers it already sends: Cache-Control sets the lifetime, a Surrogate-Key header tags the response, and the same header on a write's response purges what it names, at every edge location. Those keys share one space with the keys GraphPilot derives for GraphQL, so a REST write that names a book's key clears the cached GraphQL answers for that book as well.
The empty list
The problem
An empty list holds no objects, so it carries no object keys: once cached, "no reviews yet" can outlive the first review. And a new item in a list that already had items is in no cached answer yet, so its own key clears nothing.
With GraphPilot
GraphPilot tags an empty list with its type, so a mutation that returns that type clears it. For a list with items, a static key on the list field, named in the Surrogate-Key header of the create's response, clears the list; or the mutation returns the parent the list hangs off.
Who sees what
The problem
Members see member prices, guests see list prices. Cache the page once, and one audience can get the other's prices; turn caching off, and every request goes to your origin again.
With GraphPilot
Say who shares an answer right in your schema: @cacheControl with PUBLIC, PRIVATE, or an audience you name, such as a membership tier, in your own CacheControlScope enum. Your configuration says where that audience comes from, for example a claim of the token GraphPilot verified at the edge. GraphPilot works out each response's audience from what the query selected, so members and guests each get their own cached answer.
Rules in one line
The problem
Real rules for splitting the cache combine values: one shared answer per customer and language, a preview for editors that skips the cache, a user ID you'd rather keep out of your logs.
With GraphPilot
Radiogram, GraphPilot's policy expression language, reads verified token claims next to headers, cookies and URL parameters, joins and normalizes them, compares versions and can turn a user ID into a pseudonym, in one line of configuration.
Know which app is calling
The problem
A web app, mobile apps and an internal dashboard share one API. An old app version keeps sending a query you retired, and you can't tell which client it is, or turn it away.
With GraphPilot
A client profile names each client with a Radiogram rule on a header, a verified claim or the User-Agent, and records its name and version on every request. Each client can get its own limits, and with a minimum version, older releases are refused at the edge before they reach your origin.
Record only what you need
The problem
Health probes fill the request log every few seconds, and your privacy rules ask you to keep as little about end users as you can.
With GraphPilot
One Radiogram rule decides which requests Insights records, or records none. The client IP address is kept in full, truncated or not at all. A request left out is still served and billed, without its path, query or caller.
Comparing managed GraphQL caches? See how GraphPilot and Stellate compare, with a source for every statement.
Cleared for takeoff
Start on Glider (Free).
No credit card, no commitment.
Prefer to talk first? Book a call