# Trusted Documents

GraphQL APIs provide clients with considerable flexibility to query any data they need. This flexibility represents one of GraphQL's major strengths, but it also introduces vulnerabilities. When any client can query any data, malicious or careless queries can create excessive load on the server. Trusted Documents solve this problem.

The concept has existed since the early days of GraphQL, using terms like _persisted queries_ or _persisted operations_. An API that uses Trusted Documents accepts only GraphQL documents (queries, operations) submitted (trusted) at development or deployment time. Instead of sending the whole document, clients send a more compact _document id_. This approach enhances security by rejecting malicious queries and improves performance by transmitting only the document id, similar to what occurs in [Automatic Persisted Queries](https://grafbase.com/docs/gateway/performance/automatic-persisted-queries.md).

## Overview

Adopting Trusted Documents places constraints primarily on API clients. To enforce trusted documents in a Grafbase API, you simply set a single option in `grafbase.toml` (see below).

We will begin by exploring the more complex aspects of adopting Trusted Documents and then explain how to enforce them.

## Trusting Documents: Upload a Manifest

The purpose of Trusted Documents is to accept only queries on an allow-list. Start by generating and communicating that list. We call the allow-list document a **manifest**, and it takes the form of a JSON file. Your GraphQL client setup of choice creates the manifest.

The two most common setups for generating a trusted documents manifest are [Relay Persisted Queries](https://relay.dev/docs/guides/persisted-queries/#local-persisted-queries) and Apollo Client operation manifests ([JS](https://www.apollographql.com/docs/react/api/link/persisted-queries/#1-generate-operation-manifests), [Kotlin](https://www.apollographql.com/docs/kotlin/advanced/persisted-queries/), [iOS](https://www.apollographql.com/docs/ios/fetching/persisted-queries/#2-generate-operation-manifest)). Grafbase natively supports both Relay and Apollo Client manifest formats. If you need support for another setup or manifest format, please [contact us](/contact).

After you create a manifest JSON file that includes the GraphQL documents your application needs and the associated document IDs, submit the manifest using the `grafbase` CLI:

```bash
grafbase trust my-account/my-graph@main --manifest manifest.json --client-name ios-client
```

Let's break down the arguments:

- `grafbase trust my-account/graph-name@main`: Like many other CLI commands, `trust` requires a graph reference in the format `<account>/<graph>@<branch>`. Remember that you must include the branch name to avoid defaulting to the production branch, which can introduce security risks.
- `--manifest manifest.json`: Provide the file path to the JSON file generated by your client of choice.
- `--client-name`: Each client of an API using trusted documents must identify itself with a client name using the `x-grafbase-client-name` HTTP header. Read on for more details.

After you submit the manifest, the API trusts the GraphQL documents in the manifest, associating them with their corresponding document IDs. **The trust command applies to a single branch and a single client name**. To enforce trusted documents across multiple branches or clients, you must trust the relevant documents for each combination.

## Trusted Documents in the Client: Runtime Components

In the previous section, we uploaded the trusted document manifests. Now, our API knows which documents to expect. Our GraphQL client needs to change its requests to the API in two ways:

1. Send the `x-grafbase-client-name` header with the same name used when submitting the manifest with `grafbase trust`.
2. Send the trusted document IDs instead of the document body in GraphQL requests.

For example [in Relay](https://relay.dev/docs/guides/persisted-queries/#network-layer-changes):

```ts
function fetchQuery(operation, variables) {
  return fetch('/graphql', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'x-grafbase-client-name': 'ios-app',
    },
    body: JSON.stringify({
      // `doc_id` is also accepted.
      documentId: operation.id, // NOTE: pass md5 hash to the server
      // query: operation.text, // this is now obsolete because text is null
      variables,
    }),
  }).then(response => {
    return response.json()
  })
}
```

or with [Apollo Client](https://www.apollographql.com/docs/react/api/link/persisted-queries/#persisted-queries-implementation):

```ts
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries'
import { generatePersistedQueryIdsFromManifest } from '@apollo/persisted-query-lists'

const httpLink = new HttpLink({
  uri: 'http://localhost:4000/graphql',
  headers: {
    'x-grafbase-client-name': 'ios-app',
  },
})

const persistedQueryLink = createPersistedQueryLink(
  generatePersistedQueryIdsFromManifest({
    loadManifest: () => import('./path/to/persisted-query-manifest.json'),
  }),
)

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: persistedQueriesLink.concat(httpLink),
})
```

## Enforcing Trusted Documents with the Self-hosted Grafbase Gateway

On the server side, the process is straightforward. You will find one relevant section in `grafbase.toml`:

```toml
[trusted_documents]
enabled = true
enforced = false
bypass_header_name = "my-header-name" # default null
bypass_header_value = "my-secret-is-{{ env.SECRET_HEADER_VALUE }}" # default null
```

See the [reference documentation](https://grafbase.com/docs/gateway/configuration/trusted-documents.md) for a list of all the options and their effects.