Interpreter

GraphQL-Ruby 1.9.0 includes a new runtime module which you may use for your schema. Eventually, it will become the default.

It’s called GraphQL::Execution::Interpreter and you can hook it up with use ... in your schema class:

class MySchema < GraphQL::Schema
  use GraphQL::Execution::Interpreter
  # Also required in GraphQL-Ruby 1.10+:
  use GraphQL::Analysis::AST
end

Read on to learn more!

Rationale

The new runtime was added to address a few specific concerns:

Installation

You can opt in to the interpreter in your schema class:

class MySchema < GraphQL::Schema
  use GraphQL::Execution::Interpreter
  # Also required in GraphQL-Ruby 1.10+:
  use GraphQL::Analysis::AST
end

Some Relay configurations must be updated too. For example:

- field :node, field: GraphQL::Relay::Node.field
+ add_field(GraphQL::Types::Relay::NodeField)

(Alternatively, consider implementing Query.node in your own app, using NodeField as inspiration.)

Compatibility

The new runtime works with class-based schemas only. Several features are no longer supported:

Maybe this section should have been called incompatibility 🤔.

Extending the Runtime

See Directives.

Analyzers

GraphQL-Ruby has “analyzers” that run before execution and may reject a query. With the interpreter, you can use AST Analyzers to get better performance.

To make the migration, convert your previous analyzers to extend GraphQL::Analysis::AST::Analyzer as described in the guide, then add to your schema:

use GraphQL::Analysis::AST

When you use both Interpreter and Analysis::AST, GraphQL-Ruby will skip the slow process of building irep_nodes.

All analyzers must be migrated at once; running some legacy analyzers and some AST analyzers is not supported.

In GraphQL-Ruby 1.9, you can migrate to Interpreter before migrating to Analysis::AST. In that case, the irep_node tree will still be constructed and used for analysis, even though it will not be used for execution.

In GraphQL-Ruby 1.10+, Interpreter requires Analysis::AST and will not work without it. (Soon, these will be the default runtime modules.)

Implementation Notes

Instead of a tree of irep_nodes, the interpreter consumes the AST directly. This removes a complicated concept from GraphQL-Ruby (irep_nodes) and simplifies the query lifecycle. The main difference relates to how fragment spreads are resolved. In the previous runtime, the possible combinations of fields for a given object were calculated ahead of time, then some of those combinations were used during runtime, but many of them may not have been. In the new runtime, no precalculation is made; instead each object is checked against each fragment at runtime.

Instead of creating a GraphQL::Query::Context::FieldResolutionContext for every field in the response, the interpreter uses long-lived, mutable objects for execution bookkeeping. This is more complicated to manage, since the changes to those objects can be hard to predict, but it’s worth it for the performance gain. When needed, those bookkeeping objects can be “forked”, so that two parts of an operation can be resolved independently.

Instead of calling .to_graphql internally to convert class-based definitions to .define-based definitions, the interpreter operates on class-based definitions directly. This simplifies the workflow for creating custom configurations and using them at runtime.