From 68175cba14f37e7acba4110218e6c322fe675cf4 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 7 Jul 2023 12:31:29 +0200 Subject: [PATCH 01/12] Document the request lifecycle We need an in depth documentation of the entire request lifecycle, indicating at which points plugins can be called, and the content of requests and responses --- docs/source/customizations/overview.mdx | 94 +++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index ad42e97a28..44394b3264 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -21,6 +21,100 @@ The Apollo Router supports the following customization types: - Make network requests - Use libraries from a particular language or framework +## The request lifecycle + +Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. each point is represented by a specific service with specific request and response objects, and each service can have a set of plugins. On the request side, the plugins are executed before the service, and on the response side, the plugins are executed in reversed order after the service. + +Each request and response object contains a `Context` object, that is carried throughout the entire process, and is unique per client request. It is used to store plugin specific information between the request and response side, or to communicate between different hook points (a plugin can be called at multiple steps of the request lifecyle). + +### Router service + +The router service is called right after the HTTP server. The `RouterRequest` contains HTTP headers and the body as a stream of byte arrays. The `RouterResponse` contains HTTP headers and the body as a stream of byte arrays. The router service handles Automatic Persisted Queries, parses the GraphQL request from JSON, calls the supergraph service, and serializes the GraphQL responses to JSON. + +### Supergraph service + +The supergraph service works on a `SupergraphRequest` containing HTTP headers and a GraphQL request object, and the `SupergraphResponse` contains headers and a stream of GraphQL responses. For most queries, that stream will only contain one element, but it can contain more if the query uses the `@defer` directive or subscriptions. + +The supergraph service calls into the query planner, which will return a query plan, and it will then call the execution service. + +### Execution service + +The execution service is tasked with executing the query plan. The `ExecutionRequest` contains the `SupergraphRequest` and the query plan. The `ExecutionResponse` has the same content as the `SupergraphResponse`. + +For each fetch node of the query plan, it will create a subgraph request and call the subgraph plugins and service. Once it has received all of the subgraph responses, it formats the GraphQL responses (removing unneeded data, propagating nulls) before sending it back to the supergraph plugin. + +### Subgraph service + +The subgraph service wraps a subgraph: it transforms the subgraph request in a HTTP request to that subgraph. The `SubgraphRequest` contains the `SupergraphRequest` (read only), HTTP headers, a GraphQL request object as body and the operation kind of the subgraph request (query, mutation or subscription). The `SubgraphResponse` contains HTTP headers and a GraphQL response. + +Each subgraph will have a different corresponding subgraph service, and each one can have specific subgraph plugins configuration. + +### Flow chart + +Here is an example flow chart of the entire lifecycle: + +```mermaid +flowchart TB; + client(Client); + subgraph " " + httpServer("HTTP server") + routerService("RouterService
"); + supergraphService("SupergraphService"); + queryPlanner("QueryPlanner"); + executionService("ExecutionService"); + subgraphService1("SubgraphService(products)"); + subgraphService2("SubgraphService(reviews)"); + routerPlugins[[Router plugins]]; + supergraphPlugins[[Supergraph plugins]]; + executionPlugins[[Execution plugins]]; + subgraphPlugins1[[Subgraph plugins]]; + subgraphPlugins2[[Subgraph plugins]]; +end; +subgraph1[Subgraph 1]; +subgraph2[Subgraph 2]; + +client --"1. HTTP Request"--> httpServer; + +httpServer --"2. RouterRequest"--> routerPlugins; +routerPlugins --"3. RouterRequest"--> routerService; + +routerService -."4. SupergraphRequest"--> supergraphPlugins; +supergraphPlugins --"5.SupergraphRequest"--> supergraphService; +supergraphService --"6. Query"--> queryPlanner; +queryPlanner --"7.Query plan"--> supergraphService; + +supergraphService --"8.ExecutionRequest"--> executionPlugins; +executionPlugins --"9.ExecutionRequest"--> executionService; + +executionService --"10.SubgraphRequest"--> subgraphPlugins1; +subgraphPlugins1 --"11.SubgraphRequest"--> subgraphService1; +subgraphService1 --"12.HTTP Request"--> subgraph1; +subgraph1 --"13.HTTP Response"--> subgraphService1; +subgraphService1 --"14.SubgraphResponse"--> subgraphPlugins1; +subgraphPlugins1 --"15.SubgraphResponse"--> executionService; + +executionService --"16.SubgraphRequest"--> subgraphPlugins2; +subgraphPlugins2 --"17.SubgraphRequest"--> subgraphService2; +subgraphService2 --"18.HTTP Request"--> subgraph2; +subgraph2 --"19.HTTP Response"--> subgraphService2; +subgraphService2 --"20.SubgraphResponse"--> subgraphPlugins2; +subgraphPlugins2 --"21.SubgraphResponse"--> executionService; + +executionService --"22.ExecutionResponse"--> executionPlugins; +executionPlugins --"23.ExecutionResponse"--> supergraphService; + +supergraphService --"24.SupergraphResponse"--> supergraphPlugins; +supergraphPlugins --"25.SupergraphResponse"--> routerService; + +routerService --"26.RouterResponse"--> routerPlugins; +routerPlugins --"27.RouterResponse"--> httpServer; + +httpServer --"28. HTTP response"--> client; + + +class client,subgraph1,subgraph2 secondary; +``` + --- Next, see the documentation for your preferred [customization type](#customization-types). From bc910e6277097336aed5a22364fe99a5df6b9ae6 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 7 Jul 2023 12:35:07 +0200 Subject: [PATCH 02/12] changeset --- .changesets/docs_geal_document_request_lifecycle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changesets/docs_geal_document_request_lifecycle.md diff --git a/.changesets/docs_geal_document_request_lifecycle.md b/.changesets/docs_geal_document_request_lifecycle.md new file mode 100644 index 0000000000..0ee1b0ddae --- /dev/null +++ b/.changesets/docs_geal_document_request_lifecycle.md @@ -0,0 +1,5 @@ +### Document the request lifecycle ([PR #3391](https://github.com/apollographql/router/pull/3391)) + +This adds an in depth documentation of the entire request lifecycle, which services exist in the router, the request and response types they use, and where plugins can attach themselves + +By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/3391 From 88a18cd45f38ac2bf034aa5ebb4d8efda8420311 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Wed, 2 Aug 2023 10:38:52 -0600 Subject: [PATCH 03/12] Copy edit text --- .../docs_geal_document_request_lifecycle.md | 6 +- docs/source/customizations/overview.mdx | 63 ++++++++++--------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/.changesets/docs_geal_document_request_lifecycle.md b/.changesets/docs_geal_document_request_lifecycle.md index 0ee1b0ddae..3b4c9d9cbf 100644 --- a/.changesets/docs_geal_document_request_lifecycle.md +++ b/.changesets/docs_geal_document_request_lifecycle.md @@ -1,5 +1,9 @@ ### Document the request lifecycle ([PR #3391](https://github.com/apollographql/router/pull/3391)) -This adds an in depth documentation of the entire request lifecycle, which services exist in the router, the request and response types they use, and where plugins can attach themselves +This adds in-depth documentation of: +- the entire request lifecycle +- which services exist in the router +- the request and response types they use +- where plugins can attach themselves By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/3391 diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index 44394b3264..741fac8f25 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -1,5 +1,5 @@ --- -title: Customizations for the Apollo Router +title: Apollo Router customizations description: Extend your router with custom functionality --- @@ -10,7 +10,7 @@ You can create **customizations** for the Apollo Router to add functionality tha The Apollo Router supports the following customization types: - [**Rhai scripts**](./rhai/) - - The [Rhai scripting language](https://rhai.rs/book/) enables you to add functionality directly to your stock router binary by hooking into different phases of the router's request lifecycle. + - The [Rhai scripting language](https://rhai.rs/book/) lets you add functionality directly to your stock router binary by hooking into different phases of the router's request lifecycle. - [**External co-processing**](./coprocessor/) ([Enterprise feature](../enterprise-features/)) - If your organization has a [GraphOS Enterprise plan](https://www.apollographql.com/pricing/), you can write custom request-handling code in any language. This code can run in the same container as your router or separately. - The router calls your custom code via HTTP, passing it the details of each incoming client request. @@ -23,35 +23,13 @@ The Apollo Router supports the following customization types: ## The request lifecycle -Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. each point is represented by a specific service with specific request and response objects, and each service can have a set of plugins. On the request side, the plugins are executed before the service, and on the response side, the plugins are executed in reversed order after the service. +Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. Each point is represented by a specific service with specific request and response objects, and each service can have a set of plugins. On the request side, the plugins are executed before the service, and on the response side, the plugins are executed after the service. -Each request and response object contains a `Context` object, that is carried throughout the entire process, and is unique per client request. It is used to store plugin specific information between the request and response side, or to communicate between different hook points (a plugin can be called at multiple steps of the request lifecyle). +Each request and response object contains a `Context` object, which is carried throughout the entire process. Each request's `Context` object is unique. You can use it to store plugin-specific information between the request and response or to communicate between different hook points. (A plugin can be called at multiple steps of the request lifecycle.) -### Router service - -The router service is called right after the HTTP server. The `RouterRequest` contains HTTP headers and the body as a stream of byte arrays. The `RouterResponse` contains HTTP headers and the body as a stream of byte arrays. The router service handles Automatic Persisted Queries, parses the GraphQL request from JSON, calls the supergraph service, and serializes the GraphQL responses to JSON. - -### Supergraph service - -The supergraph service works on a `SupergraphRequest` containing HTTP headers and a GraphQL request object, and the `SupergraphResponse` contains headers and a stream of GraphQL responses. For most queries, that stream will only contain one element, but it can contain more if the query uses the `@defer` directive or subscriptions. - -The supergraph service calls into the query planner, which will return a query plan, and it will then call the execution service. - -### Execution service +### Lifecycle flowchart -The execution service is tasked with executing the query plan. The `ExecutionRequest` contains the `SupergraphRequest` and the query plan. The `ExecutionResponse` has the same content as the `SupergraphResponse`. - -For each fetch node of the query plan, it will create a subgraph request and call the subgraph plugins and service. Once it has received all of the subgraph responses, it formats the GraphQL responses (removing unneeded data, propagating nulls) before sending it back to the supergraph plugin. - -### Subgraph service - -The subgraph service wraps a subgraph: it transforms the subgraph request in a HTTP request to that subgraph. The `SubgraphRequest` contains the `SupergraphRequest` (read only), HTTP headers, a GraphQL request object as body and the operation kind of the subgraph request (query, mutation or subscription). The `SubgraphResponse` contains HTTP headers and a GraphQL response. - -Each subgraph will have a different corresponding subgraph service, and each one can have specific subgraph plugins configuration. - -### Flow chart - -Here is an example flow chart of the entire lifecycle: +The following flowchart diagrams the entire request lifecycle. Continue reading for details on what occurs in each service. ```mermaid flowchart TB; @@ -117,4 +95,31 @@ class client,subgraph1,subgraph2 secondary; --- -Next, see the documentation for your preferred [customization type](#customization-types). +### Router service + +The router service is called right after the HTTP server. The `RouterRequest` contains HTTP headers and the body as a stream of byte arrays. The `RouterResponse` contains HTTP headers and the body as a stream of byte arrays. The router service handles Automatic Persisted Queries, parses the GraphQL request from JSON, calls the supergraph service, and serializes the GraphQL responses to JSON. + +### Supergraph service + +The supergraph service works on a `SupergraphRequest` containing HTTP headers and a GraphQL request object. The `SupergraphResponse` contains headers and a stream of GraphQL responses. That stream only contains one element for most queries—it can contain more if the query uses the `@defer` directive or subscriptions. + +The supergraph service calls into the query planner, which returns a query plan. The supergraph service then calls the execution service. + +### Execution service + +The execution service is tasked with executing the query plan. The `ExecutionRequest` contains the `SupergraphRequest` and the query plan. The `ExecutionResponse` has the same content as the `SupergraphResponse`. + +For each fetch node of the query plan, the execution service creates a subgraph request and then calls the subgraph plugins and service. Once the execution service has received all subgraph responses, it formats the GraphQL responses—removing unneeded data and propagating nulls—before sending it back to the supergraph plugin. + +### Subgraph service + +The subgraph service wraps a subgraph: it transforms the subgraph request in an HTTP request to that subgraph. The `SubgraphRequest` contains: +- the (read-only) `SupergraphRequest` +- HTTP headers +- a GraphQL request object as the request body +- and the subgraph request's operation type (query, mutation, or subscription) + +The `SubgraphResponse` contains HTTP headers and a GraphQL response. + +Each subgraph has its own subgraph service, and each service can have its own subgraph plugin configuration. + From 11a3021f8ebbf13fcca4cfba6a41b1914276925e Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Wed, 2 Aug 2023 13:53:28 -0600 Subject: [PATCH 04/12] Add additional Mermaid diagrams --- docs/source/customizations/overview.mdx | 58 ++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index 741fac8f25..67307c5f9d 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -23,7 +23,63 @@ The Apollo Router supports the following customization types: ## The request lifecycle -Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. Each point is represented by a specific service with specific request and response objects, and each service can have a set of plugins. On the request side, the plugins are executed before the service, and on the response side, the plugins are executed after the service. +Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. Each point is represented by a specific service with specific request and response objects. + + +```mermaid +flowchart RL + subgraph client["Client"] + end + + subgraph router["Apollo Router"] + direction LR + routerService("Router
Service") + supergraphService("Supergraph
Service") + executionService("Execution
Service") + subgraphService("Subgraph
Service") + routerService -->|request| supergraphService -->|request| executionService -->|request| subgraphService + subgraphService -->|response| executionService -->|response| supergraphService -->|response| routerService + + end + + subgraph infra["Your infrastructure"] + direction TB + api1("subgraph A"); + api2("subgraph B"); + api3("subgraph C"); + api1 --- api2 --- api3 + + end + +client -->|request| router -->|request| infra + +infra -->|response| router -->|response| client +``` + +Each service can have a set of plugins. For requests, the plugins are executed before the service. + +```mermaid +flowchart LR + subgraph Service + Plugin1["Plugin 1"] -->|request| Plugin2["Plugin 2"] -->|request| coreService["Core
service"] + coreService + end + +Client -->|request| Plugin1 +coreService -->|request| NextService["Next service"] +``` + +For responses, the plugins are executed after the service. + +```mermaid +flowchart RL + subgraph Service + coreService["Core
service"] -->|response| Plugin2["Plugin 2"] -->|response| Plugin1["Plugin 1"] + end + +Plugin1["Plugin 1"] -->|response| Client +NextService["Next service"] -->|response| coreService +``` Each request and response object contains a `Context` object, which is carried throughout the entire process. Each request's `Context` object is unique. You can use it to store plugin-specific information between the request and response or to communicate between different hook points. (A plugin can be called at multiple steps of the request lifecycle.) From 291a621fecb97a00f964a7dd47baa603bb8c358e Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Wed, 2 Aug 2023 15:18:44 -0600 Subject: [PATCH 05/12] Update main Mermaid --- docs/source/customizations/overview.mdx | 159 +++++++++++++++--------- 1 file changed, 100 insertions(+), 59 deletions(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index 67307c5f9d..f959881978 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -25,7 +25,6 @@ The Apollo Router supports the following customization types: Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. Each point is represented by a specific service with specific request and response objects. - ```mermaid flowchart RL subgraph client["Client"] @@ -85,72 +84,114 @@ Each request and response object contains a `Context` object, which is carried t ### Lifecycle flowchart -The following flowchart diagrams the entire request lifecycle. Continue reading for details on what occurs in each service. +The following flowcharts diagram the entire request lifecycle. +The first details the path of a request from a client, through the parts of the Apollo Router, all the way to your subgraphs. +The second details the path of a response from your subgraphs back to the client. +Continue reading for details on what occurs in each service. + +#### Request path ```mermaid flowchart TB; client(Client); - subgraph " " - httpServer("HTTP server") - routerService("RouterService
"); - supergraphService("SupergraphService"); - queryPlanner("QueryPlanner"); - executionService("ExecutionService"); - subgraphService1("SubgraphService(products)"); - subgraphService2("SubgraphService(reviews)"); - routerPlugins[[Router plugins]]; - supergraphPlugins[[Supergraph plugins]]; - executionPlugins[[Execution plugins]]; - subgraphPlugins1[[Subgraph plugins]]; - subgraphPlugins2[[Subgraph plugins]]; -end; -subgraph1[Subgraph 1]; -subgraph2[Subgraph 2]; - -client --"1. HTTP Request"--> httpServer; - -httpServer --"2. RouterRequest"--> routerPlugins; -routerPlugins --"3. RouterRequest"--> routerService; - -routerService -."4. SupergraphRequest"--> supergraphPlugins; -supergraphPlugins --"5.SupergraphRequest"--> supergraphService; -supergraphService --"6. Query"--> queryPlanner; -queryPlanner --"7.Query plan"--> supergraphService; - -supergraphService --"8.ExecutionRequest"--> executionPlugins; -executionPlugins --"9.ExecutionRequest"--> executionService; - -executionService --"10.SubgraphRequest"--> subgraphPlugins1; -subgraphPlugins1 --"11.SubgraphRequest"--> subgraphService1; -subgraphService1 --"12.HTTP Request"--> subgraph1; -subgraph1 --"13.HTTP Response"--> subgraphService1; -subgraphService1 --"14.SubgraphResponse"--> subgraphPlugins1; -subgraphPlugins1 --"15.SubgraphResponse"--> executionService; - -executionService --"16.SubgraphRequest"--> subgraphPlugins2; -subgraphPlugins2 --"17.SubgraphRequest"--> subgraphService2; -subgraphService2 --"18.HTTP Request"--> subgraph2; -subgraph2 --"19.HTTP Response"--> subgraphService2; -subgraphService2 --"20.SubgraphResponse"--> subgraphPlugins2; -subgraphPlugins2 --"21.SubgraphResponse"--> executionService; - -executionService --"22.ExecutionResponse"--> executionPlugins; -executionPlugins --"23.ExecutionResponse"--> supergraphService; - -supergraphService --"24.SupergraphResponse"--> supergraphPlugins; -supergraphPlugins --"25.SupergraphResponse"--> routerService; - -routerService --"26.RouterResponse"--> routerPlugins; -routerPlugins --"27.RouterResponse"--> httpServer; - -httpServer --"28. HTTP response"--> client; - - -class client,subgraph1,subgraph2 secondary; + subgraph router["Apollo Router"] + direction LR + httpServer("HTTP server") + subgraph routerService["RouterService
"] + routerPlugins[[Router plugins]]; + end + subgraph " " + subgraph supergraphService["SupergraphService"] + supergraphPlugins[[Supergraph plugins]]; + end + queryPlanner("QueryPlanner"); + end + + + subgraph executionService["ExecutionService"] + executionPlugins[[Execution plugins]]; + end + + subgraph subgraphService["SubgraphServices"] + subgraph service1["SubgraphServiceA"] + subgraphPlugins1[[Subgraph plugins]]; + end + subgraph service2["SubgraphServiceB"] + subgraphPlugins2[[Subgraph plugins]]; + end + end + end; +subgraphA[Subgraph A]; +subgraphB[Subgraph B]; + +client --"1. HTTP request"--> httpServer; +httpServer --"2. Router request"--> routerService; +routerService --"3. Supergraph request"--> supergraphService +supergraphService --"4. Query"--> queryPlanner; +queryPlanner --"5. Query plan"--> supergraphService; +supergraphService --"6. Execution request"--> executionService; + +executionService --"7a. Subgraph request"--> service1; +executionService --"7b. Subgraph request"--> service2; + +service1 --"8a. HTTP Request"--> subgraphA; +service2 --"8b. HTTP Request"--> subgraphB; ``` --- +#### Request path + +```mermaid +flowchart TB; + client(Client); + subgraph router["Apollo Router"] + direction LR + httpServer("HTTP server") + subgraph routerService["RouterService
"] + routerPlugins[[Router plugins]]; + end + subgraph " " + subgraph supergraphService["SupergraphService"] + supergraphPlugins[[Supergraph plugins]]; + end + queryPlanner("QueryPlanner"); + end + + + subgraph executionService["ExecutionService"] + executionPlugins[[Execution plugins]]; + end + + subgraph subgraphService["SubgraphServices"] + subgraph service1["SubgraphServiceA"] + subgraphPlugins1[[Subgraph plugins]]; + end + subgraph service2["SubgraphServiceB"] + subgraphPlugins2[[Subgraph plugins]]; + end + end + end; +subgraphA[Subgraph A]; +subgraphB[Subgraph B]; + +client --"1. HTTP request"--> httpServer; +httpServer --"2. Router request"--> routerService; +routerService --"3. Supergraph request"--> supergraphService +supergraphService --"4. Query"--> queryPlanner; +queryPlanner --"5. Query plan"--> supergraphService; +supergraphService --"6. Execution request"--> executionService; + +executionService --"7a. Subgraph request"--> service1; +executionService --"7b. Subgraph request"--> service2; + +service1 --"8a. HTTP Request"--> subgraphA; +service2 --"8b. HTTP Request"--> subgraphB; +``` + +--- + + ### Router service The router service is called right after the HTTP server. The `RouterRequest` contains HTTP headers and the body as a stream of byte arrays. The `RouterResponse` contains HTTP headers and the body as a stream of byte arrays. The router service handles Automatic Persisted Queries, parses the GraphQL request from JSON, calls the supergraph service, and serializes the GraphQL responses to JSON. From 200e5190143b0b3ac63ffa0bba7f0ec4485025b1 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Wed, 2 Aug 2023 17:01:56 -0600 Subject: [PATCH 06/12] Standardize terms between request and response path --- docs/source/customizations/overview.mdx | 53 ++++++++++--------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index f959881978..82afd1f91e 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -104,7 +104,7 @@ flowchart TB; subgraph supergraphService["SupergraphService"] supergraphPlugins[[Supergraph plugins]]; end - queryPlanner("QueryPlanner"); + queryPlanner("Query Planner"); end @@ -125,27 +125,25 @@ subgraphA[Subgraph A]; subgraphB[Subgraph B]; client --"1. HTTP request"--> httpServer; -httpServer --"2. Router request"--> routerService; -routerService --"3. Supergraph request"--> supergraphService +httpServer --"2. RouterRequest"--> routerService; +routerService --"3. SupergraphRequest"--> supergraphService supergraphService --"4. Query"--> queryPlanner; queryPlanner --"5. Query plan"--> supergraphService; -supergraphService --"6. Execution request"--> executionService; +supergraphService --"6. ExecutionRequest"--> executionService; -executionService --"7a. Subgraph request"--> service1; -executionService --"7b. Subgraph request"--> service2; +executionService --"7a. SubgraphRequest"--> service1; +executionService --"7b. SubgraphRequest"--> service2; -service1 --"8a. HTTP Request"--> subgraphA; -service2 --"8b. HTTP Request"--> subgraphB; +service1 --"8a. HTTP request"--> subgraphA; +service2 --"8b. HTTP request"--> subgraphB; ``` ---- - -#### Request path +#### Response path ```mermaid -flowchart TB; +flowchart BT; client(Client); - subgraph router["Apollo Router"] + subgraph " " direction LR httpServer("HTTP server") subgraph routerService["RouterService
"] @@ -172,26 +170,19 @@ flowchart TB; end end end; -subgraphA[Subgraph A]; -subgraphB[Subgraph B]; - -client --"1. HTTP request"--> httpServer; -httpServer --"2. Router request"--> routerService; -routerService --"3. Supergraph request"--> supergraphService -supergraphService --"4. Query"--> queryPlanner; -queryPlanner --"5. Query plan"--> supergraphService; -supergraphService --"6. Execution request"--> executionService; - -executionService --"7a. Subgraph request"--> service1; -executionService --"7b. Subgraph request"--> service2; - -service1 --"8a. HTTP Request"--> subgraphA; -service2 --"8b. HTTP Request"--> subgraphB; +subgraph1[Subgraph A]; +subgraph2[Subgraph B]; + +subgraph1 -- "9a. HTTP response"--> service1; +subgraph2 -- "9b. HTTP response"--> service2; +service1 --"10a. SubgraphResponse"--> executionService; +service2 --"10b. SubgraphResponse"--> executionService; +executionService --"11. ExecutionResponse"--> supergraphService; +supergraphService --"12. SupergraphResponse"--> routerService; +routerService --"13. RouterResponse"--> httpServer; +httpServer --"HTTP response" --> client ``` ---- - - ### Router service The router service is called right after the HTTP server. The `RouterRequest` contains HTTP headers and the body as a stream of byte arrays. The `RouterResponse` contains HTTP headers and the body as a stream of byte arrays. The router service handles Automatic Persisted Queries, parses the GraphQL request from JSON, calls the supergraph service, and serializes the GraphQL responses to JSON. From f7d3db54922afc088574f20a39f77c57fef55f3f Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Wed, 2 Aug 2023 17:47:22 -0600 Subject: [PATCH 07/12] Restructure content --- docs/source/customizations/overview.mdx | 93 +++++++++++-------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index 82afd1f91e..f0ffd2169f 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -23,7 +23,7 @@ The Apollo Router supports the following customization types: ## The request lifecycle -Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. Each point is represented by a specific service with specific request and response objects. +Customizations intervene at specific points of the request lifecycle, depending on the task you want to perform. Each point is represented by a specific service with its own request and response objects. ```mermaid flowchart RL @@ -55,7 +55,7 @@ client -->|request| router -->|request| infra infra -->|response| router -->|response| client ``` -Each service can have a set of plugins. For requests, the plugins are executed before the service. +Each service can have a set of plugins. For requests, the router executes plugins before the service. ```mermaid flowchart LR @@ -68,7 +68,7 @@ Client -->|request| Plugin1 coreService -->|request| NextService["Next service"] ``` -For responses, the plugins are executed after the service. +For responses, the router executes the plugins after the service. ```mermaid flowchart RL @@ -82,41 +82,39 @@ NextService["Next service"] -->|response| coreService Each request and response object contains a `Context` object, which is carried throughout the entire process. Each request's `Context` object is unique. You can use it to store plugin-specific information between the request and response or to communicate between different hook points. (A plugin can be called at multiple steps of the request lifecycle.) -### Lifecycle flowchart - The following flowcharts diagram the entire request lifecycle. The first details the path of a request from a client, through the parts of the Apollo Router, all the way to your subgraphs. The second details the path of a response from your subgraphs back to the client. Continue reading for details on what occurs in each service. -#### Request path +### Request path ```mermaid flowchart TB; client(Client); subgraph router["Apollo Router"] direction LR - httpServer("HTTP server") - subgraph routerService["RouterService
"] + httpServer("HTTP server") + subgraph routerService["Router Service"] routerPlugins[[Router plugins]]; end subgraph " " - subgraph supergraphService["SupergraphService"] + subgraph supergraphService["Supergraph Service"] supergraphPlugins[[Supergraph plugins]]; end queryPlanner("Query Planner"); end - subgraph executionService["ExecutionService"] + subgraph executionService["Execution Service"] executionPlugins[[Execution plugins]]; end - subgraph subgraphService["SubgraphServices"] - subgraph service1["SubgraphServiceA"] + subgraph subgraphService["Subgraph Services"] + subgraph service1["Subgraph Service A"] subgraphPlugins1[[Subgraph plugins]]; end - subgraph service2["SubgraphServiceB"] + subgraph service2["Subgraph Service B"] subgraphPlugins2[[Subgraph plugins]]; end end @@ -138,34 +136,49 @@ service1 --"8a. HTTP request"--> subgraphA; service2 --"8b. HTTP request"--> subgraphB; ``` -#### Response path +1. The router receives a client request at an HTTP server. +2. The HTTP server transforms the HTTP request into a `RouterRequest` containing HTTP headers and the request body as a stream of byte arrays. +3. The router service receives the `RouterRequest`. It handles Automatic Persisted Queries (APQ), parses the GraphQL request from JSON and calls the supergraph service with the resulting `SupergraphRequest`. +4. The supergraph service calls the query planner with the GraphQL query from the `SupergraphRequest`. +5. The query planner returns a query plan for most efficiently executing the query. +6. The supergraph service calls the execution service with an `ExecutionRequest`, made up of `SupergraphRequest` and the query plan. +7. For each fetch node of the query plan, the execution service creates a `SubgraphRequest` and then calls the respective subgraph plugins and service. +8. Each subgraph has its own subgraph service, and each service can have its own subgraph plugin configuration. The subgraph service transforms the `SubgraphRequest` into an HTTP request to that subgraph. The `SubgraphRequest` contains: + - the (read-only) `SupergraphRequest` + - HTTP headers + - a GraphQL request object as the request body + - and the subgraph request's operation type (query, mutation, or subscription) + +Once your subgraphs provide a response, the response follows the path outlined below. + +### Response path ```mermaid flowchart BT; client(Client); subgraph " " direction LR - httpServer("HTTP server") - subgraph routerService["RouterService
"] + httpServer("HTTP server") + subgraph routerService["Router Service"] routerPlugins[[Router plugins]]; end subgraph " " - subgraph supergraphService["SupergraphService"] + subgraph supergraphService["Supergraph Service"] supergraphPlugins[[Supergraph plugins]]; end - queryPlanner("QueryPlanner"); + queryPlanner("QueryPlanner"); end - subgraph executionService["ExecutionService"] + subgraph executionService["Execution Service"] executionPlugins[[Execution plugins]]; end - subgraph subgraphService["SubgraphServices"] - subgraph service1["SubgraphServiceA"] + subgraph subgraphService["Subgraph Services"] + subgraph service1["Subgraph Service A"] subgraphPlugins1[[Subgraph plugins]]; end - subgraph service2["SubgraphServiceB"] + subgraph service2["Subgraph Service B"] subgraphPlugins2[[Subgraph plugins]]; end end @@ -180,34 +193,12 @@ service2 --"10b. SubgraphResponse"--> executionService; executionService --"11. ExecutionResponse"--> supergraphService; supergraphService --"12. SupergraphResponse"--> routerService; routerService --"13. RouterResponse"--> httpServer; -httpServer --"HTTP response" --> client +httpServer --"14. HTTP response" --> client ``` -### Router service - -The router service is called right after the HTTP server. The `RouterRequest` contains HTTP headers and the body as a stream of byte arrays. The `RouterResponse` contains HTTP headers and the body as a stream of byte arrays. The router service handles Automatic Persisted Queries, parses the GraphQL request from JSON, calls the supergraph service, and serializes the GraphQL responses to JSON. - -### Supergraph service - -The supergraph service works on a `SupergraphRequest` containing HTTP headers and a GraphQL request object. The `SupergraphResponse` contains headers and a stream of GraphQL responses. That stream only contains one element for most queries—it can contain more if the query uses the `@defer` directive or subscriptions. - -The supergraph service calls into the query planner, which returns a query plan. The supergraph service then calls the execution service. - -### Execution service - -The execution service is tasked with executing the query plan. The `ExecutionRequest` contains the `SupergraphRequest` and the query plan. The `ExecutionResponse` has the same content as the `SupergraphResponse`. - -For each fetch node of the query plan, the execution service creates a subgraph request and then calls the subgraph plugins and service. Once the execution service has received all subgraph responses, it formats the GraphQL responses—removing unneeded data and propagating nulls—before sending it back to the supergraph plugin. - -### Subgraph service - -The subgraph service wraps a subgraph: it transforms the subgraph request in an HTTP request to that subgraph. The `SubgraphRequest` contains: -- the (read-only) `SupergraphRequest` -- HTTP headers -- a GraphQL request object as the request body -- and the subgraph request's operation type (query, mutation, or subscription) - -The `SubgraphResponse` contains HTTP headers and a GraphQL response. - -Each subgraph has its own subgraph service, and each service can have its own subgraph plugin configuration. - +9. Each subgraph provides an HTTP response to the subgraph services. +10. Each subgraph service creates a `SubgraphResponse` containing the HTTP headers and a GraphQL response. +11. Once the execution service has received all subgraph responses, it formats the GraphQL responses—removing unneeded data and propagating nulls—before sending it back to the supergraph plugin as the `ExecutionResponse`. +12. The `SupergraphResponse` has the same content as the `ExecutionResponse`. It contains headers and a stream of GraphQL responses. That stream only contains one element for most queries—it can contain more if the query uses the `@defer` directive or subscriptions. +13. The router service receives the `SupergraphResponse` and serializes the GraphQL responses to JSON. +14. The HTTP server sends the JSON in an HTTP response to the client. \ No newline at end of file From c9ffd75cda2cacaf0452dfbf3841168f0a54fbc8 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Tue, 8 Aug 2023 08:02:00 -0600 Subject: [PATCH 08/12] Add note about Rhai scripts --- docs/source/customizations/overview.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index f0ffd2169f..ffa3097e8f 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -15,7 +15,7 @@ The Apollo Router supports the following customization types: - If your organization has a [GraphOS Enterprise plan](https://www.apollographql.com/pricing/), you can write custom request-handling code in any language. This code can run in the same container as your router or separately. - The router calls your custom code via HTTP, passing it the details of each incoming client request. -**Use [Rhai scripts](./rhai/) if they support your use case.** External co-processing is most helpful if your customization needs to do any of the following (which Rhai scripts _don't_ support): +Because [Rhai scripts](./rhai/) are easier to deploy, we recommend using them if they support your use case. External co-processing is most helpful if your customization needs to do any of the following (which Rhai scripts _don't_ support): - Read or write to disk - Make network requests From 77c26296a52678f30fe9f7a54cb673f5e52adfff Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Tue, 8 Aug 2023 08:13:28 -0600 Subject: [PATCH 09/12] Copy edits --- docs/source/customizations/overview.mdx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index ffa3097e8f..ab036d661f 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -55,7 +55,7 @@ client -->|request| router -->|request| infra infra -->|response| router -->|response| client ``` -Each service can have a set of plugins. For requests, the router executes plugins before the service. +Each service can have a set of plugins. For requests, the router executes plugins _before_ the service. ```mermaid flowchart LR @@ -68,7 +68,7 @@ Client -->|request| Plugin1 coreService -->|request| NextService["Next service"] ``` -For responses, the router executes the plugins after the service. +For responses, the router executes the plugins _after_ the service. ```mermaid flowchart RL @@ -85,7 +85,6 @@ Each request and response object contains a `Context` object, which is carried t The following flowcharts diagram the entire request lifecycle. The first details the path of a request from a client, through the parts of the Apollo Router, all the way to your subgraphs. The second details the path of a response from your subgraphs back to the client. -Continue reading for details on what occurs in each service. ### Request path @@ -138,7 +137,7 @@ service2 --"8b. HTTP request"--> subgraphB; 1. The router receives a client request at an HTTP server. 2. The HTTP server transforms the HTTP request into a `RouterRequest` containing HTTP headers and the request body as a stream of byte arrays. -3. The router service receives the `RouterRequest`. It handles Automatic Persisted Queries (APQ), parses the GraphQL request from JSON and calls the supergraph service with the resulting `SupergraphRequest`. +3. The router service receives the `RouterRequest`. It handles Automatic Persisted Queries (APQ), parses the GraphQL request from JSON, and calls the supergraph service with the resulting `SupergraphRequest`. 4. The supergraph service calls the query planner with the GraphQL query from the `SupergraphRequest`. 5. The query planner returns a query plan for most efficiently executing the query. 6. The supergraph service calls the execution service with an `ExecutionRequest`, made up of `SupergraphRequest` and the query plan. @@ -201,4 +200,8 @@ httpServer --"14. HTTP response" --> client 11. Once the execution service has received all subgraph responses, it formats the GraphQL responses—removing unneeded data and propagating nulls—before sending it back to the supergraph plugin as the `ExecutionResponse`. 12. The `SupergraphResponse` has the same content as the `ExecutionResponse`. It contains headers and a stream of GraphQL responses. That stream only contains one element for most queries—it can contain more if the query uses the `@defer` directive or subscriptions. 13. The router service receives the `SupergraphResponse` and serializes the GraphQL responses to JSON. -14. The HTTP server sends the JSON in an HTTP response to the client. \ No newline at end of file +14. The HTTP server sends the JSON in an HTTP response to the client. + +## Next steps + +To learn how to hook in to the various lifecycle stages, including examples customizations, refer to the [Rhai scripts](./rhai/) and [external coprocessing](./coprocessor/) docs. \ No newline at end of file From 2ef7db03e509f0f5bf53042ad615cb5dddd9cc49 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Tue, 8 Aug 2023 09:51:46 -0600 Subject: [PATCH 10/12] Add "nuances" section --- docs/source/customizations/overview.mdx | 82 +++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index ab036d661f..90bf38cf87 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -141,12 +141,12 @@ service2 --"8b. HTTP request"--> subgraphB; 4. The supergraph service calls the query planner with the GraphQL query from the `SupergraphRequest`. 5. The query planner returns a query plan for most efficiently executing the query. 6. The supergraph service calls the execution service with an `ExecutionRequest`, made up of `SupergraphRequest` and the query plan. -7. For each fetch node of the query plan, the execution service creates a `SubgraphRequest` and then calls the respective subgraph plugins and service. -8. Each subgraph has its own subgraph service, and each service can have its own subgraph plugin configuration. The subgraph service transforms the `SubgraphRequest` into an HTTP request to that subgraph. The `SubgraphRequest` contains: +7. For each fetch node of the query plan, the execution service creates a `SubgraphRequest` and then calls the respective subgraph service. +8. Each subgraph has its own subgraph service, and each service can have its own subgraph plugin configuration. The subgraph service transforms the `SubgraphRequest` into an HTTP request to its subgraph. The `SubgraphRequest` contains: - the (read-only) `SupergraphRequest` - HTTP headers + - the subgraph request's operation type (query, mutation, or subscription) - a GraphQL request object as the request body - - and the subgraph request's operation type (query, mutation, or subscription) Once your subgraphs provide a response, the response follows the path outlined below. @@ -202,6 +202,80 @@ httpServer --"14. HTTP response" --> client 13. The router service receives the `SupergraphResponse` and serializes the GraphQL responses to JSON. 14. The HTTP server sends the JSON in an HTTP response to the client. -## Next steps +### Request and response nuances + +For simplicity's sake, the preceding diagrams show the request and response sides separately and sequentially. In reality, some requests and responses may happen simultaneously and repeatedly. + +For example, `SubgraphRequest`s can happen both in parallel _and_ in sequence: one subgraph's response may be necessary for another's `SubgraphRequest`. (The query planner decides which requests can happen in parallel vs. which need to happen in sequence.) + +##### Requests run in parallel + +```mermaid +flowchart LR; + subgraph parallel[" "] + subgraph executionService["Execution Service"] + executionPlugins[[Execution plugins]]; + end + + subgraph subgraphService["Subgraph Services"] + subgraph service1["Subgraph Service A"] + subgraphPlugins1[[Subgraph plugins]]; + end + subgraph service2["Subgraph Service B"] + subgraphPlugins2[[Subgraph plugins]]; + end + end + + + executionService --"1A. SubgraphRequest"--> service1; + executionService --"1B. SubgraphRequest"--> service2; + service1 --"4A. SubgraphResponse"--> executionService; + service2 --"4B. SubgraphResponse"--> executionService; + end + subgraphA[Subgraph A]; + subgraphB[Subgraph B]; + + service1 --"2A. HTTP request"--> subgraphA; + service2 --"2B. HTTP request"--> subgraphB; + subgraphA --"3A. HTTP response"--> service1; + subgraphB --"3B. HTTP response"--> service2; +``` + +##### Requests run sequentially + +```mermaid +flowchart LR; + subgraph sequentially[" "] + subgraph executionService["Execution Service"] + executionPlugins[[Execution plugins]]; + end + + subgraph subgraphService["Subgraph Services"] + subgraph service1["Subgraph Service A"] + subgraphPlugins1[[Subgraph plugins]]; + end + subgraph service2["Subgraph Service B"] + subgraphPlugins2[[Subgraph plugins]]; + end + end + + + executionService --"1. SubgraphRequest"--> service1; + service1 --"4. SubgraphResponse"--> executionService; + executionService --"5. SubgraphRequest"--> service2; + service2 --"8. SubgraphResponse"--> executionService; + end + subgraphA[Subgraph A]; + subgraphB[Subgraph B]; + + service1 --"2. HTTP request"--> subgraphA; + service2 --"6. HTTP request"--> subgraphB; + subgraphA --"3. HTTP response"--> service1; + subgraphB --"7. HTTP response"--> service2; +``` + +Additionally, some requests and responses may happen multiple times for the same operation. With subscriptions, for example, a subgraph sends a new `SubgraphResponse` whenever data is updated. Each response object travels through all the services in the response path and interacts with any customizations you've created. + +## Customization creation To learn how to hook in to the various lifecycle stages, including examples customizations, refer to the [Rhai scripts](./rhai/) and [external coprocessing](./coprocessor/) docs. \ No newline at end of file From 5d496712a5b9e546f208dead37765df39c5cb76c Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Tue, 8 Aug 2023 11:38:29 -0600 Subject: [PATCH 11/12] Copy edit --- docs/source/customizations/overview.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/customizations/overview.mdx b/docs/source/customizations/overview.mdx index 90bf38cf87..cbc15e0aa8 100644 --- a/docs/source/customizations/overview.mdx +++ b/docs/source/customizations/overview.mdx @@ -15,7 +15,7 @@ The Apollo Router supports the following customization types: - If your organization has a [GraphOS Enterprise plan](https://www.apollographql.com/pricing/), you can write custom request-handling code in any language. This code can run in the same container as your router or separately. - The router calls your custom code via HTTP, passing it the details of each incoming client request. -Because [Rhai scripts](./rhai/) are easier to deploy, we recommend using them if they support your use case. External co-processing is most helpful if your customization needs to do any of the following (which Rhai scripts _don't_ support): +Because [Rhai scripts](./rhai/) are easier to deploy, we recommend using them if they support your use case. Use external co-processing if your customization needs to do any of the following (which Rhai scripts _don't_ support): - Read or write to disk - Make network requests From bfceb224ebbae0f5d4ced6eda3f4ced07e4c3725 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 11 Aug 2023 10:16:38 +0200 Subject: [PATCH 12/12] attribution --- .changesets/docs_geal_document_request_lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changesets/docs_geal_document_request_lifecycle.md b/.changesets/docs_geal_document_request_lifecycle.md index 3b4c9d9cbf..c6e6ba536a 100644 --- a/.changesets/docs_geal_document_request_lifecycle.md +++ b/.changesets/docs_geal_document_request_lifecycle.md @@ -6,4 +6,4 @@ This adds in-depth documentation of: - the request and response types they use - where plugins can attach themselves -By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/3391 +By [@Geal](https://github.com/Geal) [@Meschreiber](https://github.com/Meschreiber) in https://github.com/apollographql/router/pull/3391