Skip to content

How GraphPilot works

GraphPilot is a managed edge in front of one origin at a time: a plain HTTP API, a GraphQL API, or both on one host. Your clients send their requests to GraphPilot instead of to your server. GraphPilot answers what it can from its cache and forwards the rest to your server, called your origin.

GraphPilot runs on Fastly’s edge network. Each request is handled at an edge location near the client that sent it; see edge locations.

A hit never reaches your origin. Your config and schema reach the edge with a deploy, not with a request.

Nothing changes in your client’s code except the hostname it sends requests to. Your origin receives the client’s request with a signature added and, for a GraphQL query, the key fields GraphPilot needs; see how a request flows.

A service is one origin behind GraphPilot, with its own hostname, configuration and cache. What the origin speaks decides which features apply, not a setting on the service:

  • Plain HTTP. A REST API, a JSON or HTML backend, anything that answers HTTP requests.
  • GraphQL. A GraphQL server, a federation router or a gateway.
  • Both on one host. GraphPilot treats a request as a GraphQL operation when its path is one of request.graphql_paths (/graphql by default) and its body, or for a GET its query string, holds an operation. Every other request on the same hostname is plain HTTP. A path below a GraphQL path counts as that path (/graphql/health is /graphql, /graphql-admin is not), so keep your REST routes outside it: a body there that is not a GraphQL operation is refused with 415 by default. The other way round, a GraphQL operation sent to any other path is refused with 404 rather than forwarded past the GraphQL checks (uploads and subscriptions excepted).

These apply to a request whatever it carries, GraphQL or not.

GraphPilot never decides on its own that a response may be reused. Your origin says so with a Cache-Control header (s-maxage, max-age, stale-while-revalidate), and a response that does not allow it is passed through and not stored. Data that differs per caller can still be cached, in entries partitioned per caller, per organization or on any value you derive from a verified token, a header or a cookie. Simultaneous identical misses are collapsed into one origin request.

Responses carry a gp-cache header that says what the cache did (HIT, MISS, PASS or STALE), and a response that was not stored says why in gp-cache-reason.

Read more: when a response is cached, cache statuses, identifying callers.

Every cached response carries surrogate keys, labels that let you remove entries as a group. Your origin adds keys of its own with a Surrogate-Key response header, and you purge by key, by cache key, or everything, through the CLI, the portal or the GraphPilot API. A purge reaches every edge location.

Read more: invalidation, surrogate keys, the purge API.

GraphPilot stands between the internet and your origin, so it can refuse requests before they cost your origin anything:

  • Authentication. It verifies JWTs (and recognizes opaque API keys) at the edge, and can refuse requests whose credentials do not verify.
  • Request limits and filters. It enforces a request body limit, and header and cookie filters drop what should not reach your origin, your clients or a cache entry.
  • Request signing. It signs every request it forwards, so your origin can reject anything that did not come through GraphPilot.

It also applies your origin timeouts and retries, and records every request for Insights unless you choose to record fewer. A request GraphPilot refuses itself is answered with a stable reason code, in an application/problem+json body for a plain HTTP request.

Read more: authentication, request signing, origin timeouts, codes.

What GraphPilot adds for a GraphQL operation

Section titled “What GraphPilot adds for a GraphQL operation”

GraphQL only

When a request is a parsed GraphQL operation, GraphPilot knows what it asks for, and does more with it.

Cache by operation, with lifetimes from your schema

Section titled “Cache by operation, with lifetimes from your schema”

A query is keyed by its document, its operation name and its variables, so the same operation shares one entry whether it arrives as POST or GET. Mutations and subscriptions are never cached. Besides your origin’s Cache-Control, your schema can set lifetimes and audiences with @cacheControl, including named scopes such as one entry per organization.

Read more: how long an entry lives, who shares an entry.

Invalidate from your schema and your mutations

Section titled “Invalidate from your schema and your mutations”

GraphPilot reads your schema to find each type’s key fields (by default, fields named id, _id or key, and fields of type ID), and tags every cached response with keys such as Product:id:42 for the objects it contains. When a mutation returns an object, GraphPilot purges every cached response tagged with that object’s key, whichever query produced it. You write no purge calls for this, and clients do not have to select the key fields: GraphPilot adds them to the request and removes them from the response.

Read more: automatic invalidation, selection set expansion.

GraphPilot limits query depth, document size and selection count, blocks introspection by default, and supports automatic persisted queries (APQ) and a registry of trusted documents, so you can refuse any operation your clients did not ship.

Read more: hardening a public API, persisted queries.

TermWhat it is
OrganizationYour team’s workspace. It holds services, members and API keys. You create one after signing up, and can belong to several.
ServiceOne origin behind GraphPilot, with its own hostname, configuration and cache. It has a name, your origin URL, a service hostname (https://<subdomain>.<label>.graphpilot.cloud), a signing key, and its deployments.
Configgpilot.toml, the service’s settings: caching, partitioning, authentication, limits, persisted queries and more. Every setting has a default, so an empty file is valid, and a deployment can also run without one.
SchemaYour GraphQL API’s schema.graphql. GraphPilot reads it for types, key fields and caching directives. It does not validate operations against it: your origin stays the authority on what is a valid request.
DeploymentOne version of your config and, for a GraphQL API, your schema, built and rolled out to the edge. gpilot deploy creates one; the portal lists them under Deployments. A failed build never goes live.

A plain HTTP service deploys with its config alone. Without a schema file, gpilot deploy deploys it as plain HTTP, with GraphQL features off.

You create a service once, in the portal or with gpilot service create. After that, the work is a loop: change the config or the schema, deploy, and watch the result in the response headers and the portal.

You want toRead
Put GraphPilot in front of a GraphQL APIQuickstart: GraphQL
Put GraphPilot in front of a plain HTTP APIQuickstart: HTTP
See which features apply to plain HTTP and which to GraphQLFeatures and limitations
Follow a request through every stageHow a request flows
Understand what a deployment changes for live trafficDeployments
Look up a termGlossary