---
title: Persisted Queries
description: A guide to persisted queries in Houdini
---

# Persisted Queries

Sometimes you want to confine an API to only fire a set of pre-defined queries. This
can be useful to not only reduce the amount of information transferred over the wire
but also act as a list of approved queries, providing additional security. Regardless of
your motivation, the approach involves associating a known string with a particular query
and sending that string to the server instead of the full query body. To support this,
houdini provides a query's hash to the fetch function for you to use.

## Fixed List

The simplest solution for persisted queries it to generate a fixed mapping of hash to
query for every document that your client will send.

Two ways to generate this list:
1. Configuring the `persistedQueriesPath` option in your
`houdini.config.js` file. More info in the [config section](/api/config#fields).
2. Running the `generate` command with the `--output` flag 
and provide a path to save the map:

```bash
npx houdini generate --output ./queries.json
# or
npx houdini generate -o ./queries.json
```

Assuming you've made this list available to your server somehow, you can now simply
pass the hash under whatever field name your API is configured to use instead of
sending the full operation text with every request:

```typescript:title=src/client.ts&typescriptToggle=true
export default new HoudiniClient({
    url: 'http://localhost:4000',
    fetchParams({ hash, variables }){
        return {
            body: JSON.stringify({
                doc_id: hash,
                variables: variables
            })
        }
    }
})
```

## Automatic

An approach to Persisted Queries, popularized by Apollo, is known as
[Automatic Persisted Queries (APQ)](https://www.apollographql.com/docs/apollo-server/performance/apq/).
This involves first sending a query's hash and if its unrecognized, sending the full
query string. The easiest way to do this is to define a [client plugin](/api/client-plugins).
This might look something like:

```typescript:title=src/client.ts&typescriptToggle=true
import { HoudiniClient } from '$houdini'
import type { ClientPlugin } from '$houdini'

export default new HoudiniClient({
    url: 'localhost:4000/graphql',
    // by default, send the hash and variables
    fetchParams({ variables, hash }) {
        return {
            body: JSON.stringify({
                variables,
                extensions: {
                    persistedQuery: {
                        version: 1,
                        sha256Hash: hash
                    }
                }
            })
        }
    },
    plugins: [
        // but we'll retry the request if we identify a missing hash
        retryPlugin
    ]
})

// if the response contains an error indicating a missing hash, we
// need to try again with the full payload
function retryPlugin() {
    return {
        afterNetwork(ctx, { marshalVariables, value, next, resolve }) {
            // if there are no errors, we're good to move on
            if (!value.errors) {
                return resolve(ctx)
            }

            // there was an error, check if it indicates a missing hash
            if (value.errors.some(isMissingHashError)) {
                // try again with the query text
                ctx.fetchParams = {
                    ...ctx.fetchParams,
                    body: JSON.stringify({
                        text: ctx.text,
                        variables: marshalVariables(ctx),
                        extensions: {
                            persistedQuery: {
                                version: 1,
                                sha256Hash: hash
                            }
                        }
                    })
                }

                return next(ctx)
            }

            // the error does not indicate there was a missing
            // hash so resolve the request with the error
            return resolve(ctx)
        }
    }
}
```
