One URL
A CDN sees one blind endpoint
Every query and mutation shares one URL, with the operation and variables buried in the request. A CDN can't tell them apart, so read-heavy APIs hammer the origin all day.
Managed GraphQL edge cache
GraphPilot cuts your origin load, makes your app faster, clears what a mutation changed and needs only a new endpoint.
Drop GraphPilot in front of your GraphQL API. Repeat queries answer from the edge., and cached responses are purged when a mutation returns a changed object or you call the purge API.
Prefer to talk first? Book a call
Works in front of any GraphQL server, gateway or federated supergraph.
Free to start, no credit card required.
Video unavailable right now.
Watch the full explainer (8 min) (opens YouTube in a new tab)
measured p95 cache hit
How it was measured
Put a normal CDN in front of GraphQL and three problems surface at once.
One URL
Every query and mutation shares one URL, with the operation and variables buried in the request. A CDN can't tell them apart, so read-heavy APIs hammer the origin all day.
Stale data
Short TTLs throw away the hit rate; long TTLs serve stale data. A clock can't say which mutation changed which result, so you either under-cache or risk correctness.
1 → N caches
A single write invalidates entities, lists and derived results across many cached responses. Hand-written purge rules are fragile and rarely stay correct.
A reproducible demo benchmark, not a guaranteed service level. How it was measured
We set out to cache a GraphQL API at the edge and kept hitting the same wall: caching was never the hard part, keeping it correct was. So we built the layer we wanted.
Where we go further
the schema you write
the query, keyed either way
The backend team owns it
Cache and invalidation are directives next to the fields they govern, so the team that owns a type owns how it caches. No central config to coordinate.
And can rely on it
Forget the id in a query and caching still holds: GraphPilot injects it, so every cached response stays invalidatable.
In front of any gateway, we cache a federated graph as one. First-class, not an afterthought.
An empty result is a real cache entry, cleared when a mutation touches its type.
When a change bypasses GraphQL, purge by key or everything from the API, the CLI or the Cockpit.
No new gateway, no client code changes: clients only switch their endpoint. It sits in front of the API you already run.
See exactly which cache tags a mutation clears. Not a black box.
A data processing agreement under Art. 28 GDPR, and request telemetry stored in the EU for at most 90 days.
GraphPilot is a GraphQL-native proxy, written in Rust. It sits in front of your API and caches it for you, reading your schema to decide what to cache, how long to hold it, and what to clear when your data changes.
Client
Web · Mobile · Server
Origin
GraphQL · REST · HTTP
Cache hits answer at the edge. Only misses and writes reach your origin.
Send a mutation through GraphPilot and it purges the cached responses that contained the objects it returned, usually within seconds at every edge location, and shows you which keys it cleared. Data changed elsewhere you purge through the API or CLI, and when a mutation doesn't reveal everything it changed, the behaviours below close the gap, so you decide what a change clears.
Cached responses
The mutation only invalidates the entries it touches; each refills on its next request, not from the mutation.
When a change happens somewhere GraphPilot can't see, an event on your bus, a cron job, an admin action in another system, call the purge API with the keys to clear. Full, precise control whenever you need it.
Purge from your event bus the moment upstream data changes, with no GraphQL mutation involved.
Expire a set of keys on a cron or a job when a batch import or recompute finishes.
An admin action in another service clears exactly the right cached reads.
Purge exact cache tags, or clear everything of a type by typename.
a change from your system
When a query skips a key field, most caches still store the response but can't clear it when the data changes. GraphPilot injects the keys your schema needs, so every response stays precisely invalidatable, one entity or a whole group.
The query your client sends
injected by GraphPilotOne product, keyed by its id and its SKU, so either one invalidates it.
When a spike sends the same query all at once, GraphPilot collapses them into a single origin request. The first one fetches; the rest wait and share its result, so nothing stampedes your origin. Your database feels one hit, not a thousand, and stays calm exactly when traffic doesn't.
Personalized responses stay cacheable. Radiogramm, a small expression language built into GraphPilot, reads straight from each request (a JWT claim, a header, a cookie) and derives a cache key from it. Write one line, and the same query splits into the buckets your rule describes, each filed under its own key. Base a split that keeps data apart on verified token claims, since headers and cookies are what the caller sent.
Prefer to decide at the origin? Return a Vary response header and the same split happens server-side, no rule required.
jwt.sub ?? "public" // one bucket per signed-in userRadiogrammone query · separate buckets · no bucket sees another's data
Repeat reads are answered close to your users on a global network, so felt speed holds up worldwide, not just next to your origin.
Storing a GraphQL response takes an afternoon. Keeping every cached read correct as data changes, and as your schema keeps evolving, is a distributed-systems problem you would maintain for the life of the product.
weeks to build, then permanent maintenance
drop it in front, keep shipping features
Choosing between managed GraphQL caches? See how GraphPilot and Stellate compare, with a source for every statement.
Put GraphPilot in front of your GraphQL API and get edge speed with invalidation you can actually trust.
Prefer to talk first? Book a call