Features and limitations
Evaluating an edge is mostly a question of what it does not do, because that is what lands back on your team. This page is the inventory, with links to the detail. If a term is unfamiliar, the glossary defines the vocabulary these docs use.
The feature matrix
Section titled “The feature matrix”A request is GraphQL when GraphPilot parses a GraphQL operation from it: its path is one of
request.graphql_paths (/graphql by default) and it carries an operation. Every other request
is plain HTTP, and gets everything in the first column. See how GraphPilot
works.
Caching
Section titled “Caching”| Feature | Plain HTTP | GraphQL |
|---|---|---|
Caching from your origin’s Cache-, including s-maxage and stale- | Yes | Yes |
Lifetimes and audiences from the schema with @cacheControl | No | Yes |
| Cache key | Method, URI with query string, and request body | Query text, operation name and variables, whatever the method |
Error responses (4xx, 5xx) with a storable Cache- | Not stored unless you opt in | Not stored unless you opt in |
Partitioning per caller or per group: private, vary rules, verified claims, headers, cookies, URL parameters | Yes | Yes |
| Named scopes, such as one entry per organization, declared on schema types | No, use a vary rule | Yes |
| Authenticated requests, following RFC 9111 §3.5 | Yes | Yes |
Your origin’s Vary | Yes | Yes |
| Bypass for preview or editor traffic | Yes | Yes |
| Request collapsing | Yes | Yes |
gp-cache status headers | Yes | Yes |
| Response body | Passed through byte for byte | Key fields GraphPilot added are removed |
Invalidation
Section titled “Invalidation”| Feature | Plain HTTP | GraphQL |
|---|---|---|
Tagging from your origin with a Surrogate- response header | Yes, on GET and HEAD | Yes, on queries |
Purging from your origin with a Surrogate- header on a write’s response | Yes, on POST, PUT, PATCH and DELETE | Yes, on mutations |
| Surrogate keys derived from the schema, with selection set expansion | No | Yes |
| Automatic purging of what a mutation returned | No | Yes |
The purge API and gpilot purge, hard or soft: by key, by cache key, by request id, or everything | Yes | Yes, and by query hash |
| Purges that reach every edge location | Yes | Yes |
Security
Section titled “Security”| Feature | Plain HTTP | GraphQL |
|---|---|---|
| JWT verification and opaque API keys at the edge | Yes | Yes |
| Request signing and TLS to your origin | Yes | Yes |
| Published edge IP ranges for your firewall | Yes | Yes |
| Request body limit | Yes | Yes |
| Header and cookie filters | Yes | Yes |
| Client profiles | Identification, refusing unknown clients, minimum version, body limit, cache settings | Yes, plus depth, size, selection and persisted-query rules |
| Persisted queries and trusted documents | No | Yes |
| Limits on query depth, document size and selection count | No | Yes |
| Introspection blocked by default | No | Yes |
| GraphQL batches and multipart uploads | Not applicable | Refused unless you forward them unchecked with request. |
Operating it
Section titled “Operating it”| Feature | Plain HTTP | GraphQL |
|---|---|---|
| What a deployment needs | gpilot.toml only | gpilot.toml and schema. |
| Deploy findings | Config findings, without a schema | Schema and config findings |
| Origin timeouts | Yes | Yes |
Retries, off until you enable [origin. | GET, HEAD and OPTIONS; other methods only with retry_ | Queries; mutations only with retry_ |
| Compression toward the client | Yes | Yes |
| WebSocket upgrades | Yes | Yes |
| Per-request telemetry in Insights, on every plan | Yes, without the GraphQL fields | Yes |
| Codes on every refusal | Yes, as application/ | Yes, as a GraphQL error |
| Custom domains with managed certificates, from Jet (Starter) | Yes | Yes |
| Redeploying an earlier deployment from the portal | Yes | Yes |
Shielding, on paid plans, when you set origin. | GET and HEAD only | Queries, mutations and persisted-query hashes, not subscriptions |
| Lean mode when the Glider (Free) allowance is used up, a payment is overdue or GraphPilot pauses the account: no edge caching, no shielding, nothing recorded | Yes | Yes |
What it deliberately leaves to you
Section titled “What it deliberately leaves to you”These are not gaps. They are decisions, and each one is a decision about who is the authority.
Authorization. GraphPilot verifies who is calling. It does not decide what they may see, and on a cache hit your origin is not consulted at all. An entry keyed to one caller keeps being served to that caller until it expires or is purged, so revoking access is two steps. Detail.
Whether a response may be cached. GraphPilot never decides on its own that something is reusable. Either your origin says so or, for a GraphQL API, your schema does. A service that says neither caches nothing, which is the safe default rather than an oversight.
Query validation. For a GraphQL operation, the edge parses and plans; it does not validate the operation against your schema. Your origin remains the authority on what is a valid request.
Verifying GraphPilot’s signature. GraphPilot signs every forwarded request. Whether your origin checks it is yours to implement, and until it does, your origin is reachable by anyone who learns its address. This is the first item on the go-live checklist.
WebSocket traffic past the handshake. The WebSocket upgrade goes through the whole pipeline; the messages after it are a tunnel. Everything the edge guarantees stops at the upgrade, the query depth, document and introspection checks included. Detail.
Composing subgraphs. GraphPilot does not compose a federated graph. Put it in front of your federation router or gateway as one service, and it caches and protects the composed graph like any other GraphQL API. This works on every plan. Detail.
Current limitations
Section titled “Current limitations”| Not available | Consequence |
|---|---|
@cacheControl cannot express a shared-cache lifetime | maxAge sets both how long GraphPilot keeps the entry and the max-age of any downstream header; a separate edge lifetime needs s-maxage from an origin header |
| No conditional revalidation | no-cache is refused rather than stored-and-revalidated, and a background refresh is a full request |
| Schema-declared invalidation on mutations | A mutation invalidates what it returned; to invalidate more, name a key on the response header |
| Field-level authorization from the schema | Authorization stays with your origin |
| Mutual TLS to the origin, or a per-service source address | An IP allowlist admits the whole edge network, not only your service |
| A staging-to-production promotion flow | A staging service is a second service you deploy to first, which needs a plan that includes more than one service (see pricing) |
| Rate limiting and per-caller quotas | No request cap and no per-second limit at the edge; see quotas for what plan limits do |
| Query cost analysis | Depth, document size and selection count are limited; per-field cost is not |
| Soft purging from the automatic path | Automatic invalidation is always a hard purge |
| Single sign-on (SSO/SAML) and an audit log | Not available yet |