From 0565b2aacb4cb365500fd8a5cbc06aae4da28b60 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Tue, 7 Jan 2025 09:51:12 -0500 Subject: [PATCH] Improve documentation of `@cacheControl` (#6161) --- .../performance/response-caching.mdx | 198 +++++++++++++++++- 1 file changed, 196 insertions(+), 2 deletions(-) diff --git a/packages/web/docs/src/pages/docs/gateway/other-features/performance/response-caching.mdx b/packages/web/docs/src/pages/docs/gateway/other-features/performance/response-caching.mdx index 0d23ef6955..5bb96bed0a 100644 --- a/packages/web/docs/src/pages/docs/gateway/other-features/performance/response-caching.mdx +++ b/packages/web/docs/src/pages/docs/gateway/other-features/performance/response-caching.mdx @@ -64,8 +64,202 @@ The behaviour of this plugin can be configured by passing an object at the gatew The `@cacheControl` directive can be used to give to subgraphs the control over the cache behavior for the fields and types they are defining. You can add this directive during composition. -- [See here for Federation to learn more about the `@cacheControl` directive](https://www.apollographql.com/docs/federation/performance/caching/#using-cache-hints-with-subgraphs) -- [See here for GraphQL Mesh to learn more about the `@cacheControl` in subgraph definitions](/docs/gateway/other-features/performance/response-caching) +You can learn more about the behavior of the `@cacheControl` directive in the following section. If +you want to use the configuration API on the gateway, you can skip the `@cacheControl` directive +section. + +### `@cacheControl` directive's behavior + +This directive allows you to control response caching from the subgraph. But the behavior can be +different depending on the subgraph configuration. + +The following 3 section shows the ways of using the `@cacheControl` directive in different ways. The +usage examples in the other sections represent the third(last) use case in the following three. + +#### Apollo Server with `Cache-Control` header (This response cache plugin is not required) + +Apollo Server handles `@cacheControl` directives to set HTTP caching headers in the HTTP response to +the gateway. Then the gateway can cache the response based on these headers. +[Learn more about Apollo Server's cache control behavior](https://www.apollographql.com/docs/apollo-server/performance/caching). +In this case, gateway is still response of caching the response. Hive Gateway can handle http +caching headers using [HTTP Caching plugin](/docs/gateway/other-features/performance/http-caching). + +```mermaid +flowchart TD + A["GraphQL Client"] -->|"GraphQL Request"| B(Go shopping) + B("Hive Gateway") --> C{Let me think} + C -->|"Cache Hit"| D["Get the cached response from Cache Storage"] + C -->|"Not Cached"| E["Send HTTP Response Back"] + C{"Subgraph Executor"} + D ---|"Return the cached response"| C + E + E ---|"Get 'Cache-Control' header, and store the response for the following requests"| C{"Subgraph Executor gets the query"} +``` + +##### Example + +Let's say you have a subgraph that defines a `Post` type with a `@cacheControl` directive, your Hive +Gateway has [HTTP Caching plugin](/docs/gateway/other-features/performance/http-caching) enabled. + +```graphql +type Post @cacheControl(maxAge: 240) { + id: Int! + title: String +} +``` + +When the gateway receives a query that selects the `Post` type, it will cache the response for 240 +seconds. + +```graphql +query { + posts { + id + title + } +} +``` + +In this case, the gateway will cache the response for 240 seconds. If the same query is made within +240 seconds, the gateway will return the cached response. + +#### GraphQL Yoga with the response caching plugin (This response cache plugin is not required) + +On the other hand, GraphQL Yoga handles `@cacheControl` directives to configure the response caching +behavior rather than `Cache-Control` headers like Apollo Server. It leverages `ETag` headers to +cache the response and invalidate it by mutations. +[Learn more about GraphQL Yoga's response caching behavior](https://the-guild.dev/graphql/yoga-server/docs/features/response-caching). +So even if nothing is configured on the gateway but Yoga on the subgraph uses the response caching +plugin on its own. But this won't reduce the HTTP connection traffic in between gateway and +subgraph. + +```mermaid +flowchart TD + A["GraphQL Client"] + B(Go shopping) + B("Hive Gateway") + E["Send HTTP Response Back"] + E + E["Subgraph Yoga Server"] + E --- n1["Response Cache Plugin"] + n1 ---|"If Cached"| n2["Get the cached response from Cache Storage"] + n1 ---|"If Not Cached"| n3["Prepare the result by making database calls etc, and store it for the future calls"] + subgraph B["Hive Gateway"] + n5 + n4["Query Planner"] + end + n4 + n4 ---|"Generate Subgraph Query"| n5["Execution Engine"] + n5 + E + B + n5 + n5 + n5 ---|"HTTP Request"| E + A --- B +``` + +##### Example + +Let's say you have a subgraph that defines a `Post` type with a `@cacheControl` directive, your Hive +Gateway has [HTTP Caching plugin](/docs/gateway/other-features/performance/http-caching) enabled. + +```graphql +type Post @cacheControl(maxAge: 240) { + id: Int! + title: String +} +``` + +When the gateway receives a query that selects the `Post` type, it will forward the request to the +subgraph directly. + +```graphql +query { + posts { + id + title + } +} +``` + +Then Yoga Server will generate the response and cache it for 240 seconds. If the same query is made +within 240 seconds, Yoga Server will return the cached response. So it will always receive the HTTP +request but do the less work to generate the response. The difference between others, this case +won't reduce the HTTP connection traffic in between gateway and subgraph, but it will reduce the +work that subgraph needs to do to generate the response. + +#### Subgraphs with any server implementation using directives directly (This response cache plugin is required) + +Besides these two, you can let the gateway handle the caching on its own. In this case, you need to +define the following in the subgraphs so the supergraph has `@cacheControl` directives. Then, the +response caching plugin on the gateway will handle the caching based on these directives. + +```graphql +extend schema + @link( + # Federation spec version should be at least 2.1 to support `@composeDirective` + url: "https://specs.apollo.dev/federation/v2.1" + import: ["@composeDirective"] + ) + # Import `@cacheControl` + @link(url: "https://the-guild.dev/mesh/v1.0", import: ["@cacheControl"]) + # Use `@composeDirective` to define the directive as exposed + @composeDirective(name: "@cacheControl") + +# Then add the actual directive definitions +enum CacheControlScope { + PUBLIC + PRIVATE +} + +directive @cacheControl( + maxAge: Int + scope: CacheControlScope + inheritMaxAge: Boolean +) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION +``` + +In this case, the gateway will cache the response based on the `@cacheControl` directives defined in +the subgraphs. + +The difference between the two is that the gateway will control the caching behavior based on the +`@cacheControl` directives defined in the subgraphs. For any cached responses, the gateway will skip +query planning and federation execution phase, and return the response directly. + +```mermaid +flowchart TD + A["GraphQL Client"] + B(Go shopping) + B("Hive Gateway") + E["Send HTTP Response Back"] + E + E["Subgraph Yoga Server"] + E --- n1["Response Cache Plugin"] + n1 ---|"If Cached"| n2["Get the cached response from Cache Storage"] + n1 ---|"If Not Cached"| n3["Prepare the result by making database calls etc, and store it for the future calls"] + subgraph B["Hive Gateway"] + n7["Get Stored Response"] + n6["Response Caching Plugin"] + n5 + n4["Query Planner"] + end + n4 + n4 ---|"Generate Subgraph Query"| n5["Execution Engine"] + n5 + E + B + n5 + n5 + n5 ---|"HTTP Request"| E + A + B + n6 ---|"If Not Cached"| n4 + n6 ---|"If Cache Hit"| n7 + n7 ---|"Respond Cached Response back to Client"| A + n5 ---|"Store the response for future calls"| n6 + A ---|"Send GraphQL Request via HTTP "| n6 +``` ## Session based caching