diff --git a/configs/cargo/Cargo.lock b/configs/cargo/Cargo.lock index eabd9c54b2f..a7ec1946eb9 100644 --- a/configs/cargo/Cargo.lock +++ b/configs/cargo/Cargo.lock @@ -2604,7 +2604,7 @@ dependencies = [ [[package]] name = "hive-apollo-router-plugin" -version = "2.3.1" +version = "2.3.2" dependencies = [ "anyhow", "apollo-router", @@ -2638,7 +2638,7 @@ dependencies = [ [[package]] name = "hive-console-sdk" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "async-trait", diff --git a/packages/web/docs/src/content/router/configuration/expressions.mdx b/packages/web/docs/src/content/router/configuration/expressions.mdx index ee1278f4a29..77dcd023c1a 100644 --- a/packages/web/docs/src/content/router/configuration/expressions.mdx +++ b/packages/web/docs/src/content/router/configuration/expressions.mdx @@ -59,6 +59,9 @@ Represents the incoming HTTP request. It exposes nested fields such as: - `.request.url.host` - the request host (e.g. `api.example.com`). - `.request.url.port` - the request port (e.g. `443`). - `.request.url.path` - the request path (e.g. `/graphql`). +- `.request.path_params` - a map of path parameters if your `graphql_endpoint` uses them. For + example, if your endpoint is `/graphql/:doc_id`, you can access the `doc_id` parameter with + `.request.path_params.doc_id`. - `.request.operation.name` - the name of the GraphQL operation being executed, if provided. - `.request.operation.type` - the type of GraphQL operation being executed (e.g. `"query"`, `"mutation"`, or `"subscription"`). diff --git a/packages/web/docs/src/content/router/configuration/http.mdx b/packages/web/docs/src/content/router/configuration/http.mdx index 3c7429f21b3..01905a7530e 100644 --- a/packages/web/docs/src/content/router/configuration/http.mdx +++ b/packages/web/docs/src/content/router/configuration/http.mdx @@ -41,3 +41,40 @@ http: host: '127.0.0.1' port: 8080 ``` + +### `graphql_endpoint` + +- **Type:** `string` +- **Default:** `"/graphql"` + +The `graphql_endpoint` property allows you to customize the HTTP path where the GraphQL API is +exposed. By default, Hive Router serves the GraphQL API at the `/graphql` endpoint. + +For example, to change the GraphQL endpoint to `/api/graphql`, you would configure it as follows: + +```yaml filename="router.config.yaml" +http: + graphql_endpoint: /api/graphql +``` + +This configuration is useful if you want to namespace your GraphQL API under a specific path or if +you have multiple services running on the same domain and want to avoid path conflicts. + +You can also use path parameters in the `graphql_endpoint` to capture dynamic segments of the URL. +This can be useful such features like Persisted Documents where the document ID can be part of the +URL path. + +`path_params` will be available in the request object inside the VRL expression context for further +processing. + +[Learn more about using path parameters with Persisted Documents](/docs/router/configuration/persisted_documents#using-url-path-parameters-to-provide-document-id). +[Learn more about the request object in the VRL expressions](/docs/router/configuration/expressions#request). + +```yaml filename="router.config.yaml" +http: + graphql_endpoint: /graphql/:doc_id +persisted_documents: + enabled: true + spec: + expression: .request.path_params.doc_id +``` diff --git a/packages/web/docs/src/content/router/configuration/index.mdx b/packages/web/docs/src/content/router/configuration/index.mdx index 0090314a06b..fffcfe9f9a9 100644 --- a/packages/web/docs/src/content/router/configuration/index.mdx +++ b/packages/web/docs/src/content/router/configuration/index.mdx @@ -28,3 +28,5 @@ that explains how to use that feature. - [`supergraph`](./configuration/supergraph): Tell the router where to find your supergraph schema. - [`traffic_shaping`](./configuration/traffic_shaping): Manage connection pooling and request handling to subgraphs. +- [`persisted_documents`](./configuration/persisted_documents): Enable persisted documents to reduce + payload size and secure your API. diff --git a/packages/web/docs/src/content/router/configuration/persisted_documents.mdx b/packages/web/docs/src/content/router/configuration/persisted_documents.mdx new file mode 100644 index 00000000000..f695dda1afe --- /dev/null +++ b/packages/web/docs/src/content/router/configuration/persisted_documents.mdx @@ -0,0 +1,304 @@ +--- +title: 'persisted_documents' +--- + +# persisted_documents + +The `persisted_documents` configuration allows you to enable [Persisted Documents] on Hive Router +that allows you to reduce the payload size of your GraphQL requests and secure your GraphQL API by +allowing only operations that are known and trusted by your Router. + +[Learn more about Persisted Documents with Hive Console](/docs/schema-registry/app-deployments#persisted-documents-on-graphql-server-and-gateway) + +## Configuration Structure + +The `persisted_documents` key is a top-level object in your `router.config.yaml`. It contains the +following fields: + +First you need to enable persisted documents by setting the `enabled` field to `true`. + +```yaml +persisted_documents: + enabled: true +``` + +### Source Configuration with `source` + +The `source` field defines where the Router will load the persisted documents from. It supports two +sources: Hive Console CDN and a local file. + +- `source`: **(object)** The source configuration for persisted documents. + +#### Hive Console CDN + +Then we need a way to fetch the persisted documents. The recommended way is to use Hive Console as +your persisted documents storage. + +- `hive`: **(object)** The Hive Console CDN source configuration. + - `endpoint`: **(string)** The Hive Console CDN endpoint to fetch persisted documents from. + Alternatively you can set the `HIVE_CDN_ENDPOINT` environment variable. + - `key`: **(string)** The Hive Console CDN API key to authenticate requests. Alternatively you can + set the `HIVE_CDN_KEY` environment variable. + +To do so, you can either use environment variables or directly set the `endpoint` and `key` fields. + +```yaml {2-4} filename="router.config.yaml" +persisted_documents: + enabled: true + source: + hive: + endpoint: https://cdn.graphql-hive.com/artifacts/v1/... # Use your Hive CDN endpoint here + key: hvABCD # Use your Hive CDN API key here +``` + +or using environment variables: + +```yaml filename="router.config.yaml" +persisted_documents: + enabled: true +``` + +Then set the following environment variables in your environment: + +```bash +export HIVE_CDN_ENDPOINT="https://cdn.graphql-hive.com/artifacts/v1/..." +export HIVE_CDN_KEY="hvABCD" +``` + +#### A Persisted Documents File as Source + +Alternatively, you can also load persisted documents from a local file. + +- `file`: **(string)** The path to the local persisted documents JSON file. + +```yaml {2-3} filename="router.config.yaml" +persisted_documents: + enabled: true + source: + file: ./persisted_documents.json # point to your local persisted documents file +``` + +That file must be a JSON file containing a map of document IDs to GraphQL operations, for example: + +```json +{ + "a1b2c3d4e5": "query GetUser($id: ID!) { user(id: $id) { id name } }", + "f6g7h8i9j0": "mutation UpdateUser($id: ID!, $name: String!) { updateUser(id: $id, name: $name) { id name } }" +} +``` + +### Configure Extraction of Document ID from Requests with `spec` + +The `spec` field defines how the Router will extract the persisted document ID from incoming +requests. By default it is set to `hive` which expects the document ID to be provided in the body as +`documentId`. + +- `spec`: **(object)** The specification for extracting document IDs from requests. +- Possible values are: + - `hive`: The default Hive specification using `documentId` in the request body. + - `apollo`: The Apollo Persisted Documents specification using + `extensions.persistedQuery.sha256Hash` in the request body. + - `relay`: The Relay specification using `doc_id` in the request body. + - `expression`: A custom specification using a user-defined expression. + +#### `documentId` in Request Body (Hive / Default) + +The default `hive` spec expects the persisted document ID to be provided in the request body as +`documentId`. + +So the request body should look like this: + +```json +{ + "documentId": "my-app~my-version~a1b2c3d4e5", + "variables": { + "id": "123" + } +} +``` + +Then the Router will look up the operation associated with the provided `documentId` and execute it. + +#### Apollo Persisted Documents Spec using `extensions.persistedQuery.sha256Hash` + +There is also support for the Apollo Persisted Documents spec, which expects the document ID to be +provided in the `extensions.persistedQuery.sha256Hash` field of the request body. + +To use this spec, set the `spec` field to `apollo`: + +```yaml {3} filename="router.config.yaml" +persisted_documents: + enabled: true + spec: apollo +``` + +So the request body should look like this: + +```json +{ + "extensions": { + "persistedQuery": { + "version": 1, + "sha256Hash": "ecf4edb46db40b5132295c0291d62fb65d6759a9eedfa4d5d612dd5ec54a6b38" + } + } +} +``` + +which can be sent like this using `curl`: + +```bash +curl -X POST -H 'Content-Type: application/json' http://localhost:4000/graphql \ + -d '{"extensions":{"per sistedQuery":{"version":1,"sha256Hash":"ecf4edb46db40b5132295c0291d62fb65d6759a9eedfa4d5d612dd5ec54a6b38"}}}' +``` + +Then the Router will look up the operation associated with the provided `sha256Hash` and execute it. + +#### Relay Specification using `doc_id` in Request Body + +You can also use the Relay specification, which expects the persisted document ID to be provided in +the request body as `doc_id`. + +To use this spec, set the `spec` field to `relay`: + +```yaml {3} filename="router.config.yaml" +persisted_documents: + enabled: true + spec: relay +``` + +Then the request body should look like this: + +```json +{ + "doc_id": "a1b2c3d4e5", + "variables": { + "id": "123" + } +} +``` + +Then the Router will look up the operation associated with the provided `doc_id` and execute it. + +#### Custom Specification with `expression` + +If none of the built-in specifications fit your needs, you can define a custom specification using +the `expression` option. + +- `expression`: **(string)** A [VRL expression](./expressions) that extracts the document ID from + the incoming request. + +To use a custom specification, set the `spec` field to `expression` and provide the `expression` +field: + +```yaml {3-4} filename="router.config.yaml" +persisted_documents: + enabled: true + spec: + expression: .request.body.customDocumentId +``` + +##### Using Header to Provide Document ID + +In this example, we extract the document ID from a custom header `x-document-id`. + +```yaml {3-4} filename="router.config.yaml" +persisted_documents: + enabled: true + spec: + expression: .request.headers."x-document-id" +``` + +Then the request should include the `x-document-id` header: + +```bash +curl -X POST -H 'Content-Type: application/json' -H 'x-document-id: a1b2c3d4e5' http://localhost:4000/graphql \ + -d '{"variables":{"id":"123"}}' +``` + +##### Using a Query Parameter to Provide Document ID + +In this example, we extract the document ID from a query parameter `query_id`. + +```yaml {3-4} filename="router.config.yaml" +persisted_documents: + enabled: true + spec: + expression: .request.body.query_id +``` + +Then the request URL should include the `query_id` query parameter: + +```bash +curl -X POST -H 'Content-Type: application/json' 'http://localhost:4000/graphql?query_id=a1b2c3d4e5' \ + -d '{"variables":{"id":"123"}}' +``` + +##### Using URL Path Parameters to Provide Document ID + +You can also the the request path to extract the persisted operation id. This requires you to also +customize the GraphQL endpoint. + +This combination is powerful as it allows you to use the persisted operation id as it can easily be +combined with any type of HTTP proxy cache. + +```yaml {3-5} filename="router.config.yaml" +http: + graphql_endpoint: /graphql/:doc_id +persisted_documents: + enabled: true + spec: + expression: .request.path_params.doc_id +``` + +Then the request URL should include the document ID in the path: + +```bash +curl -X POST -H 'Content-Type: application/json' 'http://localhost:4000/graphql/a1b2c3d4e5' \ + -d '{"variables":{"id":"123"}}' +``` + +### Allowing Non-Persisted Arbitrary Documents with `allow_arbitrary_operations` + +After enabling persisted documents, by default the Router will reject any requests that do not +provide a valid persisted document ID. + +- `allow_arbitrary_operations`: **(boolean / object)** Whether to allow arbitrary operations + alongside persisted documents. Default is `false`. + +If you want to allow arbitrary operations alongside persisted documents, you can set the +`allow_arbitrary_operations` field to `true`. + +```yaml {3} filename="router.config.yaml" +persisted_documents: + enabled: true + allow_arbitrary_operations: true +``` + +#### Conditional Allowance with `expression` + +Alternatively, you can conditionally allow arbitrary operations based on a user-defined expression. + +- `expression`: **(string)** A [VRL expression](./expressions) that evaluates to a boolean value to + determine whether to allow arbitrary operations. + +To use a conditional allowance, set the `allow_arbitrary_operations` field to an object with the +`expression` field: + +```yaml {3-4} filename="router.config.yaml" +persisted_documents: + enabled: true + allow_arbitrary_operations: + expression: .request.headers."x-allow-arbitrary" == "true" +``` + +Then the Router will evaluate the provided expression for each request. If it evaluates to `true`, +the Router will allow arbitrary operations; otherwise, it will enforce persisted documents only. + +In the example above, you can allow arbitrary operations by including the `x-allow-arbitrary: true` +header in your request: + +```bash +curl -X POST -H 'Content-Type: application/json' -H 'x-allow-arbitrary: true' http://localhost:4000/graphql \ + -d '{"query":"query { user(id: \"123\") { id name } }"}' +``` diff --git a/packages/web/docs/src/content/schema-registry/app-deployments.mdx b/packages/web/docs/src/content/schema-registry/app-deployments.mdx index 3603b1a9c55..01254ba2354 100644 --- a/packages/web/docs/src/content/schema-registry/app-deployments.mdx +++ b/packages/web/docs/src/content/schema-registry/app-deployments.mdx @@ -267,7 +267,7 @@ by your Gateway. Hive serves as the source of truth for the allowed persisted documents and provides a CDN for fetching these documents as they are requested. - + {/* Hive Gateway */} @@ -295,6 +295,35 @@ For further information, please refer to the +{/* Hive Router */} + + + +For Hive Router, you can choose the Hive source for resolving persisted documents. Adjust your +`router.config.yaml` file as follows. + +```yaml filename="router.config.yaml" +persisted_documents: + enabled: true + source: + hive: + endpoint: https://cdn.graphql-hive.com/artifacts/v1/... # Use your Hive CDN endpoint here + key: hvABCD # Use your Hive CDN API key here +``` + +Or you can just use the environment variables `HIVE_CDN_ENDPOINT` and `HIVE_CDN_KEY` to configure +the CDN access, then you can simplify the configuration as follows: + +```yaml filename="router.config.yaml" +persisted_documents: + enabled: true +``` + +For further configuration options, please refer to the +[Hive Router documentation for `persisted_documents`](/docs/router/configuration/persisted_documents) + + + {/* GraphQL Yoga */}