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.

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
import { HoudiniClient } from '$houdini'

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 HoudiniClient(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 mapping of hash to query for every document that your client will send. To support this, you can pass the --output flag to the generate command and provide a path to save the map:

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

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

Once your serer has this list, instead of sending the full operation text with every request, you can now simply pass the hash under whatever field name your API is configured to use:

/// src/environment.ts
export default new HoudiniClient(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()
})