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.
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.
One service, one origin
Section titled “One service, one origin”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(/graphqlby default) and its body, or for aGETits 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/healthis/graphql,/graphql-adminis not), so keep your REST routes outside it: a body there that is not a GraphQL operation is refused with415by default. The other way round, a GraphQL operation sent to any other path is refused with404rather than forwarded past the GraphQL checks (uploads and subscriptions excepted).
What GraphPilot does for every request
Section titled “What GraphPilot does for every request”These apply to a request whatever it carries, GraphQL or not.
Cache what your origin allows
Section titled “Cache what your origin allows”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.
Tag and purge
Section titled “Tag and purge”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.
Protect your origin
Section titled “Protect your origin”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.
Bound what a client may ask
Section titled “Bound what a client may ask”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.
The pieces you work with
Section titled “The pieces you work with”| Term | What it is |
|---|---|
| Organization | Your team’s workspace. It holds services, members and API keys. You create one after signing up, and can belong to several. |
| Service | One origin behind GraphPilot, with its own hostname, configuration and cache. It has a name, your origin URL, a service hostname (https:/), a signing key, and its deployments. |
| Config | gpilot.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. |
| Schema | Your GraphQL API’s schema.. 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. |
| Deployment | One 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.
Where to go next
Section titled “Where to go next”| You want to | Read |
|---|---|
| Put GraphPilot in front of a GraphQL API | Quickstart: GraphQL |
| Put GraphPilot in front of a plain HTTP API | Quickstart: HTTP |
| See which features apply to plain HTTP and which to GraphQL | Features and limitations |
| Follow a request through every stage | How a request flows |
| Understand what a deployment changes for live traffic | Deployments |
| Look up a term | Glossary |