Skip to content

samokat-oss/graphql-codegen-persisted-queries-map

Repository files navigation

Apollo Persisted Queries plugin

English | Русский

Description

This is a plugin for the graphql-codegen library, which:

  • Generates a hash-table for the pairs of "sha256 operation hash - operation" based on all GraphQL operations found in the application
  • Creates a JSON file in the specified directory with the previously generated hash-table
  • Provides a function getApolloLinks, which creates an array with the following Apollo Link:
    • A wrapper over createPersistedQueryLink, that uses the generated hash table to transform the operation into a hash
    • A link that adds an "Operation-Hash" header with the operation hash to the requests

Motivation

Sometimes we face the task of controlling and limiting GraphQL operations in a production environment. This, at minimum, may be necessary from a security standpoint, to ensure that no potentially dangerous operation can affect the operation of our GraphQL server.

But how can we take into account the peculiarities of the GraphQL query language?

This is where the Automatic Persisted Queries functionality from Apollo comes in handy. It reduces network load by sending hashes instead of operation descriptions.

Taking this functionality as a basis and a slight modification, we can implement the following scheme:

Workflow

  1. The client application and the GraphQL server are configured to run in "Persisted Queries" mode. Among other static files, the client application contains a JSON file generated by the plugin with an object of the style "sha256 operation hash - operation".
  2. During the deployment process of the client application, the list of allowed hashes and corresponding operations (taken from the generated JSON file) is updated on the GraphQL server using available CD tools.
  3. In addition, the client application sends an "Operation-Hash" header in GraphQL queries, which contains the hash of the current operation.
  4. The GraphQL server ignores the operation description string in the request body and only looks at the "Operation-Hash" header. It extracts the operation hash from the header and checks whether this hash is in the allowed list. If the hash is in this list, the server executes the operation corresponding to this hash, applying the variables passed in the request body to it. Otherwise, the server returns an error different from PERSISTED_QUERY_NOT_FOUND to prevent the Apollo Client from making another request.

Installation

Install the plugin via npm:

npm i -S @samokat/apollo-persisted-queries-map

Usage Example

codegen.js

module.exports = {
  // ...
  generates: {
    // ...
    ['./src/__generated__/operationsMap.ts']: {
      documents: ['./src/**/!(__generated__)/*.gql.ts'],
      plugins: [
        '@samokat/apollo-persisted-queries-map'
      ],
      config: {
        persistedQueriesMap: {
          // If true, the plugin verifies all conditions for its work but does not generate data
          dryRun: true,
          // Path to generate json for the server
          operationsMapPath: 'public/operationsMap.json'
        }
      }
    },
  },
}

App.tsx

import { getApolloLinks } from '@samokat/apollo-persisted-queries-map'
import { ApolloProvider, ApolloClient, from } from '@apollo/client'
import { VFC } from 'react'

import { operationsToHashMap } from 'src/__generated__/operationsMap'

const client = new ApolloClient({
  link: from([...getApolloLinks(operationsToHashMap)])
})

export const App: VFC = () => (
  <ApolloProvider client={client}>
    {/*...*/}
  </ApolloProvider>
)