Skip to content

Other servers

GraphPilot works with any origin that speaks HTTP. The pages for Apollo Server, GraphQL Yoga, PostGraphile and federation routers show the steps for those servers. This page gives the same four steps in general form, then what differs on a few other servers.

  1. Export the schema. A GraphQL service deploys a schema.graphql next to its gpilot.toml, and it has to be the schema your server runs with its directives left in, because @cacheControl and @surrogateKey are read from that file. Most printers drop applied directives unless you ask for them. Export it before every deployment, for example from CI, and deploy it before the server that adds fields to it: a query selecting a field the deployed schema does not declare is never cached. See deploy the schema before the origin.
  2. Make responses cacheable. Either your origin sends Cache-Control, or your schema carries @cacheControl hints and your origin sends no Cache-Control of its own. An origin header that states a lifetime always wins; see how long an entry lives. Check what your server actually sends before you decide: some frameworks and hosting platforms add a header you did not write, see why is nothing cached?
  3. Verify the request signature, so your origin refuses anything that did not come through GraphPilot. The scheme and verifiers for Node.js and Python are on verify the signature on your origin. Hash the raw body as received, before your framework parses it.
  4. Purge when data changes. A GraphQL mutation that returns the object it changed purges it on its own; see automatic invalidation. For changes that do not pass through GraphPilot, use the purge API or the CLI.

Hot Chocolate exports its schema from the command line once your app runs through RunWithGraphQLCommandsAsync:

Terminal window
dotnet run -- schema export --output schema.graphql

Its cache-control feature (AddCacheControl() with UseQueryCache()) computes a Cache-Control header from its own @cacheControl directive, including s-maxage from sharedMaxAge, public or private from scope, and Vary from vary. GraphPilot follows that header, so this is the simplest setup: keep it, and give your service a private policy for responses it marks private. If you would rather GraphPilot decide from the schema, for example to use named scopes, switch the header off and annotate the exported schema with GraphPilot’s @cacheControl.

graphql-java, and Spring for GraphQL on top of it, sends no Cache-Control of its own, so the schema decides unless your application adds a header.

  • Schema first: your .graphqls files are the schema. Declare @cacheControl in them as shown in its definition, so graphql-java accepts the annotations, and concatenate the files into schema.graphql.
  • Code first: print the built schema with directives included, for example new SchemaPrinter(SchemaPrinter.Options.defaultOptions().includeDirectives(true)).print(schema), and write the result to schema.graphql.

For the signature, read the raw body before Spring’s message converters do, for example in a servlet filter that wraps the request in a caching wrapper.

Strawberry exports its schema with its CLI:

Terminal window
strawberry export-schema app.schema:schema --output schema.graphql

Strawberry has no @cacheControl of its own. Declare it as a schema directive, so it appears in the exported file:

import strawberry
from strawberry.schema_directive import Location
@strawberry.schema_directive(
name="cacheControl",
locations=[Location.OBJECT, Location.FIELD_DEFINITION],
)
class CacheControl:
max_age: int = strawberry.directive_field(name="maxAge")
@strawberry.type(directives=[CacheControl(max_age=300)])
class Product:
id: strawberry.ID
name: str

Add a scope field in the same way for types that are not PUBLIC, with a CacheControlScope enum that matches your schema; see scopes in the schema. In ASGI frameworks, read the raw body for the signature before Strawberry parses it.

A REST or other plain HTTP API has no schema to annotate, so step 1 does not apply. Step 2 is always your origin’s Cache-Control. Deploy with gpilot.toml alone: without a schema file, gpilot deploy deploys the service as plain HTTP, with GraphQL features off.

Step 4 uses the Surrogate-Key header: on the response to a read it tags the entry, on the response to a write it purges. Caching a REST API walks through it.