Persisted queries in GraphQL: Slim down Apollo requests to your Ruby application
Tags: ruby, graphql, apollo
Published: 2019-11-07 13:24:52

---
title: Persisted queries in GraphQL: Slim down Apollo requests to your Ruby application
published: true
description: How to set up persisted queries between a Ruby GraphQL application and an Apollo client
tags: ruby, graphql, apollo
canonical_url: https://evilmartians.com/chronicles/persisted-queries-in-graphql-slim-down-apollo-requests-to-your-ruby-application
---

Learn how to reduce the size of network requests from the Apollo client in the front-end to the GraphQL Ruby back-end with the help of [persisted queries](https://www.apollographql.com/docs/apollo-server/performance/apq/). In this article, we will show how these queries work and set them up both on a client and a server with the help of our [graphql-ruby-persisted_queries](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries) Ruby gem.

One of the benefits of using [GraphQL](https://graphql.org) is its flexibility: back-end describes the data model using types, so front-end can only get the data it needs. The problem is that the _amount_ of data required to render a page in a real-world application can be significant, and the query to fetch this data will get out of hand pretty quick. What could help here?

> Read how Relay deals with persisted queries [here](https://relay.dev/docs/en/persisted-queries).

First of all, the amount and the variety of often-used queries in your application are limited: usually, front-end knows precisely what it needs from the back-end for every particular view. Popular GraphQL frameworks like [Apollo](https://www.apollographql.com) and [Relay](https://relay.dev) use that fact to _persist_ queries in the back-end so that a front-end can send just a unique ID over the wire, instead of the full query in all its verboseness. This article will focus on the Apollo implementation on [persisted queries](https://www.apollographql.com/docs/apollo-server/performance/apq/).

> If you have the Pro Version of `graphql-ruby`–you already have built-in [support](https://graphql-ruby.org/operation_store/overview.html) for persisted queries.

Most of Ruby applications that implement GraphQL server use a gem called [graphql-ruby](https://graphql-ruby.org), in this article we are going to find out how to make it work with persisted query IDs coming from the Apollo Client. By default, `graphql-ruby` uses POST requests, as query strings tend to become very long, and it makes it hard to configure HTTP caching. If we switch to persisted queries, we will be able to turn on GET requests too and unleash the power of HTTP caching!

The idea behind persisting queries is pretty straightforward–the back-end should look for a special query parameter, containing the unique ID of the query, find the query in the _store_ (we'll talk about it later, imagine that it's just a key-value store) and use it to prepare the response.

Relay has a compiler, and it _knows_ all the queries in the app, so it can put them to a dedicated text file along with their _md5_ hashes when `--persist-output` option is provided. You can save these queries to your _store_, and you're done: back-end is ready to consume IDs instead of full queries.

Apollo has no compiler, and there is no built-in way to get the list of queries. In this case, queries should be saved to a _store_ at runtime, and there is a special [apollo-link-persisted-queries](https://github.com/apollographql/apollo-link-persisted-queries) library that enables this feature. First, change your frontend configuration like so:

```js
import { createPersistedQueryLink } from "apollo-link-persisted-queries";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import ApolloClient from "apollo-client";

const link = createPersistedQueryLink().concat(createHttpLink({ uri: "/graphql" }));
const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: link,
});
```

Now, let's talk about the back-end. With the configured link, client will _make an attempt_ to send the unique ID (_sha256_ is used in this case) in the `extension` param, e.g.:

```js
{
  extensions: {
    persistedQuery: {
      version: 1,
      sha256Hash: "688787d8ff144c502c7f5cffaafe2cc588d86079f9de88304c26b0cb99ce91c6"
    }
  }
}
```

When server finds the ID in the store—it serves the query if it were sent in full, otherwise–server returns `{ errors: [{ message: "PersistedQueryNotFound" }] }` in the response payload. In this case client will send the full query along with unique ID and back-end will persist it to the store.

Now we need to implement our back-end functionality in the `GraphqlController`:

```ruby
class GraphqlController < ApplicationController
  PersistedQueryNotFound = Class.new(StandardError)

  def execute
    query_str = persisted_query || params[:query]

    result = GraphqlSchema.execute(
      query_str,
      variables: ensure_hash(params[:variables]),
      context: {},
      operation_name: params[:operationName]
    )

    render json: result
  rescue PersistedQueryNotFound
    render json: { errors: [{ message: "PersistedQueryNotFound" }] }
  end

  private

  def persisted_query
    return if params[:extensions].nil?

    hash = ensure_hash(params[:extensions]).dig("persistedQuery", "sha256Hash")

    return if hash.nil?

    if params[:query]
      store.save_query(hash, params[:query])
      return
    end

    query_str = store.fetch_query(hash)

    raise PersistedQueryNotFound if query_str.nil?

    query_str
  end

  def store
    MemoryStore.instance
  end

  def ensure_hash(ambiguous_param)
    # ...
  end
end

```

Now we need to implement the `MemoryStore` (as we know, we need a very simple key-value storage with read and write operations), and that's it, minimalistic back-end support of persisted queries is ready:

```ruby
class MemoryStore
  def self.instance
    @instance ||= new
  end

  def initialize
    @storage = {}
  end

  def fetch_query(hash)
    @storage[hash]
  end

  def save_query(hash, query)
    @storage[hash] = query
  end
end
```

And that is it! If you configured your client properly and changed your `GraphqlController`, it should work! So you don't have to copy all the boilerplate above between projects, I released a little gem called [graphql-ruby-persisted_queries](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries), which implements all the features we discussed earlier, and more:

- [Redis](https://redis.io) storage;
- hash verification;
- hash function configuration;
- Relay support is on its way!

But did not we mention GET requests earlier? Now, we can turn them on, just open up `routes.rb` and add the following line:

```ruby
get "/graphql", to: "graphql#execute"
```

We also need to slightly change the initialization of `apollo-link-persisted-queries`, which should send queries without query string as GET, and full queries as POST:

```js
const link = createPersistedQueryLink({ useGETForHashedQueries: true }).concat(
  createHttpLink({ uri: "/graphql" })
);
```

---

Persisted queries can help you reduce the amount of traffic between your GraphQL server and its clients by storing query strings at the back-end. If you have the pro version of `graphql-ruby`–you're all set, otherwise, take a look at the open-source alternative [graphql-ruby-persisted_queries](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries) we have just cooked up.

---

Read more dev articles on [https://evilmartians.com/chronicles](https://evilmartians.com/chronicles)!