From 1932427056064076b173ca5aa80095a28b5386d3 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Thu, 26 Dec 2024 15:10:10 +0100 Subject: [PATCH] docs: Rework the Custom Plugins page (#6128) Co-authored-by: Arda TANRIKULU --- .../gateway/other-features/custom-plugins.mdx | 706 +++++++++++++----- 1 file changed, 539 insertions(+), 167 deletions(-) diff --git a/packages/web/docs/src/pages/docs/gateway/other-features/custom-plugins.mdx b/packages/web/docs/src/pages/docs/gateway/other-features/custom-plugins.mdx index f9339b31b..a2d6ac6e4 100644 --- a/packages/web/docs/src/pages/docs/gateway/other-features/custom-plugins.mdx +++ b/packages/web/docs/src/pages/docs/gateway/other-features/custom-plugins.mdx @@ -1,106 +1,501 @@ --- -searchable: false +description: Add feature or third party integrations with Hive Gateway plugin system. --- import { Callout } from '@theguild/components' # Custom Plugins -Hive Gateway uses -[GraphQL Yoga](https://the-guild.dev/graphql/yoga-server/docs/features/envelop-plugins), and it uses -[Envelop](https://the-guild.dev/graphql/envelop) plugin system which allows you to hook into the -different phases of the GraphQL execution to manipulate or track the entire workflow step-by-step. +Hive Gateway is built with an highly modular architecture. The core is as small as possible, and +most of the features are built around a plugin system. - -You can both use Yoga or Envelop or Gateway plugins with your GraphQL Gateway. -But you should always opt-in for the Hive Gateway variant of the plugin, then Yoga then Envelop because each of them have more control over the execution. -For example, Yoga variant of the plugin leverage HTTP hooks, and Hive Gateway one can leverage more hooks and more control over the context. +A plugin can hook into each important steps of the lifecycle of the gateway, including the graphql +request execution. -We'd recommend to check the features of the gateway first, and if you can't find what you are -looking for, then you can use this option on your own to add plugins from either GraphQL Yoga or -[Envelop's Plugin Hub](https://the-guild.dev/graphql/envelop/plugins). +The gateway is built using Graphql Yoga (a GraphQL HTTP server) and Envelop (a GraphQL execution +orchestrator), which are also highly modular technologies. Hive Gateway is extending Yoga and +Envelop plugin system by providing gateway specific hooks. - +This means that if a plugin exists in multiple variants, you should follow this priority order: -You can provide those plugins as an array of objects, +1. Hive Gateway: is aware of subgraphs and upstream request management. +2. Yoga: is aware of the HTTP transport. It most of the time provides optimisations over the + Envelop variants. +3. Envelop: is only aware of the GraphQL operation execution. -```ts filename="gateway.config.ts" {7} -import { useGraphQLJit } from '@envelop/graphql-jit' +For example, the Prometheus integration plugin is available for Envelop (`@envelop/prometheus`), +Yoga(`@graphql-yoga/plugin-prometheus`) and Hive Gateway(`@graphql-mesh/prometheus`). In this case, +you should pick `@graphql-mesh/prometheus` plugin. It is built on top of Yoga and Envelop variants, +but adds monitoring capabilities of upstream subgraphs. + +For the most used features, you will not have to explicitly setup the dedicated plugin. They will be +added automatically based on your gateway configuration. + +## Configuration + +You can add plugins to your gateway using the `plugins` option. + +It have to be a function, which will be called each time the gateway have update its configuration. +For example, if polling is enabled, this function will be called for each poll. + +Most Hive Gateway plugins takes an object as a parameter, and expect some common components like a +`logger`, a `pubsub`, etc... Those components are given in parameters to the `plugins` function. It +is advised to spread the plugin's factory context into the plugins options, this way plugins will +have access to all components they need. + +```ts filename="gateway.config.ts" import { defineConfig } from '@graphql-hive/gateway' +import useNewRelic from '@graphql-mesh/plugin-newrelic' +import { useSOFA } from '@graphql-yoga/plugin-sofa' export const gatewayConfig = defineConfig({ - plugins: () => [useGraphQLJit()] + plugins: ctx => [ + useSOFA({ + ...ctx, + basePath: '/rest', + swaggerUIEndpoint: '/rest/docs' + }), + useNewRelic({ + ...ctx + }) + ] }) ``` -## Writing Plugins +## Custom Plugins -Sometimes you might want to build your own plugins. You can write your own gateway plugin and even -share it with other people by publishing it to `npm`. +You can take advantage of the plugin system to create your own custom plugin. This allows you to +integrate your gateway with any technology that we don't officially support, being public or private +to your company. - - A good entry-point for discovering how to write Gateway plugins is to look at the source code of - the existing plugins maintained by us. - +A good entry-point for discovering how to write Gateway plugins is to look at the source code of the +existing plugins maintained by us. -The most hooks for Hive Gateway origin from the Envelop and Yoga plugin systems. -[Please refer to the Envelop Plugin Lifecycle documentation for more information.](https://the-guild.dev/graphql/envelop/docs/plugins/lifecycle) -and -[Yoga Plugin Lifecycle documentation](https://the-guild.dev/graphql/yoga-server/docs/features/envelop-plugins). -In addition, Yoga adds more HTTP specific hooks while Hive Gateway adds more related to the subgraph -execution. Gateway plugins also uses -[Explicit Resource Management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html), -so all the resources are cleaned up gracefully when Hive Gateway is shut down. You can see -`Symbol.asyncDispose` below. +A plugin is an object that can define methods to hook into different phases or events of the gateway +lifecycle. Each hook will receive a single parameter, the hook payload, containing data related to +the hooked phase and functions allowing you to modify the behavior of this phase. -### Plugin Lifecycle +There are 2 categories of hooks: -The following diagram shows the plugin lifecycle of Hive Gateway. For a detailed description of each -hook, please refer to the detail sections of each hook. Please check Yoga and Envelop documentation -for more information about the hooks except `onSubgraphExecute`. +- Gateway lifecycle hooks: they are related to the lifecycle or events of the server itself and is + not related to a specific request. +- Request lifecycle hooks: they are tight to a specific phase of a specific request. Those hooks + always give access to the related request in there payload. + +### Gateway lifecycle + +Gateway lifecycle allows to setup long running services, initialize in-memory cache or stores, react +to general events. + +
```mermaid stateDiagram-v2 - [*] --> onRequest + onYogaInit: onYogaInit + onPluginInit: onPluginInit + onDispose: onDispose + listening: Listening for HTTP requests - state onRequest_if_state <> - - onRequest --> onRequest_if_state - - onRequest_if_state --> onResponse: Is not a GraphQL Request - onRequest_if_state --> GraphQLRequest: Is GraphQL Request - - GraphQLRequest: GraphQL Request - - state GraphQLRequest { - [*] --> onRequestParse - onRequestParse --> onParams - onParams --> onParse - onParse --> onValidate - onValidate --> onContextBuilding - onContextBuilding --> onExecute - onContextBuilding --> onSubscribe - - onExecute --> onSubgraphExecute - onSubscribe --> onSubgraphExecute - - onSubgraphExecute --> onFetch - onFetch --> onSubgraphExecuteDone - - onSubgraphExecuteDone --> onExecuteDone - onSubgraphExecuteDone --> onSubscribeDone - - onExecuteDone --> onResultProcess - onSubscribeDone --> onResultProcess - - onResultProcess --> [*] - } - - GraphQLRequest --> onResponse - onResponse --> [*] + [*] --> onYogaInit + onYogaInit --> onPluginInit + onPluginInit --> listening + listening --> onDispose: Gateway is shutting down + note left of listening: onSchemaChange called for each new schema loaded + note left of listening: onFetch called for each outgoing HTTP request ``` +
+ +#### `onYogaInit` + +It is the very first hook called just before the gateway starts listening for HTTP requests. + +You can use this hook to customize the Yoga instance. + +| Payload field | Description | +| ------------- | -------------------------------------------------------------- | +| `yoga` | `YogaServer` instance that is about to start with the gateway. | + +#### `onPluginInit` + +Called before the server starts listening to HTTP requests. + +In this hook, you can initialize every long running components, such as values stores, cache, third +party clients, etc... + +You can add other plugin in this hook, which allow you to ensure plugins you depends on are present. + +| Payload field | Description | +| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `addPlugin(plugin: Plugin)` | Allows adding a plugin to the plugin list. The new plugin will be added just after the one calling this function. It can be called multiple times, preserving plugin order. | +| `plugins: Plugin[]` | The current list of plugins. It can be used to verify if a dependent plugin is already in the list or to ensure your plugin is not present twice. This should never be mutated; use `addPlugin` instead. | +| `setSchema(schema: GraphQLSchema)` | Allows replacing the current schema. | +| `registerContextErrorHandler(handler)` | Registers a handler that will be called if the GraphQL context factory throws an error. Useful for error reporting. | + +#### `onSchemaChange` + +This hook is called when the schema changes, either because the Gateway loaded it the first time, or +because a plugin changed it. + +Possible usage examples of this hooks are: + +- Monitor changes of schemas +- Analyse the schema or keep track of it for other hooks +- Modify the schema + +| Payload field | Description | +| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `schema` | The new loaded schema | +| `replaceSchema` | Replace the schema. This will trigger a new call to `onSchemaChange`, so you should keep track of schema already modified by your plugin to avoid infinite change loop. | + +#### `onFetch` + +This hook is called each time the `fetch` function is called. It can be either a request to an +upstream subgraph using the http transport, or a request made a plugin to a third party technology. + +Possible usage examples of the hooks are: + +- Manipulate HTTP [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object +- Manipulate HTTP [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object +- Change [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) implementation +- Add custom headers +- Monitor the HTTP request + +| Payload field | Description | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `url` | The target URL of the request | +| `setUrl` | Replace the target URL | +| `options` | The `fetch` function options | +| `setOptions` | Replace the options that will be used to send the request | +| `info` | The GraphQL operation info | +| `fetchFn` | The fetch function that will be used to make the request. By default, it is the one provided in the Gateway configuration if provided, or `@whatwg-node/fetch` if not. | +| `setFetchFn` | Replace the `fetch` function that will be used to make the request. It should be compatible with standard `fetch` API. | +| `executionRequest` | Present only if the request is an upstream subgraph request. It contains all information about the upstream query, notably the target subgraph name. | +| `requestId` | A unique ID identifying the client request. This is used to correlate downstream and upstream requests across services. | +| `logger` | The logger instance for the specific request that includes the details of the request and the response. | + +#### `onDispose` + +In order to clean up resources when Hive Gateway is shut down, you can use `onDispose`, +`Symbol.asyncDispose` or `Symbol.syncDispose` to clean up resources. + +```ts +export const useMyPlugin = () => { + return { + async onDispose() { + // Clean up resources + await stopConnection() + } + } +} +``` + +[You can learn more about Explicit Resource Management in Yoga documentation](https://the-guild.dev/graphql/yoga-server/docs/features/explicit-resource-management). + +### Request Lifecycle + +The following diagram shows the lifecycle of request in Hive Gateway. + +Each hook is given a single parameter called payload. Please see detail section of each hook to know +what contains each of this payloads. + +
+ +```mermaid +stateDiagram-v2 + onRequest: onRequest + onResponse: onResponse + GraphQLRequest: GraphQL Request + + [*] --> onRequest + + state onRequest_if_state <> + + onRequest --> onRequest_if_state + + onRequest_if_state --> onResponse: Is not a GraphQL Request + onRequest_if_state --> onRequestParse: Is GraphQL Request + + onRequestParse: onRequestParse + onParams: onParams + onEnveloped: onEnveloped + onParse: onParse + onValidate: onValidate + onContextBuilding: onContextBuilding + onExecute: onExecute + onSubscribe: onSubscribe + + onExecuteDone: onExecuteDone + onFetch: onFetch (HTTP only) + onSubscribeDone: onSubscribeDone + onExecutionResult: onExecutionResult + onResultProcess: onResultProcess + onDelegationPlan: onDelegationPlan + onDelegationStageExecute: onDelegationStageExecute + onSubgraphExecute: onSubgraphExecute + onSubgraphExecuteDone: onSubgraphExecuteDone + + + onRequestParse --> onParams + onParams --> onEnveloped + onEnveloped --> onParse + onParse --> onValidate + onValidate --> onContextBuilding + onContextBuilding --> onExecute: query or mutation + onContextBuilding --> onSubscribe: subscription + + state is_plan_cached <> + onExecute --> is_plan_cached + onSubscribe --> is_plan_cached + is_plan_cached --> onDelegationPlan: Plan is not cached + is_plan_cached --> onDelegationStageExecute: Plan is cached + + note left of onDelegationPlan + Will be called for each nested selection set of the query + end note + onDelegationPlan --> onDelegationStageExecute + onDelegationStageExecute --> onSubgraphExecute + onSubgraphExecute --> onFetch + onFetch --> onSubgraphExecuteDone + + state is_plan_finished <> + onSubgraphExecuteDone --> is_plan_finished + is_plan_finished --> onDelegationStageExecute: plan not finished + + state end_of_execution <> + is_plan_finished --> end_of_execution: plan finished + end_of_execution --> onExecuteDone: query or mutation + end_of_execution --> onSubscribeDone: subscription + + onExecuteDone --> onExecutionResult + onSubscribeDone --> onExecutionResult + + onExecutionResult --> onResultProcess + + onResultProcess --> onResponse + onResponse --> [*] +``` + +
+ +#### `onRequest` + +This hook is invoked for ANY incoming HTTP request. Here you can manipulate the request or create a +short circuit before Yoga handles the request. + + + +Exceptions thrown by this hook are not caught. This means they will buble up to the HTTP server +underlying implementation. + +For example, the `node:http` server crashes the entire process on uncaught exceptions. + +Prefer `onRequestParse` when possible, or wrap the hook code in a `try` block. + + + +For example, you can shortcut the manually handle an HTTP request, short-circuiting the Hive Gateway +HTTP handler: + +```ts +import type { GatewayPlugin } from '@graphql-hive/gateway' + +function useAuth(): GatewayPlugin { + return { + onRequest({ request, fetchAPI, endResponse }) { + if (!request.headers.get('authorization')) { + endResponse( + new fetchAPI.Response(null, { + status: 401, + headers: { + 'Content-Type': 'application/json' + } + }) + ) + } + } + } +} +``` + +Possible usage examples of this hook are: + +- Manipulate the request +- Short circuit before Yoga handles the request + +| Payload field | Description | +| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `request` | The incoming HTTP request as WHATWG `Request` object. [Learn more about the request](https://developer.mozilla.org/en-US/docs/Web/API/Request). | +| `serverContext` | The early context object that is shared between all hooks and the GraphQL execution. [Learn more about the context](/docs/features/context). | +| `fetchAPI` | WHATWG Fetch API implementation. [Learn more about the fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). | +| `url` | WHATWG URL object of the incoming request. [Learn more about the URL object](https://developer.mozilla.org/en-US/docs/Web/API/URL). | +| `endResponse` | A function that allows you to end the request early and send a response to the client. | + +#### `onRequestParse` + +This hook is invoked for any incoming GraphQL HTTP request and is invoked before attempting to parse +the GraphQL parameters. Here you can manipulate the request, set a custom request parser or apply +security measures such as checking for access tokens etc. + +Possible usage examples of this hook are: + +- Manipulate the request +- Set custom GraphQL request parser (use custom GraphQL protocol) +- Apply security measures + +| Payload field | Description | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `request` | The incoming HTTP request as WHATWG `Request` object. [Learn more about the request](https://developer.mozilla.org/en-US/docs/Web/API/Request). | +| `url` | The incoming HTTP request target url | +| `requestParser` | The function that will be used to extract GraphQL params from the request. | +| `setRequestParser` | Allows to replace the function that will be used to extract GraphQL params from the request | + +#### `onParams` + +This hook is invoked for an incoming GraphQL request after the GraphQL parameters (`query`, +`variables`, `extensions` and `operationName`) have been ATTEMPTED to be parsed. + +Within this hook you can manipulate and customize the parameters or even implement a whole new way +of parsing the parameters (if you wish to diverge from the GraphQL over HTTP specification). + +In addition to that you could also short-circuit and skip the GraphQL execution. E.g. you might want +to serve a result from the cache instead. + +Possible usage examples of this hook are: + +- **[Response Cache](/docs/features/response-caching)**: Short-circuit GraphQL execution if response + can be served from the cache. +- **[Persisted Operations](/docs/features/persisted-operations):** Load the `query` document string + from the persisted operations store before running the execution. +- **[APQ](/docs/features/automatic-persisted-queries):** Load/persist the `query` document string on + the persisted operations store. + +| Payload field | Description | +| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `request` | The incoming HTTP request as WHATWG `Request` object. [Learn more about the request](https://developer.mozilla.org/en-US/docs/Web/API/Request). | +| `params` | The `GraphQLParams` extracted from the request. | +| `setParams` | Allows to replace the `GraphQLParams` for this request | +| fetchAPI | [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) provided in the Hive Gateway configuration. This should be used to make outgoing HTTP request, since it will trigger the `onFetch` hook. | +| `context` | The server context. It can contain runtime specific data, such as `req` and `res` object when running in Node for example. | + +#### `onParse` + +Called for parsing the GraphQL document. This hook has a before and after stage. You can hook into +before the document is parsed and/or into after the document is parsed. + +**Example actions in this hook:** + +- Replace GraphQL parser and support future GraphQL syntax +- Collect metrics about parsing success/failures + +| Payload Field | Description | +| ------------------- | ------------------------------------------------------------------------------------------------------- | +| `context` | The current context object. | +| `extendContext` | Extend the context object with a partial. | +| `params` | The parameters that are passed to the parse call. | +| `parseFn` | The current parse function. | +| `setParseFn` | Replace the current parse function. | +| `setParsedDocument` | Set/overwrite the parsed document. If a parsed document is set the call to the parseFn will be skipped. | + +#### `onValidate` + +Called for validating the GraphQL document. This hook has a before and after stage. You can hook +into before the document is parsed and/or into after the document is parsed. + +**Example actions in this hook:** + +- Register additional validation rules (e.g. [Disable Introspection](/docs/features/introspection)) +- Collect metrics about validation success/failures + +| Payload field | Description | +| ------------------- | ------------------------------------------------------------------------- | +| `context` | The current context object. | +| `extendContext` | Extend the context object with a partial. | +| `params` | The parameters with which the validate function will be invoked. | +| `addValidationRule` | Register a validation rule that will be used for the validate invocation. | +| `validateFn` | The current validate function that will be invoked. | +| `setValidationFn` | Overwrite the current validate function. | +| `setResult` | Set a validation error result and skip the validate invocation. | + +#### `onContextBuilding` + +Called for building the GraphQL context. This hook has a before and after stage. You can hook into +before and after the context is built. + +**Example actions in this hook:** + +- Authentication +- Add data to context object (e.g. user data or dataloader instances) + +| Payload field | Description | +| ---------------------- | ---------------------------------------------------- | +| `context` | The current GraphQL context object. | +| `extendContext` | Extend the context object with a partial. | +| `breakContextBuilding` | Prevent calls on any further context building hooks. | + +#### `onExecute` + +Called for executing a GraphQL mutation or query operation. This hook has a before and after stage. +You can hook into before and after the GraphQL request is executed. + +**Example actions in this hook:** + +- Collect metrics about execution time +- Error logging/reporting + +| Payload field | Description | +| --------------------------- | -------------------------------------------------------------- | +| `executeFn` | Current execute function that will be used for execution. | +| `args` | Arguments the execute function will be invoked with. | +| `setExecuteFn` | Replace the current execute function with a new one. | +| `setResultAndStopExecution` | Set an execution result and skip calling the execute function. | +| `extendContext` | Extend the context object with a partial. | + +#### `onSubscribe` + +Called for subscribing to a GraphQL subscription operation. This hook has a before and after stage. +You can hook into before and after the GraphQL subscription is executed. + +**Example actions in this hook:** + +- Collect metrics about execution time +- Error logging/reporting + +| Payload field | Description | +| --------------------------- | -------------------------------------------------------------------------------------------------------- | +| `subscribeFn` | Current subscribe function that will be used for setting up the subscription. | +| `args` | Current arguments with which the subscribe function will be invoked. | +| `setSubscribeFn` | Replace the current subscribe function with a new one that will be used for setting up the subscription. | +| `extendContext` | Extend the context object with a partial. | +| `setResultAndStopExecution` | Set a subscribe result and skip calling the subscribe function. | + +#### `onDelegationPlan` + +Called each time a delegation plan is calculated for a query. This hook is not called for each +queries, because the plan is cached. This means it will be called only the first time a query +document is encountered since the last schema change. + +This hook is mostly used for monitoring and tracing purposes. + +| Field Name | Description | +| -------------------------- | -------------------------------------------------------- | +| `supergraph` | The GraphQL schema of the supergraph. | +| `subgraph` | The name of the subgraph. | +| `sourceSubschema` | The source subschema. | +| `typeName` | The name of the type. | +| `variables` | The variables used in the request. | +| `fragments` | The fragments used in the request. | +| `fieldNodes` | The field nodes of the request. | +| `context` | The context object. | +| `requestId` | An optional unique ID identifying the client request. | +| `logger` | An optional logger instance for the specific request. | +| `info` | An optional GraphQLResolveInfo object. | +| `delegationPlanBuilder` | The delegation plan builder. | +| `setDelegationPlanBuilder` | Function to replace the current delegation plan builder. | + +#### `onDelegationStageExecute` + #### `onSubgraphExecute` This hook is invoked for ANY request that is sent to the subgraph. @@ -114,58 +509,89 @@ This hook is invoked for ANY request that is sent to the subgraph. You can see [Prometheus plugin](/docs/gateway/authorization-authentication) for an example of how to use this hook. -#### `onFetch` +| Payload Field | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------- | +| `subgraph` | The GraphQL schema of the subgraph. | +| `subgraphName` | The name of the subgraph. | +| `transportEntry` | The transport entry that will be used to resolve queries for this subgraph | +| `executionRequest` | The execution request object containing details of the upstream GraphQL operation. | +| `setExecutionRequest` | Function to replace the current execution request. | +| `executor` | The executor function used to execute the upstream request. | +| `setExecutor` | Function to replace the current executor. | +| `requestId` | A unique ID identifying the client request. | +| `logger` | The logger instance for the specific request that includes the details of the request and the response. | -This hook is invoked everytime the gateways sends an outgoing HTTP request to an upstream service. +#### `onExecutionResult` + +This hook is invoked for each result produced for GraphQL operation, before it is processed to be +sent to client. + +In particular, it is useful to handle batched operations. If a request contains batched operations, +this hook is called once of each operation, while `onResultProcess` will be only called once for the +entire request. + +Here, you can modify the result, to add monitoring or instrumentation extensions for example. **Example actions in this hook:** -- Manipulate HTTP [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object -- Manipulate HTTP [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object -- Change [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) implementation -- Add custom headers -- Monitor the HTTP request +- Add metadata to results +- Collect errors -**Example plugins:** +| Payload field | Description | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `request` | The incoming HTTP request as WHATWG `Request` object. [Learn more about the request](https://developer.mozilla.org/en-US/docs/Web/API/Request). | +| `result` | The execution result, which can be an `ExecutionResultWithSerializer`, an `AsyncIterable`. | +| `setResult` | Allows to replace the execution result. | +| `context` | The GraphQL context. | -- [Prometheus plugin](/docs/gateway/authorization-authentication) +#### `onResultProcess` -##### `API` +This hook is invoked after a GraphQL request has been processed and before the response is forwarded +to the client. Here you can customize what transport/response processor format should be used for +sending the result over the wire. -- `supergraph`: The unified graph -- `subgraph`: The subgraph -- `subgraphName`: The name of the subgraph -- `transportEntry`: The transport entry for the subgraph including the configuration for the - upstream communication, and details. -- `executionRequest`: The execution request object that is sent to the subgraph, that includes - `document`, `variables`, `contextValue`, `operationName`, and etc. -- `setExecutionRequest`: A function to replace the execution request object that will be sent to the - subgraph. -- `executor`: The executor function that will be used to execute the request to the subgraph, and it - takes the execution request object. -- `setExecutor`: A function to replace the executor function -- `logger`: The logger instance for the specific request that includes the details of the request - and the response. +**Example actions in this hook:** -#### `Symbol.asyncDispose` or `Symbol.dispose` +- Specify custom response format +- Logging/Metrics -In order to clean up resources when Hive Gateway is shut down, you can use `Symbol.asyncDispose` or -`Symbol.syncDispose` to clean up resources. +| Field Name | Description | +| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `request` | The incoming HTTP request as WHATWG [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. | +| `result` | The execution result, which can be an `ExecutionResultWithSerializer`, an `AsyncIterable`. If the request contains batch queries, this will be an array | +| `setResult` | Allows to replace the execution result. | +| `resultProcessor` | The result processor to be used to turn the result into an HTTP [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. | +| `acceptableMediaTypes` | List of acceptable media types for the result processor. | +| `setResultProcessor` | Allows to set the result processor and the accepted media type. | +| `serverContext` | The server context which can contain runtime specific data, such as for example `req` and `res` object when running in Node | -```ts -export const useMyPlugin = () => { - return { - async [Symbol.asyncDispose]() { - // Clean up resources - stopConnection() - } - } -} -``` +#### `onResponse` -You can learn more about -[Explicit Resource Management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management) -here. +This hook is invoked after a HTTP request (both GraphQL and NON GraphQL) has been processed and +after the response has been forwarded to the client. Here you can perform any cleanup or logging +operations, or you can manipulate the outgoing response object. + + + +Exceptions thrown by this hook are not caught. This means they will buble up to the HTTP server +underlying implementation. + +For example, the `node:http` server crashes the entire process on uncaught exceptions. + +Prefer `onRequestParse` when possible, or wrap the hook code in a `try` block. + + + +**Example actions in this hook:** + +- Specify custom response format +- Logging/Metrics + +| Field Name | Description | +| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `request` | The incoming HTTP request as WHATWG `Request` object. [Learn more about the request](https://developer.mozilla.org/en-US/docs/Web/API/Request). | +| `serverContext` | The final context object that is shared between all hooks and the GraphQL execution. [Learn more about the context](/docs/features/context). | +| `response` | The outgoing HTTP response as WHATWG `Response` object. [Learn more about the response interface](https://developer.mozilla.org/en-US/docs/Web/API/Response). | ### Plugin Context @@ -190,57 +616,3 @@ export const gatewayConfig = defineConfig({ } }) ``` - -## Example Additional Plugin (SOFA) - -GraphQL SOFA allows you to generate a fully documented REST API from your GraphQL schema. This is -useful when you need to support REST clients or when you want to expose a REST API to the public. - -- **Don’t choose between REST and GraphQL** -- Get most of the **benefits of GraphQL** on the backend and frontend, while using and **exposing - REST** -- **Support all your existing clients** with REST while improving your backend stack with GraphQL -- Create custom, perfectly client-aligned REST endpoints for your frontend simply by naming a route - and attaching a query -- In the other way around (REST to GraphQL) you won’t get the best of both worlds. Instead just less - powerful, harder-to-maintain server implementations with a some of the benefits of GraphQL. It can - be a good and fast start for a migration though. -- Fully **generated documentation** that is always up-to-date -- **GraphQL Subscriptions as Webhooks** - -## Installation - -```sh npm2yarn -npm i @graphql-yoga/plugin-sofa -``` - -## Quick Start - -```ts filename="gateway.config.ts" -import { defineConfig } from '@graphql-hive/gateway' -import { useSOFA } from '@graphql-yoga/plugin-sofa' - -export const gatewayConfig = defineConfig({ - plugins: pluginCtx => [ - useSOFA({ - // The path where the REST API will be served - basePath: '/rest', - // The path where the Swagger UI will be served - swaggerUIEndpoint: '/rest/docs', - // OpenAPI Document details - info: { - title: 'My API', - description: 'My API Description', - version: '1.0.0' - } - }) - ] -}) -``` - -[Learn more about SOFA](https://the-guild.dev/graphql/sofa-api/docs) - - - You can consume the API using [feTS Client](https://the-guild.dev/openapi/fets/client/quick-start) - without any code generation! -