🌟 Enterprise Feature 🌟 This feature is bundled with GraphQL-Enterprise.

Schema Setup

To prepare the schema to serve cached responses, you have to add GraphQL::Enterprise::ObjectCache and implement a few hooks.

Add the Cache

In your schema, add use GraphQL::Enterprise::ObjectCache, redis: ...:

class MySchema < GraphQL::Schema
  use GraphQL::Enterprise::ObjectCache, redis: CACHE_REDIS

See the Redis guide for details about configuring cache storage.

Additionally, it accepts some options for customizing how introspection is cached, see Caching Introspection

Context Fingerprint

Additionally, you should implement def self.private_context_fingerprint_for(context) to return a string identifying the private scope of the given context. This method will be called whenever a query includes a public: false type or field. For example:

class MySchema < GraphQL::Schema
  # ...
  def self.private_context_fingerprint_for(context)
    viewer = context[:viewer]
    if viewer.nil?
      # This should never happen, but just in case:
      raise("Invariant: No viewer in context! Can't create a private context fingerprint" )

    # include permissions in the fingerprint so that if the viewer's permissions change, the cache will be invalidated
    permission_fingerprint = viewer.team_memberships.map { |tm| "#{tm.team_id}/#{tm.permission}" }.join(":")


Whenever queries including public: false are cached, the private context fingerprint will be part of the cache key, preventing responses from being shared between different viewers.

The returned String should reflect any aspects of context that, if changed, should invalidate the cache. For example, if a user’s permission level or team memberships change, then any previously-cached responses should be ignored.

Object Fingerprint

In order to determine whether cached results should be returned or invalidated, GraphQL needs a way to determine the “version” of each object in the query. It uses Schema.object_fingerprint_for(object) to do this. By default, it checks .cache_key_with_version (implemented by Rails), then .to_param, then it returns nil. Returning nil tells the cache not to use the cache at all. To customize this behavior, you can implement def self.object_fingerprint_for(object) in your schema:

class MySchema < GraphQL::Schema
  # ...

  # For example, if you defined `.custom_cache_key` and `.uncacheable?`
  # on objects in your application:
  def self.object_fingerprint_for(object)
    if object.respond_to?(:custom_cache_key)
    elsif object.respond_to?(:uncacheable?) && object.uncacheable?
      nil # don't cache queries containing this object

The returned strings are used as cache keys in the database – whenever they change, stale data is left to be cleaned up by Redis.

Object Identification

ObjectCache depends on object identification hooks used elsewhere in GraphQL-Ruby:

After your schema is setup, you can configure caching on your types and fields.

Schema Fingerprint

ObjectCache will also call .fingerprint on your Schema class. You can implement this method to return a new string if you make breaking changes to your schema, for example:

class MySchema < GraphQL::Schema
  def self.fingerprint
    "v2" # increment this if there are breaking changes to the schema

By returning a new MySchema.fingerprint, all previously-cached results will be expired.