diff --git a/.changeset/lemon-flies-dream.md b/.changeset/lemon-flies-dream.md new file mode 100644 index 0000000000000..7f7901d7b6ecd --- /dev/null +++ b/.changeset/lemon-flies-dream.md @@ -0,0 +1,6 @@ +--- +"@graphql-mesh/supergraph": minor +"@graphql-mesh/types": patch +--- + +Ability to configure subgraphs diff --git a/packages/legacy/handlers/supergraph/src/index.ts b/packages/legacy/handlers/supergraph/src/index.ts index e56531803a22c..7ecac757cc5b9 100644 --- a/packages/legacy/handlers/supergraph/src/index.ts +++ b/packages/legacy/handlers/supergraph/src/index.ts @@ -3,6 +3,7 @@ import { process } from '@graphql-mesh/cross-helpers'; import { PredefinedProxyOptions, StoreProxy } from '@graphql-mesh/store'; import { getInterpolatedHeadersFactory, + ResolverData, stringInterpolator, } from '@graphql-mesh/string-interpolation'; import { @@ -81,6 +82,7 @@ export default class SupergraphHandler implements MeshHandler { } async getMeshSource({ fetchFn }: GetMeshSourcePayload): Promise { + const subgraphConfigs = this.config.subgraphs || []; this.fetchFn = fetchFn; const supergraphSdl = await this.getSupergraphSdl(); const operationHeadersFactory = @@ -89,17 +91,38 @@ export default class SupergraphHandler implements MeshHandler { : undefined; const schema = getStitchedSchemaFromSupergraphSdl({ supergraphSdl, - onExecutor: ({ endpoint }) => { + onExecutor: ({ subgraphName, endpoint }) => { + const subgraphConfiguration: YamlConfig.SubgraphConfiguration = subgraphConfigs.find( + subgraphConfig => subgraphConfig.name === subgraphName, + ) || { + name: subgraphName, + }; return buildHTTPExecutor({ endpoint, + ...(subgraphConfiguration as any), fetch: fetchFn, - headers: - operationHeadersFactory && - (execReq => - operationHeadersFactory({ - env: process.env, - context: execReq.context, - })), + headers(executorRequest) { + const subgraphConfiguration = subgraphConfigs.find( + subgraphConfig => subgraphConfig.name === subgraphName, + ); + const headers = {}; + const resolverData: ResolverData = { + root: executorRequest.rootValue, + env: process.env, + context: executorRequest.context, + info: executorRequest.info, + args: executorRequest.variables, + }; + if (subgraphConfiguration?.operationHeaders) { + const headersFactory = getInterpolatedHeadersFactory( + subgraphConfiguration.operationHeaders, + ); + Object.assign(headers, headersFactory(resolverData)); + } + if (operationHeadersFactory) { + Object.assign(headers, operationHeadersFactory(resolverData)); + } + }, } as HTTPExecutorOptions); }, batch: this.config.batch == null ? true : this.config.batch, diff --git a/packages/legacy/handlers/supergraph/yaml-config.graphql b/packages/legacy/handlers/supergraph/yaml-config.graphql index 5f5dadf0b53bb..5692cc93892c0 100644 --- a/packages/legacy/handlers/supergraph/yaml-config.graphql +++ b/packages/legacy/handlers/supergraph/yaml-config.graphql @@ -15,4 +15,74 @@ type SupergraphHandler @md { operationHeaders: Any batch: Boolean + + subgraphs: [SubgraphConfiguration!] +} + +type SubgraphConfiguration { + """ + The name of the subgraph you want to configure + """ + name: String! + + """ + A url or file path to your remote GraphQL endpoint. + If you provide a path to a code file(js or ts), + other options will be ignored and the schema exported from the file will be used directly. + """ + endpoint: String + + """ + JSON object representing the Headers to add to the runtime of the API calls only for operation during runtime + """ + operationHeaders: JSON + """ + Use HTTP GET for Query operations + """ + useGETForQueries: Boolean + """ + HTTP method used for GraphQL operations + """ + method: GraphQLHandlerHTTPMethod + """ + Request Credentials if your environment supports it. + [See more](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) + + @default "same-origin" + """ + credentials: RequestCredentials + """ + Path to a custom W3 Compatible WebSocket Implementation + """ + webSocketImpl: String + """ + Path to the introspection + You can separately give schema introspection or SDL + """ + source: String + """ + SSE - Server Sent Events + WS - New graphql-ws + LEGACY_WS - Legacy subscriptions-transport-ws + """ + subscriptionsProtocol: SubscriptionProtocol + """ + URL to your endpoint serving all subscription queries for this source + """ + subscriptionsEndpoint: String + """ + Retry attempts if fails + """ + retry: Int + """ + Timeout in milliseconds + """ + timeout: Int + """ + JSON object representing the `connectionParams` from a WebSocket connection to add to the runtime of the API calls only for operation during runtime. + More information about the WebSocket `connectionParams`: + - When using `subscriptionsProtocol=WS` (graphql-ws): https://github.com/enisdenjo/graphql-ws/blob/master/docs/interfaces/client.ClientOptions.md#connectionparams + - When using `subscriptionsProtocol=LEGACY_WS` (subscriptions-transport-ws): https://github.com/apollographql/subscriptions-transport-ws/blob/51270cc7dbaf09c7b9aa67368f1de58148c7d334/README.md#subscriptionclient + """ + connectionParams: JSON } diff --git a/packages/legacy/types/src/config-schema.json b/packages/legacy/types/src/config-schema.json index 64a7fae26f3b6..d2d69ed857f6a 100644 --- a/packages/legacy/types/src/config-schema.json +++ b/packages/legacy/types/src/config-schema.json @@ -3177,10 +3177,86 @@ }, "batch": { "type": "boolean" + }, + "subgraphs": { + "type": "array", + "items": { + "$ref": "#/definitions/SubgraphConfiguration" + }, + "additionalItems": false } }, "required": ["source"] }, + "SubgraphConfiguration": { + "additionalProperties": false, + "type": "object", + "title": "SubgraphConfiguration", + "properties": { + "name": { + "type": "string", + "description": "The name of the subgraph you want to configure" + }, + "endpoint": { + "type": "string", + "description": "A url or file path to your remote GraphQL endpoint.\nIf you provide a path to a code file(js or ts),\nother options will be ignored and the schema exported from the file will be used directly." + }, + "operationHeaders": { + "type": "object", + "properties": {}, + "description": "JSON object representing the Headers to add to the runtime of the API calls only for operation during runtime" + }, + "useGETForQueries": { + "type": "boolean", + "description": "Use HTTP GET for Query operations" + }, + "method": { + "type": "string", + "enum": ["GET", "POST"], + "description": "HTTP method used for GraphQL operations (Allowed values: GET, POST)" + }, + "credentials": { + "type": "string", + "enum": ["omit", "include"], + "description": "Request Credentials if your environment supports it.\n[See more](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)\n\n@default \"same-origin\" (Allowed values: omit, include)" + }, + "webSocketImpl": { + "type": "string", + "description": "Path to a custom W3 Compatible WebSocket Implementation" + }, + "source": { + "type": "string", + "description": "Path to the introspection\nYou can separately give schema introspection or SDL" + }, + "subscriptionsProtocol": { + "type": "string", + "enum": ["SSE", "WS", "LEGACY_WS"], + "description": "SSE - Server Sent Events\nWS - New graphql-ws\nLEGACY_WS - Legacy subscriptions-transport-ws (Allowed values: SSE, WS, LEGACY_WS)" + }, + "subscriptionsEndpoint": { + "type": "string", + "description": "URL to your endpoint serving all subscription queries for this source" + }, + "retry": { + "type": "integer", + "description": "Retry attempts if fails" + }, + "timeout": { + "type": "integer", + "description": "Timeout in milliseconds" + }, + "batch": { + "type": "boolean", + "description": "Enable/Disable automatic query batching" + }, + "connectionParams": { + "type": "object", + "properties": {}, + "description": "JSON object representing the `connectionParams` from a WebSocket connection to add to the runtime of the API calls only for operation during runtime.\nMore information about the WebSocket `connectionParams`:\n - When using `subscriptionsProtocol=WS` (graphql-ws): https://github.com/enisdenjo/graphql-ws/blob/master/docs/interfaces/client.ClientOptions.md#connectionparams\n - When using `subscriptionsProtocol=LEGACY_WS` (subscriptions-transport-ws): https://github.com/apollographql/subscriptions-transport-ws/blob/51270cc7dbaf09c7b9aa67368f1de58148c7d334/README.md#subscriptionclient" + } + }, + "required": ["name"] + }, "ThriftHandler": { "additionalProperties": false, "type": "object", diff --git a/packages/legacy/types/src/config.ts b/packages/legacy/types/src/config.ts index 1287c78b4836b..3ef3f5a3d0102 100644 --- a/packages/legacy/types/src/config.ts +++ b/packages/legacy/types/src/config.ts @@ -1012,6 +1012,80 @@ export interface SupergraphHandler { schemaHeaders?: any; operationHeaders?: any; batch?: boolean; + subgraphs?: SubgraphConfiguration[]; +} +export interface SubgraphConfiguration { + /** + * The name of the subgraph you want to configure + */ + name: string; + /** + * A url or file path to your remote GraphQL endpoint. + * If you provide a path to a code file(js or ts), + * other options will be ignored and the schema exported from the file will be used directly. + */ + endpoint?: string; + /** + * JSON object representing the Headers to add to the runtime of the API calls only for operation during runtime + */ + operationHeaders?: { + [k: string]: any; + }; + /** + * Use HTTP GET for Query operations + */ + useGETForQueries?: boolean; + /** + * HTTP method used for GraphQL operations (Allowed values: GET, POST) + */ + method?: 'GET' | 'POST'; + /** + * Request Credentials if your environment supports it. + * [See more](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) + * + * @default "same-origin" (Allowed values: omit, include) + */ + credentials?: 'omit' | 'include'; + /** + * Path to a custom W3 Compatible WebSocket Implementation + */ + webSocketImpl?: string; + /** + * Path to the introspection + * You can separately give schema introspection or SDL + */ + source?: string; + /** + * SSE - Server Sent Events + * WS - New graphql-ws + * LEGACY_WS - Legacy subscriptions-transport-ws (Allowed values: SSE, WS, LEGACY_WS) + */ + subscriptionsProtocol?: 'SSE' | 'WS' | 'LEGACY_WS'; + /** + * URL to your endpoint serving all subscription queries for this source + */ + subscriptionsEndpoint?: string; + /** + * Retry attempts if fails + */ + retry?: number; + /** + * Timeout in milliseconds + */ + timeout?: number; + /** + * Enable/Disable automatic query batching + */ + batch?: boolean; + /** + * JSON object representing the `connectionParams` from a WebSocket connection to add to the runtime of the API calls only for operation during runtime. + * More information about the WebSocket `connectionParams`: + * - When using `subscriptionsProtocol=WS` (graphql-ws): https://github.com/enisdenjo/graphql-ws/blob/master/docs/interfaces/client.ClientOptions.md#connectionparams + * - When using `subscriptionsProtocol=LEGACY_WS` (subscriptions-transport-ws): https://github.com/apollographql/subscriptions-transport-ws/blob/51270cc7dbaf09c7b9aa67368f1de58148c7d334/README.md#subscriptionclient + */ + connectionParams?: { + [k: string]: any; + }; } /** * Handler for OData diff --git a/website/src/generated-markdown/SupergraphHandler.generated.md b/website/src/generated-markdown/SupergraphHandler.generated.md index 90b0ce11e6f39..8bf3aaa2ed689 100644 --- a/website/src/generated-markdown/SupergraphHandler.generated.md +++ b/website/src/generated-markdown/SupergraphHandler.generated.md @@ -4,4 +4,30 @@ If you provide a path to a code file(js or ts), other options will be ignored and the schema exported from the file will be used directly. * `schemaHeaders` (type: `Any`) * `operationHeaders` (type: `Any`) -* `batch` (type: `Boolean`) \ No newline at end of file +* `batch` (type: `Boolean`) +* `subgraphs` (type: `Array of Object`, required): + * `name` (type: `String`, required) - The name of the subgraph you want to configure + * `endpoint` (type: `String`) - A url or file path to your remote GraphQL endpoint. +If you provide a path to a code file(js or ts), +other options will be ignored and the schema exported from the file will be used directly. + * `operationHeaders` (type: `JSON`) - JSON object representing the Headers to add to the runtime of the API calls only for operation during runtime + * `useGETForQueries` (type: `Boolean`) - Use HTTP GET for Query operations + * `method` (type: `String (GET | POST)`) - HTTP method used for GraphQL operations + * `credentials` (type: `String (omit | include)`) - Request Credentials if your environment supports it. +[See more](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) + +@default "same-origin" + * `webSocketImpl` (type: `String`) - Path to a custom W3 Compatible WebSocket Implementation + * `source` (type: `String`) - Path to the introspection +You can separately give schema introspection or SDL + * `subscriptionsProtocol` (type: `String (SSE | WS | LEGACY_WS)`) - SSE - Server Sent Events +WS - New graphql-ws +LEGACY_WS - Legacy subscriptions-transport-ws + * `subscriptionsEndpoint` (type: `String`) - URL to your endpoint serving all subscription queries for this source + * `retry` (type: `Int`) - Retry attempts if fails + * `timeout` (type: `Int`) - Timeout in milliseconds + * `batch` (type: `Boolean`) - Enable/Disable automatic query batching + * `connectionParams` (type: `JSON`) - JSON object representing the `connectionParams` from a WebSocket connection to add to the runtime of the API calls only for operation during runtime. +More information about the WebSocket `connectionParams`: + - When using `subscriptionsProtocol=WS` (graphql-ws): https://github.com/enisdenjo/graphql-ws/blob/master/docs/interfaces/client.ClientOptions.md#connectionparams + - When using `subscriptionsProtocol=LEGACY_WS` (subscriptions-transport-ws): https://github.com/apollographql/subscriptions-transport-ws/blob/51270cc7dbaf09c7b9aa67368f1de58148c7d334/README.md#subscriptionclient \ No newline at end of file diff --git a/website/src/pages/docs/handlers/supergraph.mdx b/website/src/pages/docs/handlers/supergraph.mdx index 6adebba67b271..56355eaaabb81 100644 --- a/website/src/pages/docs/handlers/supergraph.mdx +++ b/website/src/pages/docs/handlers/supergraph.mdx @@ -22,6 +22,42 @@ sources: myTokenHeader: MY_TOKEN_VALUE ``` +### Configuring subgraphs within the supergraph + +You can also configure subgraphs within the supergraph by providing the subgraph name in order to +change the endpoint and headers for each subgraph. + +```yaml +sources: + - name: Supergraph + handler: + supergraph: + source: http://some-source.com/supergraph.graphql + subgraphs: + accounts: + endpoint: http://localhost:9871/graphql + operationHeaders: + Authorization: "Bearer {context.headers['x-accounts-token']}" + reviews: + endpoint: http://localhost:9872/graphql + operationHeaders: + Authorization: "Bearer {context.headers['x-accounts-token']}" + products: + endpoint: http://localhost:9873/graphql + operationHeaders: + Authorization: "Bearer {context.headers['x-accounts-token']}" + inventory: + endpoint: http://localhost:9874/graphql + operationHeaders: + Authorization: "Bearer {context.headers['x-accounts-token']}" +``` + +### Config API Reference + +import API from '../../../generated-markdown/SoapHandler.generated.md' + + + ## Using Mesh to build the supergraph You can provide the existing subgraphs within GraphQL Mesh, or you can use