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 write 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 passes a queries hash to the fetch function for you to use.

Automatic

An approach to Persisted Queries, popularized by Apollo, is known as Automatic Persisted Queries. This involves first sending a query’s hash and if its unrecognized, sending the full query string. This might look something like:

/// src/environment.ts
async function sendFetch({ text, variables, hash }) {
    const result = await this.fetch('localhost:4000/graphql', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            query: text,
            variables,
            extensions: {
                persistedQuery: {
                    version: 1,
                    sha256Hash: hash
                }
            }
        })
    })

    return result.json()
}

export default new Environment(async function (args) {
    // first send the request without the text, only the hash
    const response = await sendFetch.call(this, {
        variables: args.variables,
        hash: args.hash,
        text: null
    })

    // if there were no errors, we're good to go
    if (!response.errors) {
        return response
    }

    // there were errors, send the hash and the query to associate
    // the two for future requests
    return await sendFetch.call(this, args)
})

Fixed List

If you don’t want the flexibility of Automatic Persisted Queries, you will need a fixed association of hash to query for every document that your client will send. To support this, you can pass the --persist-output flag to the generate command and provide a path to save the map:

npx houdini generate --persist-output ./queries.json
# or
npx houdini generate -po ./queries.json

Once this map has been created, you will have to make it available to your server.

Now, instead of sending the full operation text with every request, you can now simply pass the hash under whatever field name you prefer:

/// src/environment.ts
export default new Environment(async function (args) {
    const result = await this.fetch('http://localhost:4000', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            doc_id: args.hash,
            variables: args.variables
        })
    })

    // parse the result as json
    return await result.json()
})