# Technical Documentation: rmosolgo/graphql-ruby

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/rmosolgo/graphql-ruby)

### Overview (`readme.md`)

# graphql <img src="https://cloud.githubusercontent.com/assets/2231765/9094460/cb43861e-3b66-11e5-9fbf-71066ff3ab13.png" height="40" alt="graphql-ruby"/>

[](https://github.com/rmosolgo/graphql-ruby/actions/workflows/ci.yaml)
[](https://rubygems.org/gems/graphql)

A Ruby implementation of [GraphQL](https://graphql.org/).

- [Website](https://graphql-ruby.org/)
- [API Documentation](https://www.rubydoc.info/github/rmosolgo/graphql-ruby)
- [Newsletter](https://buttondown.email/graphql-ruby)

## Installation

Install from RubyGems by adding it to your `Gemfile`, then bundling.

```ruby
# Gemfile
gem 'graphql'
```

```
$ bundle install
```

## Getting Started

```
$ rails generate graphql:install
```

After this, you may need to run `bundle install` again, as by default graphiql-rails is added on installation.

Or, see ["Getting Started"](https://graphql-ruby.org/development/getting_started.html).

## Upgrade

I also sell [GraphQL::Pro](https://graphql.pro) which provides several features on top of the GraphQL runtime, including:

- [Persisted queries](https://graphql-ruby.org/operation_store/overview)
- [API versioning](https://graphql-ruby.org/changesets/overview)
- [Streaming payloads](https://graphql-ruby.org/defer/overview)
- [Server-side caching](https://graphql-ruby.org/object_cache/overview)
- [Rate limiters](https://graphql-ruby.org/limiters/overview)
- Subscriptions backends for [Pusher](https://graphql-ruby.org/subscriptions/pusher_implementation) and [Ably](https://graphql-ruby.org/subscriptions/ably_implementation)
- Authorization plugins for [Pundit](https://graphql-ruby.org/authorization/pundit_integration) and  [CanCan](https://graphql-ruby.org/authorization/can_can_integration)

Besides that, Pro customers get email support and an opportunity to support graphql-ruby's development!

## Goals

- Implement the GraphQL spec & support a Relay front end
- Provide idiomatic, plain-Ruby API with similarities to reference implementation where possible
- Support Ruby on Rails and Relay

## Getting Involved

- __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9).
- __Report bugs__ by posting a description, full stack trace, and all relevant code in a  [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues).
- __Start hacking__ with the [Development guide](https://graphql-ruby.org/development).

---

### Overview (`javascript_client/readme.md`)

Find the `graphql-ruby-client` docs on the [GraphQL-Ruby website](https://graphql-ruby.org/javascript_client/overview).

## License

`graphql-ruby-client` is available under the LGPLv3 license;
[graphql-pro](https://graphql.pro) customers are granted a special commercial license.

## Development

- With GraphQL-Ruby:
  - Install the dependencies with `rake js:install`
  - Run the tests with `rake js:test`
- Stand-alone:
  - Install dependencies `yarn install`
  - Run the tests `yarn run test`
- Run the TypeScript compiler: `yarn tsc -w`
- Install for local development with `npm link .`

---

### Guides Authorization Overview (`guides/authorization/overview.md`)

# Overview

Here's a conceptual approach to GraphQL authorization, followed by an introduction to the built-in authorization framework. Each part of the framework is described in detail in its own guide.

## Authorization: GraphQL vs REST

In a REST API, the common authorization pattern is fairly simple. Before performing the requested action, the server asserts that the current client has the required permissions for that action. For example:

```ruby
class PostsController < ApiController
  def create
    # First, check the client's permission level:
    if current_user.can?(:create_posts)
      # If the user is permitted, then perform the action:
      post = Post.create(params)
      render json: post
    else
      # Otherwise, return an error:
      render nothing: true, status: 403
    end
  end
end
```

However, this request-by-request mindset doesn't map well to GraphQL because there's only one controller and the requests that come to it may be _very_ different. To illustrate the problem:

```ruby
class GraphqlController < ApplicationController
  def execute
    # What permission is required for `query_str`?
    # It depends on the string! So, you can't generalize at this level.
    if current_user.can?(:"???")
      MySchema.execute(query_str, context: ctx, variables: variables)
    end
  end
end
```

So, what new mindset will work with a GraphQL API?

For __mutations__, remember that each mutation is like an API request in itself. For example, `Posts#create` above would map to the `createPost(...)` mutation in GraphQL. So, each mutation should be authorized in its own right.

For __queries__, you can think of each individual _object_ like a `GET` request to a REST API. So, each object should be authorized for reading in its own right.

By applying this mindset, each part of the GraphQL query will be properly authorized before it is executed. Also, since the different units of code are each authorized on their own, you can be sure that each incoming query will be properly authorized, even if it's a brand new query that the server has never seen before.

## What About Authentication?

As a reminder:

- _Authentication_ is the process of determining what user is making the current request, for example, accepting a username and password, or finding a `User` in the database from `session[:current_user_id]`.
- _Authorization_ is the process of verifying that the current user has permission to do something (or see something), for example, checking `admin?` status or looking up permission groups from the database.

In general, authentication is _not_ addressed in GraphQL at all. Instead, your controller should get the current user based on the HTTP request (eg, an HTTP header or a cookie) and provide that information to the GraphQL query. For example:

```ruby
class GraphqlController < ApplicationController
  def execute
    # Somehow get the current `User` from this HTTP request.
    current_user = get_logged_in_user(request)
    # Provide the current user in `context` for use during the query
    context = { current_user: current_user }
    MySchema.execute(query_str, context: context, ...)
  end
end
```

After your HTTP handler has loaded the current user, you can access it via `context[:current_user]` in your GraphQL code.

## Authorization in Your Business Logic

Before introducing GraphQL-specific authorization, consider the advantages of application-level authorization. (See the [GraphQL.org post](https://graphql.org/learn/authorization/) on the same topic.) For example, here's authorization mixed into the GraphQL API layer:

```ruby
field :posts, [Types::Post], null: false

def posts
  # Perform an auth check in the GraphQL field code:
  if context[:current_user].admin?
    Post.all
  else
    Post.published
  end
end
```

The downside of this is that, when `Types::Post` is queried in other contexts, the same authorization check may not be applied. Additionally, since the authorization code is coupled with the GraphQL API, the only way to test it is via GraphQL queries, which adds some complexity to tests.

Alternatively, you could move the authorization to your business logic, the `Post` class:

```ruby
class Post < ActiveRecord::Base
  # Return the list of posts which `user` may see
  def self.posts_for(user)
    if user.admin?
      self.all
    else
      self.published
    end
  end
end
```

Then, use this application method in your GraphQL code:

```ruby
field :posts, [Types::Post], null: false

def posts
  # Fetch the posts this user can see:
  Post.posts_for(context[:current_user])
end
```

In this case, `Post.posts_for(user)` could be tested _independently_ from GraphQL. Then, you have less to worry about in your GraphQL tests. As a bonus, you can use `Post.posts_for(user)` in _other_ parts of the app, too, such as the web UI or REST API.

## GraphQL-Ruby's Authorization Framework

Despite the advantages of authorization at the application layer, as described above, there might be some reasons to authorize in the API layer:

- Have an extra assurance that your API layer is secure
- Authorize the API request _before_ running it (see "visibility" below)
- Integrate with code that doesn't have authorization built-in

To accomplish these, you can use GraphQL-Ruby's authorization framework. The framework has three levels, each of which is described in its own guide:

- [Visibility](/authorization/visibility) hides parts of the GraphQL schema from users who don't have full permission.
- [Authorization](/authorization/authorization) checks application objects during execution to be sure the user has permission to access them.

Also, [GraphQL::Pro](https://graphql.pro) has integrations for [CanCan](/authorization/can_can_integration) and [Pundit](/authorization/pundit_integration).

---

### Guides Changesets Overview (`guides/changesets/overview.md`)

# API Versioning for GraphQL-Ruby

Out-of-the-box, GraphQL is [versionless by design](https://graphql.org/learn/schema-design/). GraphQL's openness to extension paves the way for continuously expanding and improving an API. You can _always_ add new fields, new arguments, and new types to implement new features and customize existing behavior.

However, sometimes a business case may call for a different versioning scheme. [GraphQL-Enterprise](https://graphql.pro/enterprise)'s "Changesets" enable schemas to release _any_ change -- even breaking changes -- to clients, depending on what version of the schema they're using. With Changesets, you can redefine existing fields, define new types using old names, add or remove enum values -- anything, really -- while maintaining compatibility for existing clients.

## Why Changesets?

Changesets are a _complementary_ evolution technique to continuous additions. In general, additive changes (new fields, new arguments, new types) are best added right to the existing schema. But if you need to _remove_ something from the schema or redefine existing parts of the schema in non-backwards-compatible ways, Changesets provide a handy way of doing so.

For example, if you add a values to an Enum, you can just add it to the existing schema:

```diff
  class Types::RecipeTag < Types::BaseEnum
    value "LOW_FAT"
    value "LOW_CARB"
+   value "VEGAN"
+   value "KETO"
+   value "GRAPEFRUIT_DIET"
  end
```

However, if you want to change the schema in ways that would _break_ previous queries, you can do that with a Changeset:

```ruby
class Types::RecipeTag < Types::BaseEnum
  # Turns out this makes you sick:
  value "GRAPEFRUIT_DIET", removed_in: Changesets::RemoveLegacyDiets
end
```

Then, only clients requesting API versions _before_  this changeset would be able to use `GRAPEFRUIT_DIET`; clients requesting newer versions could not send it as input and would not receive it in responses.

(Changesets _also_ support additive changes, if you prefer to make them that way.)

## Getting Started

To start using Changesets, read on:

- [Installing Changesets](/changesets/installation)
- [Writing Changesets](/changesets/definition)
- [Releasing Changesets](/changesets/releases)

---

### Guides Dataloader Overview (`guides/dataloader/overview.md`)

# Overview

 [GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) provides efficient, batched access to external services, backed by Ruby's `Fiber` concurrency primitive. It has a per-query result cache and [AsyncDataloader](/dataloader/async_dataloader) supports truly parallel execution out-of-the-box.

`GraphQL::Dataloader` is inspired by [`@bessey`'s proof-of-concept](https://github.com/bessey/graphql-fiber-test/tree/no-gem-changes) and [shopify/graphql-batch](https://github.com/shopify/graphql-batch).

## Batch Loading

`GraphQL::Dataloader` facilitates a two-stage approach to fetching data from external sources (like databases or APIs):

- First, GraphQL fields register their data requirements (eg, object IDs or query parameters)
- Then, after as many requirements have been gathered as possible, `GraphQL::Dataloader` initiates _actual_ fetches to external services

That cycle is repeated during execution: data requirements are gathered until no further GraphQL fields can be executed, then `GraphQL::Dataloader` triggers external calls based on those requirements and GraphQL execution resumes.

## Fibers

`GraphQL::Dataloader` uses Ruby's `Fiber`, a lightweight concurrency primitive which supports application-level scheduling _within_ a `Thread`. By using `Fiber`, `GraphQL::Dataloader` can pause GraphQL execution when data is requested, then resume execution after the data is fetched.

At a high level, `GraphQL::Dataloader`'s usage of `Fiber` looks like this:

- GraphQL execution is run inside a Fiber.
- When that Fiber returns, if the Fiber was paused to wait for data, then GraphQL execution resumes with the _next_ (sibling) GraphQL field inside a new Fiber.
- That cycle continues until no further sibling fields are available and all known Fibers are paused.
- `GraphQL::Dataloader` takes the first paused Fiber and resumes it, causing the `GraphQL::Dataloader::Source` to execute its `#fetch(...)` call. That Fiber continues execution as far as it can.
- Likewise, paused Fibers are resumed, causing GraphQL execution to continue, until all paused Fibers are evaluated completely.

Whenever `GraphQL::Dataloader` creates a new `Fiber`, it copies each pair from `Thread.current[...]` and reassigns them inside the new `Fiber`.

`AsyncDataloader`, built on top of the [`async` gem](https://github.com/socketry/async), supports parallel I/O operations (like network and database communication) via Ruby's non-blocking `Fiber.schedule` API. [Learn more →](/dataloader/async_dataloader).

## Getting Started

To install [GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader), add it to your schema with `use ...`, for example:

```ruby
class MySchema < GraphQL::Schema
  # ...
  use GraphQL::Dataloader
end
```

Then, inside your schema, you can request batch-loaded objects by their lookup key with `dataloader.with(...).load(...)`:

```ruby
field :user, Types::User do
  argument :handle, String
end

def user(handle:)
  dataloader.with(Sources::UserByHandle).load(handle)
end
```

Or, load several objects by passing an array of lookup keys to `.load_all(...)`:

```ruby
field :is_following, Boolean, null: false do
  argument :follower_handle, String
  argument :followed_handle, String
end

def is_following(follower_handle:, followed_handle:)
  follower, followed = dataloader
    .with(Sources::UserByHandle)
    .load_all([follower_handle, followed_handle])

  followed && follower && follower.follows?(followed)
end
```

To prepare requests from several sources, use `.request(...)`, then call `.load` after all requests are registered:

```ruby
class AddToList < GraphQL::Schema::Mutation
  argument :handle, String
  argument :list, String, as: :list_name

  field :list, Types::UserList

  def resolve(handle:, list_name:)
    # first, register the requests:
    user_request = dataloader.with(Sources::UserByHandle).request(handle)
    list_request = dataloader.with(Sources::ListByName, context[:viewer]).request(list_name)
    # then, use `.load` to wait for the external call and return the object:
    user = user_request.load
    list = list_request.load
    # Now, all objects are ready.
    list.add_user!(user)
    { list: list }
  end
end
```

### `loads:` and `object_from_id`

`dataloader` is also available as `context.dataloader`, so you can use it to implement `MySchema.object_from_id`. For example:

```ruby
class MySchema < GraphQL::Schema
  def self.object_from_id(id, ctx)
    model_class, database_id = IdDecoder.decode(id)
    ctx.dataloader.with(Sources::RecordById, model_class).load(database_id)
  end
end
```

Then, any arguments with `loads:` will use that method to fetch objects. For example:

```ruby
class FollowUser < GraphQL::Schema::Mutation
  argument :follow_id, ID, loads: Types::User

  field :followed, Types::User

  def resolve(follow:)
    # `follow` was fetched using the Schema's `object_from_id` hook
    context[:viewer].follow!(follow)
    { followed: follow }
  end
end
```

## Data Sources

To implement batch-loading data sources, see the [Sources guide](/dataloader/sources).

## Parallelism

You can run I/O operations in parallel with GraphQL::Dataloader. There are two approaches:

- `AsyncDataloader` uses the `async` gem to automatically background I/O from `Dataloader::Source#fetch` calls. [Read More](/dataloader/async_dataloader)
- You can manually call `dataloader.yield` after starting work in the background. [Read More](/dataloader/parallelism)

---

### Guides Defer Overview (`guides/defer/overview.md`)

# Overview

`@defer` is a [directive](/type_definitions/directives) for streaming GraphQL responses from the server to the client.

By streaming the response, the server can send the most critical (or most available) data _first_, following up with secondary data shortly afterward.

`@defer` was first described by [Lee Byron at React Europe 2015](https://youtu.be/ViXL0YQnioU?t=768) and got experimental support in [Apollo in 2018](https://blog.apollographql.com/introducing-defer-in-apollo-server-f6797c4e9d6e).

`@stream` is like `@defer`, but it returns list items one at a time. Find details in the [Stream guide](/defer/stream).

## Example

GraphQL queries can be large and complex, requiring lots of computation or dependencies on slow external services.

In this example, the local server maintains an index of items ("decks"), but the item data ("cards") is actually hosted on a remote server. So, GraphQL queries must make remote calls in order to serve that data.

Without `@defer`, the whole query is blocked until the last field is done resolving:



But, we can add `@defer` to slow fields:

```diff
  deck {
    slots {
      quantity
-     card
+     card @defer {
        name
        price
      }
    }
  }
```

Then, the response will stream to the client bit by bit, so the page can load progressively:




This way, clients get a snappy feel from the app even while data is still loading.

View the full demo at https://github.com/rmosolgo/graphql_defer_example.

## Considerations

- `@defer` adds some overhead to the response, so only apply it judiciously.
- `@defer` is single-threaded. `@defer`ed fields are still evaluated in sequence, but in a chunk-by-chunk way.

## Next Steps

[Set up your server](/defer/setup) to support `@defer` or read about [client usage](/defer/usage) of it.

---

### Guides Errors Overview (`guides/errors/overview.md`)

# Errors in GraphQL

There are a _lot_ of different kinds of errors in GraphQL! In this guide, we'll discuss some of the main categories and learn when they apply.

## Validation Errors

Because GraphQL is strongly typed, it performs validation of all queries before executing them. If an incoming query is invalid, it isn't executed. Instead, a response is sent back with `"errors"`:

```ruby
{
  "errors" => [ ... ]
}
```

Each error has a message, line, column and path.

The validation rules are part of the GraphQL specification and built into GraphQL-Ruby, so there's not really a way to customize this behavior, except to pass `validate: false` when executing a query, which skips validation altogether.

You can configure your schema to stop validating after a certain number of errors by setting [Schema.validate_max_errors](rdoc-ref:GraphQL::Schema.validate_max_errors). Also, you can add a timeout to this step with [Schema.validate_timeout](rdoc-ref:GraphQL::Schema.validate_timeout).

## Analysis Errors

GraphQL-Ruby supports pre-execution analysis, which may return `"errors"` instead of running a query. You can find details in the [Analysis guide](/queries/ast_analysis).

## GraphQL Invariants

While GraphQL-Ruby is executing a query, some constraints must be satisfied. For example:

- Non-null fields may not return `nil`.
- Interface and union types must resolve objects to types that belong to that interface/union.

These constraints are part of the GraphQL specification, and when they are violated, it must be addressed somehow. Read more in [Type Errors](/errors/type_errors).

## Top-level `"errors"`

The GraphQL specification provides for a top-level `"errors"` key which may include information about errors during query execution. `"errors"` and `"data"` may _both_ be present in the case of a partial success.

In your own schema, you can add to the `"errors"` key by raising `GraphQL::ExecutionError` (or subclasses of it) in your code. Read more in the [Execution Errors guide](/errors/execution_errors).

## Handled Errors

A schema can be configured to handle certain errors during field execution with handlers that you give it, using `rescue_from`. Read more in the [Error Handling guide](/errors/error_handling).

## Unhandled Errors (Crashes)

When a `raise`d error is not `rescue`d, the GraphQL query crashes entirely and the surrounding code (like a Rails controller) must handle the exception.

For example, Rails will probably return a generic `500` page.

## Errors as Data

When you want end users (human beings) to read error messages, you can express errors _in the schema_, using normal GraphQL fields and types. In this approach, errors are strongly-typed data, queryable in the schema, like any other application data.

For more about this approach, see [Mutation Errors](/mutations/mutation_errors.html#errors-as-data)

---

### Guides Javascript Client Overview (`guides/javascript_client/overview.md`)

# Overview

There is a JavaScript client for GraphQL-Ruby, `graphql-ruby-client`.

You can install it from NPM or Yarn:

```sh
yarn add graphql-ruby-client
# Or:
npm install graphql-ruby-client
```

The source code is [in the graphql-ruby repository](https://github.com/rmosolgo/graphql-ruby/tree/master/javascript_client).

See detailed guides for more info about its features:

- [sync CLI](/javascript_client/sync) for use with [graphql-pro](https://graphql.pro)'s persisted query backend
- Subscription support:
  - [Apollo integration](/javascript_client/apollo_subscriptions)
  - [Relay integration](/javascript_client/relay_subscriptions)
  - [urql integration](/javascript_client/urql_subscriptions)
  - [GraphiQL integration](/javascript_client/graphiql_subscriptions)

---

### Guides Limiters Overview (`guides/limiters/overview.md`)

# Rate Limiters for GraphQL

`GraphQL::Enterprise` includes rate limiters built especially for GraphQL.

For REST APIs, rate limiters often count _requests_ and block clients when they exceed their limit over a certain period of time. However, this paradigm doesn't translate well to GraphQL because the cost of serving a request may vary dramatically depending on the GraphQL query contained in the request. Instead, `GraphQL::Enterprise` implements two other kinds of limiters:

- An __active operation limiter__ which allows clients to run a certain number of operations _at a time_. For example, if the limit is five concurrent operations, and a client sends six requests simultaneously, then only five of those incoming operations will be executed; the sixth will be returned with an error and it may be retried when one of the five others finishes.
- A __runtime limiter__ which limits the amount of processing time a client may consume during a given window. For example, a limit of 120 seconds per minute would allow two concurrent requests on average -- although in practice, it might be spiky: perhaps five concurrent, 20-second-long requests, followed by 40 seconds of no requests.

There's some overlap in these limiters; both of them constrain the amount of _time_ a client may force the server to spend in handling requests. The active operation limiter puts an upper bound on how _many_ processes a client may occupy while the runtime limiter puts a bound on total processing time (regardless of the number of concurrent operations at a given moment).

To get started, read on:

- [Configure Redis](/limiters/redis) for the limiters' backend
- [Active Operation Limiter](/limiters/active_operations)
- [Runtime Limiter](/limiters/runtime)

---

### Guides Object Cache Overview (`guides/object_cache/overview.md`)

# GraphQL ObjectCache

`GraphQL::Enterprise::ObjectCache` is an application-level cache for GraphQL-Ruby servers. It works by storing a [_cache fingerprint_ for each object](/object_cache/schema_setup#object-fingerprint) in a query, then serving a cached response as long as those fingerprints don't change. The cache can also be customized with [TTLs](/object_cache/caching#ttl).

## Why?

`ObjectCache` can greatly reduce GraphQL response times by serving cached responses when the underlying data for a query hasn't changed.

Usually, a GraphQL query alternates between data fetching and calling application logic:





But with `ObjectCache`, it checks the cache first, returning a cached response if possible:



This reduces latency for clients and reduces the load on your database and application server.

## How

Before running a query, `ObjectCache` creates a fingerprint for the query using [GraphQL::Query#fingerprint](rdoc-ref:GraphQL::Query#fingerprint) and [`Schema.context_fingerprint_for(ctx)`](/object_cache/schema_setup#context-fingerprint). Then, it checks the backend for a cached response which matches the fingerprint.

If a match is found, the `ObjectCache` fetches the objects previously visited by this query. Then, it compares the current fingerprint of each object ot the one in the cache and checks `.authorized?` for that object. If the fingerprints all match and all objects pass authorization checks, then the cached response returned. (Authorization checks can be [disabled](/object_cache/schema_setup#disabling-reauthorization).)

If there is no cached response or if the fingerprints don't match, then the incoming query is re-evaluated. While it's executed, `ObjectCache` gathers the IDs and fingerprints of each object it encounters. When the query is done, the result and the new object fingerprints are written to the cache.

## Setup

To get started with the object cache:

- [Prepare the schema](/object_cache/schema_setup)
- Set up a [Redis backend](/object_cache/redis) or [Memcached backend](/object_cache/memcached)
- [Configure types and fields for caching](/object_cache/caching)
- Check out the [runtime considerations](/object_cache/runtime_considerations)

---

### Guides Operation Store Overview (`guides/operation_store/overview.md`)

# Overview

`GraphQL::Pro::OperationStore` uses `Rack` and a storage backend ([ActiveRecord](/operation_store/active_record_backend) or [Redis](/operation_store/redis_backend)) to maintain a normalized, deduplicated database of _persisted queries_ for your GraphQL system.

In this guide, you'll find:

- [Description of persisted queries](#what-are-persisted-queries)
- [The rationale](#why-persisted-queries) behind them
- [How `OperationStore` works](#how-it-works), in brief

In other guides, you can read more about:

- [Getting Started](/operation_store/getting_started) installing `OperationStore` in your app
- [Workflow](/operation_store/client_workflow) and usage for client apps
- [Authentication](/operation_store/access_control) for the sync API
- [Server Management](/operation_store/server_management) after your system is running

Also, you can find a [demo app on GitHub](https://github.com/rmosolgo/graphql-pro-operation-store-example).

## What are Persisted Queries?

_Persisted queries_ are GraphQL queries (`query`, `mutation`, or `subscription`) that are saved on the server and invoked by clients by _reference_. In this arrangement, clients don't send GraphQL queries over the network. Instead, clients send:

- __Client name__, to identify the client who is making the request
- __Query alias__, to specify which stored operation to run
- __Query variables__, to provide values for the stored operation

Then, the server uses the identifier to fetch the full GraphQL document from the database.

Without persisted queries, clients send the whole document:

```ruby
# Before, without persisted queries
query_string = "query GetUserDetails($userId: ID!) { ... }"

MyGraphQLEndpoint.post({
  query: query_string,
  operationName: "GetUserDetails",
  variables: { userId: "100" },
})
```


But with persisted queries, the full document isn't sent because the server already has a copy of it:

```ruby
# After, with persisted queries:
MyGraphQLEndpoint.post({
  operationId: { "relay-app-v1/fc84dbba3623383fdc",
  #               client name / query alias (eg, @relayHash)
  variables: { userId: "100" },
})
```

## Why Persisted Queries?

Using persisted queries improves the _security_, _efficiency_ and _visibility_ of your GraphQL system.


### Security

Persisted queries improve security because you can reject arbitrary GraphQL queries, removing an attack vector from your system. The query database serves a whitelist, so you can be sure that no unexpected queries will hit your system.

For example, after all clients have migrated to persisted queries, you can reject arbitrary GraphQL in production:

```ruby
# app/controllers/graphql_controller.rb
if Rails.env.production? && params[:query].present?
  # Reject arbitrary GraphQL in production:
  render json: { errors: [{ message: "Raw GraphQL is not accepted" }]}
else
  # ...
end
```

### Efficiency

Persisted queries improve the _efficiency_ of your system by reducing HTTP traffic. Instead of repeatedly sending GraphQL over the wire, queries are fetched from the database, so your requests require less bandwidth.

For example, _before_ using persisted queries, the entire query is sent to the server:



But _after_ using persisted queries, only the query identification info is sent to the server:



### Visibility

Persisted queries improve _visibility_ because you can track GraphQL usage from a single location. `OperationStore` maintains an index of type, field and argument usage so that you can analyze your traffic.



## How it Works

`OperationStore` uses tables in your database to store normalized, deduplicated GraphQL strings. The database is immutable: new operations may be added, but operations are never modified or removed.

When clients [sync their operations](/operation_store/client_workflow), requests are [authenticated](/operation_store/access_control), then the incoming GraphQL is validated, normalized, and added to the database if needed. Also, the incoming client name is associated with all operations in the payload.

Then, at runtime, clients send an _operation ID_ to run a persisted query. It looks like this in `params`:

```ruby
params[:operationId] # => "relay-app-v1/810c97f6631001..."
```

`OperationStore` uses this to fetch the matching operation from the database. From there, the query is evaluated normally.

## Getting Started

See the [getting started guide](/operation_store/getting_started) to add `OperationStore` to your app.

---

### Guides Pagination Overview (`guides/pagination/overview.md`)

# Overview

GraphQL-Ruby ships with several implementations of Relay's "connection"-style pagination. You can familiarize yourself with connections in [Connection Concepts](/pagination/connection_concepts) and see how to use them in [Using Connections](/pagination/using_connections). It also supports custom connection implementations and type definitions, which you can explore in the [Custom Connections](/pagination/custom_connections) guide.

GraphQL has its own [great pagination docs](https://graphql.org/learn/pagination/) for further reading.

---

### Guides Subscriptions Overview (`guides/subscriptions/overview.md`)

# Overview

_Subscriptions_ allow GraphQL clients to observe specific events and receive updates from the server when those events occur. This supports live updates, such as websocket pushes. Subscriptions introduce several new concepts:

- The __Subscription type__ is the entry point for subscription queries
- __Subscription classes__ are resolvers for handing initial subscription requests and subsequent updates
- __Triggers__ begin the update process
- The __Implementation__ provides application-specific methods for executing & delivering updates.
- __Broadcasts__ can send the same GraphQL result to any number of subscribers.

## Subscription Type

`subscription` is an entry point to your GraphQL schema, like `query` or `mutation`. It is defined by your `SubscriptionType`, a root-level `GraphQL::Schema::Object`.

Read more in the [Subscription Type guide](/subscriptions/subscription_type).

## Subscription Classes

[GraphQL::Schema::Subscription](rdoc-ref:GraphQL::Schema::Subscription) is a resolver class with subscription-specific behaviors. Each subscription field should be implemented by a subscription class.

Read more in the [Subscription Classes guide](/subscriptions/subscription_classes)

## Triggers

After an event occurs in our application, _triggers_ begin the update process by sending a name and payload to GraphQL.

Read more in the [Triggers guide](/subscriptions/triggers).

## Implementation

Besides the GraphQL component, your application must provide some subscription-related plumbing, for example:

- __state management__: How does your application keep track of who is subscribed to what?
- __transport__: How does your application deliver payloads to clients?
- __queueing__: How does your application distribute the work of re-running subscription queries?

Read more in the [Implementation guide](/subscriptions/implementation) or check out the [ActionCable implementation](/subscriptions/action_cable_implementation), [Pusher implementation](/subscriptions/pusher_implementation) or [Ably implementation](/subscriptions/ably_implementation).

## Broadcasts

By default, the subscription implementations listed above handle each subscription in total isolation. However, this behavior can be optimized by setting up broadcasts. Read more in the [Broadcast guide](/subscriptions/broadcast).

## Multi-Tenant

See the [Multi-tenant guide](/subscriptions/multi_tenant) for supporting multi-tenancy in GraphQL subscriptions.

---

### Guides Testing Overview (`guides/testing/overview.md`)

# Overview

So, you've spiked a GraphQL API, and now you're ready to tighten things up and add some proper tests. These guides will help you think about how to ensure stability and compatibility for your GraphQL system.

- [Structure testing](/testing/schema_structure) verifies that schema changes are backwards-compatible. This way, you don't break existing clients.
- [Integration testing](/testing/integration_tests) exercises the various behaviors of the GraphQL system, making sure that it returns the right data to the right clients.
- [Testing helpers](/testing/helpers) for running GraphQL fields without writing a whole query

---

### Guide Metadata.Yml (`docs/guide_metadata.yml`)

version: 1

# Navigation order migrated from the Jekyll `index` frontmatter.
order:
  authorization:
    - overview
    - visibility
    - authorization
    - can_can_integration
    - pundit_integration
    - scoping
  changesets:
    - overview
    - installation
    - definition
    - releases
  dataloader:
    - overview
    - sources
    - dataloader
    - adopting
    - testing
    - async_dataloader
    - parallelism
  defer:
    - overview
    - setup
    - usage
    - stream
    - graphiql
  development:
    - getting_started
    - development
    - faq
    - related_projects
    - documentation_maintenance
  errors:
    - overview
    - execution_errors
    - error_handling
    - type_errors
  execution:
    - next
    - migration
  fields:
    - introduction
    - arguments
    - resolvers
    - validation
    - limits
  javascript_client:
    - overview
    - sync
    - apollo_subscriptions
    - relay_subscriptions
    - urql_subscriptions
    - graphiql_subscriptions
  language_tools:
    - visitor
    - c_parser
  limiters:
    - overview
    - redis
    - active_operations
    - runtime
    - deployment
  mutations:
    - mutation_root
    - mutation_classes
    - mutation_errors
    - mutation_authorization
  object_cache:
    - overview
    - schema_setup
    - caching
    - memcached
    - redis
    - runtime_considerations
  operation_store:
    - overview
    - getting_started
    - active_record_backend
    - redis_backend
    - client_workflow
    - server_management
    - access_control
  pagination:
    - overview
    - connection_concepts
    - using_connections
    - custom_connections
    - cursors
    - stable_relation_connections
  pro:
    - home
    - installation
    - dashboard
    - encoders
    - privacy
  queries:
    - executing_queries
    - ast_analysis
    - phases_of_execution
    - complexity_and_depth
    - timeout
    - multiplex
    - lookahead
    - tracing
    - backtrace_annotations
    - logging
    - response_extensions
  relay:
    - range_add
  schema:
    - definition
    - root_types
    - generators
    - introspection
    - lazy_execution
    - sdl
    - dynamic_types
    - object_identification
  subscriptions:
    - overview
    - subscription_classes
    - subscription_type
    - triggers
    - broadcast
    - implementation
    - action_cable_implementation
    - pusher_implementation
    - ably_implementation
    - multi_tenant
  testing:
    - overview
    - schema_structure
    - integration_tests
    - helpers
    - profiling
  type_definitions:
    - objects
    - scalars
    - enums
    - input_objects
    - interfaces
    - unions
    - lists
    - non_nulls
    - extensions
    - directives
    - field_extensions

# Product callouts migrated from the Jekyll `pro` and `enterprise` frontmatter.
products:
  pro:
    - authorization/can_can_integration
    - authorization/pundit_integration
    - defer/*
    - operation_store/*
    - pagination/stable_relation_connections
    - pro/dashboard
    - pro/encoders
    - pro/home
    - pro/installation
    - subscriptions/ably_implementation
    - subscriptions/pusher_implementation
  enterprise:
    - changesets/*
    - limiters/*
    - object_cache/*

---

### Redirects.Yml (`docs/redirects.yml`)

# Guide URLs are generated from the source tree so every existing guide has a
# concrete redirect. Add explicit entries for pages that move into an API ref.
guide_source: guides
redirects:
  - old_path: /docs/maintenance
    destination:
      kind: page
      value: guides/development/documentation_maintenance.md
  - old_path: /guides
    destination:
      kind: page
      value: guides/development/getting_started.md
  - old_path: /queries/executing_queries
    destination:
      kind: rdoc_ref
      value: GraphQL::Schema.execute
  - old_path: /getting_started
    destination:
      kind: page
      value: guides/development/getting_started.md

---

### Guides Authorization Authorization (`guides/authorization/authorization.md`)

# Authorization

While a query is running, you can check each object to see whether the current user is authorized to interact with that object. If the user is _not_ authorized, you can handle the case with an error.

## Adding Authorization Checks

Schema members have `authorized?` methods which will be called during execution:

- Type classes have `.authorized?(object, context)` class methods
- Fields have `#authorized?(object, args, context)` instance methods
- Arguments have `#authorized?(object, arg_value, context)` instance methods
- Mutations and Resolvers have `.authorized?(object, context)` class methods and `#authorized?(args)` instance methods
- Enum values have `#authorized?(context)` instance methods

These methods are called with:

- `object`: the object from your application which was returned from a field
- `args`/`arg_value`: The arguments for a field, or the value of an argument
- `context`: the query context, based on the hash passed as `context:`

#### Object Authorization

When you implement this method to return `false`, the query will be halted, for example:

```ruby
class Types::Friendship < Types::BaseObject
  # You can only see the details on a `Friendship`
  # if you're one of the people involved in it.
  def self.authorized?(object, context)
    super && (object.to_friend == context[:viewer] || object.from_friend == context[:viewer])
  end
end
```

(Always call `super` to get the default checks, too.)

Now, whenever an object of type `Friendship` is going to be returned to the client, it will first go through the `.authorized?` method. If that method returns false, the field will get `nil` instead of the original object, and you may handle that case with an error (see below).

#### Field Authorization

Field `#authorized?` methods are called before resolving a field, for example:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  # Pass `field ..., require_admin: true` to reject non-admin users from a given field
  def initialize(*args, require_admin: false, **kwargs, &block)
    @require_admin = require_admin
    super(*args, **kwargs, &block)
  end

  def authorized?(obj, args, ctx)
    # if `require_admin:` was given, then require the current user to be an admin
    super && (@require_admin ? ctx[:viewer]&.admin? : true)
  end
end
```

For this to work, the base field class must be [configured with other GraphQL types](/type_definitions/extensions.html#customizing-fields).

#### Argument Authorization

Argument `#authorized?` hooks are called before resolving the field that the argument belongs to. For example:

```ruby
class Types::BaseArgument < GraphQL::Schema::Argument
  def initialize(*args, require_logged_in: false, **kwargs, &block)
    @require_logged_in = require_logged_in
    super(*args, **kwargs, &block)
  end

  def authorized?(obj, arg_value, ctx)
    super && if @require_logged_in
      ctx[:viewer].present?
    else
      true
    end
  end
end
```

For this to work, the base argument class must be [configured with other GraphQL types](/type_definitions/extensions.html#customizing-arguments).

## Mutation Authorization

See [Mutation Authorization](/mutations/mutation_authorization.html#can-this-user-perform-this-action) in the Mutation Guides.

## Enum Value Authorization

[GraphQL::Schema::EnumValue#authorized?](rdoc-ref:GraphQL::Schema::EnumValue#authorized?) is called when client input is received and when the schema returns values to the client.

For authorizing input, if a value's `#authorized?` method returns false, then a [GraphQL::UnauthorizedEnumValueError](rdoc-ref:GraphQL::UnauthorizedEnumValueError) is raised. It passed to your schema's `.unauthorized_object` hook, where you can handle it another way if you want.

For authorizing return values, if an outgoing value's `#authorized?` method returns false, then a [GraphQL::Schema::Enum::UnresolvedValueError](rdoc-ref:GraphQL::Schema::Enum::UnresolvedValueError) is raised, which crashes the query. In this case, you should modify your field or resolver to _not_ return this value to an unauthorized viewer. (In this case, the error isn't returned to the viewer because the viewer can't do anything about it -- it's a developer-facing issue instead.)

## Handling Unauthorized Objects

By default, GraphQL-Ruby silently replaces unauthorized objects with `nil`, as if they didn't exist. You can customize this behavior by implementing [Schema.unauthorized_object](rdoc-ref:GraphQL::Schema.unauthorized_object) in your schema class, for example:

```ruby
class MySchema < GraphQL::Schema
  # Override this hook to handle cases when `authorized?` returns false for an object:
  def self.unauthorized_object(error)
    # Add a top-level error to the response instead of returning nil:
    raise GraphQL::ExecutionError, "An object of type #{error.type.graphql_name} was hidden due to permissions"
  end
end
```

Now, the custom hook will be called instead of the default one.

If `.unauthorized_object` returns a non-`nil` object (and doesn't `raise` an error), then that object will be used in place of the unauthorized object.

A similar hook is available for unauthorized fields:

```ruby
class MySchema < GraphQL::Schema
  # Override this hook to handle cases when `authorized?` returns false for a field:
  def self.unauthorized_field(error)
    # Add a top-level error to the response instead of returning nil:
    raise GraphQL::ExecutionError, "The field #{error.field.graphql_name} on an object of type #{error.type.graphql_name} was hidden due to permissions"
  end
end
```

---

### Guides Authorization Can Can Integration (`guides/authorization/can_can_integration.md`)

# CanCan Integration

[GraphQL::Pro](https://graphql.pro) includes an integration for powering GraphQL authorization with [CanCan](https://github.com/CanCanCommunity/cancancan).

__Why bother?__ You _could_ put your authorization code in your GraphQL types themselves, but writing a separate authorization layer gives you a few advantages:

- Since the authorization code isn't embedded in GraphQL, you can use the same logic in non-GraphQL (or legacy) parts of the app.
- The authorization logic can be tested in isolation, so your end-to-end GraphQL tests don't have to cover as many possibilities.

## Getting Started

__NOTE__: Requires the latest gems, so make sure your `Gemfile` has:

```ruby
# For CanCanIntegration:
gem "graphql-pro", ">=1.7.11"
# For list scoping:
gem "graphql", ">=1.8.7"
```

Then, `bundle install`.

Whenever you run queries, include `:current_user` in the context:

```ruby
context = {
  current_user: current_user,
  # ...
}
MySchema.execute(..., context: context)
```

### Rails Generator

If your schema files follow the same convention as `rails generate graphql:install`, then you can install the CanCan integration with a Rails generator:

```bash
$ rails generate graphql:cancan:install
```

This will insert all the necessary `include ...`s described below. Alternatively, check the docs below to mix in `CanCanIntegration`'s modules.

## Authorizing Objects

For each object type, you can assign a required action for Ruby objects of that type. To get started, include the `ObjectIntegration` in your base object class:

```ruby
# app/graphql/types/base_object.rb
class Types::BaseObject < GraphQL::Schema::Object
  # Add the CanCan integration:
  include GraphQL::Pro::CanCanIntegration::ObjectIntegration
  # By default, require `can :read, ...`
  can_can_action(:read)
  # Or, to require no permissions by default:
  # can_can_action(nil)
end
```

Now, anyone fetching an object will need `can :read, ...` for that object.

CanCan configurations are inherited, and can be overridden in subclasses. For example, to allow _all_ viewers to see the `Query` root type:

```ruby
class Types::Query < Types::BaseObject
  # Allow anyone to see the query root
  can_can_action nil
end
```

### Bypassing CanCan

`can_can_action(nil)` will override any inherited configuration and skip CanCan checks for an object, field, argument or mutation.

### Handling Unauthorized Objects

When any CanCan check returns `false`, the unauthorized object is passed to [Schema.unauthorized_object](rdoc-ref:GraphQL::Schema.unauthorized_object), as described in [Handling unauthorized objects](/authorization/authorization#handling-unauthorized-objects).

## Scopes

#### ActiveRecord::Relation

The CanCan integration adds [CanCan's `.accessible_by`](https://github.com/cancancommunity/cancancan/wiki/Fetching-Records) to GraphQL-Ruby's [list scoping](/authorization/scoping)

To scope lists of interface or union type, include the integration in your base union class and base interface module _and_ set a base `can_can_action`, if desired:

```ruby
class BaseUnion < GraphQL::Schema::Union
  include GraphQL::Pro::CanCanIntegration::UnionIntegration
  # To provide a default action for scoping lists:
  can_can_action :read
end

module BaseInterface
  include GraphQL::Schema::Interface
  include GraphQL::Pro::CanCanIntegration::InterfaceIntegration
  # To provide a default action for scoping lists:
  can_can_action :read
end
```

#### Array

For Arrays, the CanCan integration will use `.select { ... }` to filter items using the `can_can_action` from the lists's type.

#### Bypassing scopes

To allow an unscoped relation to be returned from a field, disable scoping with `scope: false`, for example:

```ruby
# Allow anyone to browse the job postings
field :job_postings, [Types::JobPosting], null: false,
  scope: false
```

## Authorizing Fields

You can also require certain checks on a field-by-field basis. First, include the integration in your base field class:

```ruby
# app/graphql/types/base_field.rb
class Types::BaseField < GraphQL::Schema::Field
  # Add the CanCan integration:
  include GraphQL::Pro::CanCanIntegration::FieldIntegration
  # By default, don't require a role at field-level:
  can_can_action nil
end
```

If you haven't already done so, you should also hook up your base field class to your base object and base interface:

```ruby
# app/graphql/types/base_object.rb
class Types::BaseObject < GraphQL::Schema::Object
  field_class Types::BaseField
end
# app/graphql/types/base_interface.rb
module Types::BaseInterface
  # ...
  field_class Types::BaseField
end
# app/graphql/mutations/base_mutation.rb
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  field_class Types::BaseField
end
```

Then, you can add `can_can_action:` options to your fields:

```ruby
class Types::JobPosting < Types::BaseObject
  # Only allow `can :review_applications, JobPosting` users
  # to see who has applied
  field :applicants, [Types::User],
    can_can_action: :review_applicants
end
```

It will require the named action (`:review_applicants`) for the object being viewed (a `JobPosting`).

### Authorizing by attribute

CanCan 3.0 added attribute-level authorization ([pull request](https://github.com/CanCanCommunity/cancancan/pull/474)). You can leverage this in your field definitions with the `can_can_attribute:` configuration:

```ruby
# This will call `.can?(:read, user, :email_address)`
field :email_address, String,
  can_can_action: :read,
  can_can_attribute: :email_address
```

You could also provide a _default value_ for `can_can_attribute` in your base field class:

```ruby
class Types::BaseField
  def initialize(*args, **kwargs, &block)
    # pass all configs to the super class:
    super
    # Then set a new `can_can_attribute` value, if applicable
    if can_can_attribute.nil? && can_can_action.present?
      # `method_sym` is the thing GraphQL-Ruby will use to resolve this field
      can_can_attribute(method_sym)
    end
  end
end
```

(See [GraphQL::Schema::Field](rdoc-ref:GraphQL::Schema::Field) for the different values available for defaults.)

### Providing a Custom CanCan Subject

Authorization checks are _skipped_ whenever the underlying `object` is `nil`. This can happen in root query fields, for example, when no `root_value: ...` is given. To provide a `can_can_subject` in this case, you can add it as a field configuration:

```ruby
field :users, Types::User.connection_type, null: false,
  can_can_action: :manage,
  # `:all` will be used instead of `object` (which is `nil`)
  can_can_subject: :all
```

The configuration above will call `can?(:manage, :all)` whenever that field is requested.

## Authorizing Arguments

Similar to field-level checks, you can require certain permissions to _use_ certain arguments. To do this, add the integration to your base argument class:

```ruby
class Types::BaseArgument < GraphQL::Schema::Argument
  # Include the integration and default to no permissions required
  include GraphQL::Pro::CanCanIntegration::ArgumentIntegration
  can_can_action nil
end
```

Then, make sure your base argument is hooked up to your base field and base input object:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  argument_class Types::BaseArgument
  # PS: see "Authorizing Fields" to make sure your base field is hooked up to objects, interfaces and mutations
end

class Types::BaseInputObject < GraphQL::Schema::InputObject
  argument_class Types::BaseArgument
end

class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  argument_class Types::BaseArgument
end
```

Now, arguments accept a `can_can_action:` option, for example:

```ruby
class Types::Company < Types::BaseObject
  field :employees, Types::Employee.connection_type do
    # Only admins can filter employees by email:
    argument :email, String, required: false, can_can_action: :admin
  end
end
```

This will check for `can :admin, Company` (or a similar rule for the `company` being queried) for the current user.

## Authorizing Mutations

There are a few ways to authorize GraphQL mutations with the CanCan integration:

- Add a [mutation-level roles](#mutation-level-roles)
- Run checks on [objects loaded by ID](#authorizing-loaded-objects)

Also, you can configure [unauthorized object handling](#unauthorized-mutations)

#### Setup

Add `MutationIntegration` to your base mutation, for example:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  include GraphQL::Pro::CanCanIntegration::MutationIntegration

  # Also, to use argument-level authorization:
  argument_class Types::BaseArgument
end
```

Also, you'll probably want a `BaseMutationPayload` where you can set a default role:

```ruby
class Types::BaseMutationPayload < Types::BaseObject
  # If `BaseObject` requires some permissions, override that for mutation results.
  # Assume that anyone who can run a mutation can read their generated result types.
  can_can_action nil
end
```

And hook it up to your base mutation:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  object_class Types::BaseMutationPayload
  field_class Types::BaseField
end
```

#### Mutation-level roles

Each mutation can have a class-level `can_can_action` which will be checked before loading objects or resolving, for example:

```ruby
class Mutations::PromoteEmployee < Mutations::BaseMutation
  can_can_action :run_mutation
end
```

In the example above, `can :run_mutation, Mutations::PromoteEmployee` will be checked before running the mutation. (The currently-running instance of `Mutations::PromoteEmployee` is passed to the ability checker.)

#### Authorizing Loaded Objects

Mutations can automatically load and authorize objects by ID using the `loads:` option.

Beyond the normal [object reading permissions](#authorizing-objects), you can add an additional role for the specific mutation input using a `can_can_action:` option:

```ruby
class Mutations::FireEmployee < Mutations::BaseMutation
  argument :employee_id, ID,
    loads: Types::Employee,
    can_can_action: :supervise,
end
```

In the case above, the mutation will halt unless the `can :supervise, ...` check returns true. (The fetched instance of `Employee` is passed to the ability checker.)

#### Unauthorized Mutations

By default, an authorization failure in a mutation will raise a Ruby exception. You can customize this by implementing `#unauthorized_by_can_can(owner, value)` in your base mutation, for example:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  def unauthorized_by_can_can(owner, value)
    # No error, just return nil:
    nil
  end
end
```

The method is called with:

- `owner`: the `GraphQL::Schema::Argument` instance or mutation class whose role was not satisfied
- `value`: the object which didn't pass for `context[:current_user]`

Since it's a mutation method, you can also access `context` in that method.

Whatever that method returns will be treated as an early return value for the mutation, so for example, you could return [errors as data](/mutations/mutation_errors):

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  field :errors, [String]

  def unauthorized_by_can_can(owner, value)
    # Return errors as data:
    { errors: ["Missing required permission: #{owner.can_can_action}, can't access #{value.inspect}"] }
  end
end
```

## Authorizing Resolvers

Resolvers are authorized just like [mutations](#authorizing-mutations), and require similar setup:

```ruby
# app/graphql/resolvers/base_resolver.rb
class Resolvers::BaseResolver < GraphQL::Schema::Resolver
  include GraphQL::Pro::CanCanIntegration::ResolverIntegration
  argument_class BaseArgument
  # can_can_action(nil) # to disable authorization by default
end
```

Beyond that, see [Authorizing Mutations](#authorizing-mutations) above for further details.

## Custom Abilities Class

By default, the integration will look for a top-level `::Ability` class.

If you're using a different class, provide an instance ahead-of-time as `context[:can_can_ability]`

For example, you could _always_ add one in your schema's `#execute` method:

```ruby
class MySchema < GraphQL::Schema
  # Override `execute` to provide a custom Abilities instance for the CanCan integration
  def self.execute(*args, context: {}, **kwargs)
    # Assign `context[:can_can_ability]` to an instance of our custom class
    context[:can_can_ability] = MyAuthorization::CustomAbilitiesClass.new(context[:current_user])
    super
  end
end
```

---

### Guides Authorization Pundit Integration (`guides/authorization/pundit_integration.md`)

# Pundit Integration

[GraphQL::Pro](https://graphql.pro) includes an integration for powering GraphQL authorization with [Pundit](https://github.com/varvet/pundit) policies.

__Why bother?__ You _could_ put your authorization code in your GraphQL types themselves, but writing a separate authorization layer gives you a few advantages:

- Since the authorization code isn't embedded in GraphQL, you can use the same logic in non-GraphQL (or legacy) parts of the app.
- The authorization logic can be tested in isolation, so your end-to-end GraphQL tests don't have to cover as many possibilities.

## Getting Started

__NOTE__: Requires the latest gems, so make sure your `Gemfile` has:

```ruby
# For PunditIntegration:
gem "graphql-pro", ">=1.7.9"
# For list scoping:
gem "graphql", ">=1.8.7"
```

Then, `bundle install`.

Whenever you run queries, include `:current_user` in the context:

```ruby
context = {
  current_user: current_user,
  # ...
}
MySchema.execute(..., context: context)
```

### Rails Generator

If your schema files follow the same convention as `rails generate graphql:install`, then you can install the Pundit integration with a Rails generator:

```bash
$ rails generate graphql:pundit:install
```

This will insert all the necessary `include ...`s described below. Alternatively, check the docs below to mix in `PunditIntegration`'s modules.

## Authorizing Objects

You can specify Pundit roles that must be satisfied in order for viewers to see objects of a certain type. To get started, include the `ObjectIntegration` in your base object class:

```ruby
# app/graphql/types/base_object.rb
class Types::BaseObject < GraphQL::Schema::Object
  # Add the Pundit integration:
  include GraphQL::Pro::PunditIntegration::ObjectIntegration
  # By default, require staff:
  pundit_role :staff
  # Or, to require no permissions by default:
  # pundit_role nil
end
```

Now, anyone trying to read a GraphQL object will have to pass the `#staff?` check on that object's policy.

Then, each child class can override that parent configuration. For example, allow _all_ viewers to read the `Query` root:

```ruby
class Types::Query < Types::BaseObject
  # Allow anyone to see the query root
  pundit_role nil
end
```

#### Policies and Methods

For each object returned by GraphQL, the integration matches it to a policy and method.

The policy is found using [`Pundit.policy!`](https://www.rubydoc.info/gems/pundit/Pundit%2Epolicy!), which looks up a policy using the object's class name. (This can be customized, see below.)

Then, GraphQL will call a method on the policy to see whether the object is permitted or not. This method is assigned in the object class, for example:

```ruby
class Types::Employee < Types::BaseObject
  # Only show employee objects to their bosses,
  # or when that employee is the current viewer
  pundit_role :employer_or_self
  # ...
end
```

That configuration will call `#employer_or_self?` on the corresponding Pundit policy.

#### Custom Policy Class

By default, the integration uses `Pundit.policy!(current_user, object)` to find a policy. You can specify a policy class using `pundit_policy_class(...)`:

```ruby
class Types::Employee < Types::BaseObject
  pundit_policy_class(Policies::CustomEmployeePolicy)
  # Or, you could use a string:
  # pundit_policy_class("Policies::CustomEmployeePolicy")
end
```

For really custom policy lookup, see [Custom Policy Lookup](#custom-policy-lookup) below.

#### Bypassing Policies

The integration requires that every object with a `pundit_role` has a corresponding policy class. To allow objects to _skip_ authorization, you can pass `nil` as the role:

```ruby
class Types::PublicProfile < Types::BaseObject
  # Anyone can see this
  pundit_role nil
end
```

#### Handling Unauthorized Objects

When any Policy method returns `false`, the unauthorized object is passed to [Schema.unauthorized_object](rdoc-ref:GraphQL::Schema.unauthorized_object), as described in [Handling unauthorized objects](/authorization/authorization#handling-unauthorized-objects).

## Scopes

The Pundit integration adds [Pundit scopes](https://github.com/varvet/pundit#scopes) to GraphQL-Ruby's [list scoping](/authorization/scoping) feature. Any list or connection will be scoped. If a scope is missing, the query will crash rather than risk leaking unfiltered data.

To scope lists of interface or union type, include the integration in your base union class and base interface module:

```ruby
class BaseUnion < GraphQL::Schema::Union
  include GraphQL::Pro::PunditIntegration::UnionIntegration
end

module BaseInterface
  include GraphQL::Schema::Interface
  include GraphQL::Pro::PunditIntegration::InterfaceIntegration
end
```

#### Bypassing scopes

To allow an unscoped relation to be returned from a field, disable scoping with `scope: false`, for example:

```ruby
# Allow anyone to browse the job postings
field :job_postings, [Types::JobPosting], null: false,
  scope: false
```

## Authorizing Fields

You can also require certain checks on a field-by-field basis. First, include the integration in your base field class:

```ruby
# app/graphql/types/base_field.rb
class Types::BaseField < GraphQL::Schema::Field
  # Add the Pundit integration:
  include GraphQL::Pro::PunditIntegration::FieldIntegration
  # By default, don't require a role at field-level:
  pundit_role nil
end
```

If you haven't already done so, you should also hook up your base field class to your base object and base interface:

```ruby
# app/graphql/types/base_object.rb
class Types::BaseObject < GraphQL::Schema::Object
  field_class Types::BaseField
end
# app/graphql/types/base_interface.rb
module Types::BaseInterface
  # ...
  field_class Types::BaseField
end
# app/graphql/mutations/base_mutation.rb
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  field_class Types::BaseField
end
```

Then, you can add `pundit_role:` options to your fields:

```ruby
class Types::JobPosting < Types::BaseObject
  # Allow signed-in users to browse listings
  pundit_role :signed_in

  # But, only allow `JobPostingPolicy#staff?` users to see
  # who has applied
  field :applicants, [Types::User],
    pundit_role: :staff
end
```

It will call the named role (eg, `#staff?`) on the parent object's policy (eg `JobPostingPolicy`).

#### Custom Policy Class

You can override the policy class for a field using `pundit_policy_class:`, for example:

```ruby
class Types::JobPosting < Types::BaseObject
  # Only allow `ApplicantsPolicy#staff?` users to see
  # who has applied
  field :applicants, [Types::User],
    pundit_role: :staff,
    pundit_policy_class: ApplicantsPolicy
    # Or with a string:
    # pundit_policy_class: "ApplicantsPolicy"
end
```

This will initialize an `ApplicantsPolicy` with the parent object (a `Job`) and call `#staff?` on it.

For really custom policy lookup, see [Custom Policy Lookup](#custom-policy-lookup) below.

## Authorizing Arguments

Similar to field-level checks, you can require certain permissions to _use_ certain arguments. To do this, add the integration to your base argument class:

```ruby
class Types::BaseArgument < GraphQL::Schema::Argument
  # Include the integration and default to no permissions required
  include GraphQL::Pro::PunditIntegration::ArgumentIntegration
  pundit_role nil
end
```

Then, make sure your base argument is hooked up to your base field and base input object:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  argument_class Types::BaseArgument
  # PS: see "Authorizing Fields" to make sure your base field is hooked up to objects, interfaces and mutations
end

class Types::BaseInputObject < GraphQL::Schema::InputObject
  argument_class Types::BaseArgument
end

class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  argument_class Types::BaseArgument
end
```

Now, arguments accept a `pundit_role:` option, for example:

```ruby
class Types::Company < Types::BaseObject
  field :employees, Types::Employee.connection_type do
    # Only admins can filter employees by email:
    argument :email, String, required: false, pundit_role: :admin
  end
end
```

The role will be called on the parent object's policy, for example `CompanyPolicy#admin?` in the case above.

## Authorizing Mutations

There are a few ways to authorize GraphQL mutations with the Pundit integration:

- Add a [mutation-level roles](#mutation-level-roles)
- Run checks on [objects loaded by ID](#authorizing-loaded-objects)

Also, you can configure [unauthorized object handling](#unauthorized-mutations)

#### Setup

Add `MutationIntegration` to your base mutation, for example:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  include GraphQL::Pro::PunditIntegration::MutationIntegration

  # Also, to use argument-level authorization:
  argument_class Types::BaseArgument
end
```

Also, you'll probably want a `BaseMutationPayload` where you can set a default role:

```ruby
class Types::BaseMutationPayload < Types::BaseObject
  # If `BaseObject` requires some permissions, override that for mutation results.
  # Assume that anyone who can run a mutation can read their generated result types.
  pundit_role nil
end
```

And hook it up to your base mutation:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  object_class Types::BaseMutationPayload
  field_class Types::BaseField
end
```

#### Mutation-level roles

Each mutation can have a class-level `pundit_role` which will be checked before loading objects or resolving, for example:

```ruby
class Mutations::PromoteEmployee < Mutations::BaseMutation
  pundit_role :admin
end
```

In the example above, `PromoteEmployeePolicy#admin?` will be checked before running the mutation.

#### Custom Policy Class

By default, Pundit uses the mutation's class name to look up a policy. You can override this by defining `pundit_policy_class` on your mutation:

```ruby
class Mutations::PromoteEmployee < Mutations::BaseMutation
  pundit_policy_class ::UserPolicy
  pundit_role :admin
end
```

Now, the mutation will check `UserPolicy#admin?` before running.

For really custom policy lookup, see [Custom Policy Lookup](#custom-policy-lookup) below.

#### Authorizing Loaded Objects

Mutations can automatically load and authorize objects by ID using the `loads:` option.

Beyond the normal [object reading permissions](#authorizing-objects), you can add an additional role for the specific mutation input using a `pundit_role:` option:

```ruby
class Mutations::FireEmployee < Mutations::BaseMutation
  argument :employee_id, ID,
    loads: Types::Employee,
    pundit_role: :supervisor,
end
```

In the case above, the mutation will halt unless the `EmployeePolicy#supervisor?` method returns true.

#### Unauthorized Mutations

By default, an authorization failure in a mutation will raise a Ruby exception. You can customize this by implementing `#unauthorized_by_pundit(owner, value)` in your base mutation, for example:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  def unauthorized_by_pundit(owner, value)
    # No error, just return nil:
    nil
  end
end
```

The method is called with:

- `owner`: the `GraphQL::Schema::Argument` or mutation class whose role was not satisfied
- `value`: the object which didn't pass for `context[:current_user]`

Since it's a mutation method, you can also access `context` in that method.

Whatever that method returns will be treated as an early return value for the mutation, so for example, you could return [errors as data](/mutations/mutation_errors):

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  field :errors, [String]

  def unauthorized_by_pundit(owner, value)
    # Return errors as data:
    { errors: ["Missing required permission: #{owner.pundit_role}, can't access #{value.inspect}"] }
  end
end
```

## Authorizing Resolvers

Resolvers are authorized just like [mutations](#authorizing-mutations), and require similar setup:

```ruby
# app/graphql/resolvers/base_resolver.rb
class Resolvers::BaseResolver < GraphQL::Schema::Resolver
  include GraphQL::Pro::PunditIntegration::ResolverIntegration
  argument_class BaseArgument
  # pundit_role nil # to disable authorization by default
end
```

Beyond that, see [Authorizing Mutations](#authorizing-mutations) above for further details.

## Custom Policy Lookup

By default, the integration uses `Pundit`'s top-level methods to interact with policies:

- `Pundit.policy!(context[:current_user], object)` is called to find a policy instance
- `Pundit.policy_scope!(context[:current_user], items)` is called to filter `items`

### Custom Policy Methods

You can implement a custom lookup by defining the following methods in your schema:

- `pundit_policy_class_for(object, context)` to return a policy class (or raise an error if one isn't found)
- `pundit_role_for(object, context)` to return a role method (Symbol), or `nil` to bypass authorization
- `scope_by_pundit_policy(context, items)` to apply a scope to `items` (or raise an error if one isn't found)

Since different objects have different lifecycles, the hooks are installed slightly different ways:

- Your base argument, field, and mutation classes should have _instance methods_ with those names
- Your base type classes should have _class methods_ with that name

Here's an example of how the custom hooks can be installed:

```ruby
module CustomPolicyLookup
  # Lookup policies in the `SystemAdmin::` namespace for system_admin users
  # @return [Class]
  def pundit_policy_class_for(object, context)
    current_user = context[:current_user]
    if current_user.system_admin?
      SystemAdmin.const_get("#{object.class.name}Policy")
    else
      super
    end
  end

  # Require admin permissions if the object is pending_approval
  def pundit_role_for(object, context)
    if object.pending_approval?
      :admin
    else
      super # fall back to the normally-configured role
    end
  end
end

# Add policy hooks as class methods
class Types::BaseObject < GraphQL::Schema::Object
  extend CustomPolicyLookup
end
class Types::BaseUnion < GraphQL::Schema::Union
  extend CustomPolicyLookup
end
module Types::BaseInterface
  include GraphQL::Schema::Interface
  # Add this as a class method that will be "inherited" by other interfaces:
  definition_methods do
    include CustomPolicyLookup
  end
end

# Add policy hooks as instance methods
class Types::BaseField < GraphQL::Schema::Field
  include CustomPolicyLookup
end
class Types::BaseArgument < GraphQL::Schema::Argument
  include CustomPolicyLookup
end
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  include CustomPolicyLookup
end
```

### One Policy Per Class

Another good approach is to have one policy per class. You can implement `policy_class_for(object, context)` to look up a policy _within_ the class, for example:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  def policy_class_for(_object, _context)
    # Look up a nested `Policy` constant:
    self.class.const_get(:Policy)
  end
end
```

Then, each mutation can define its policy inline, for example:

```ruby
class Mutations::PromoteEmployee < Mutations::BaseMutation
  # This will be found by `BaseMutation.policy_class`, defined above:
  class Policy
    # ...
  end

  pundit_role :admin
end
```

Now, `Mutations::PromoteEmployee::Policy#admin?` will be checked before running the mutation.

## Custom User Lookup

By default, the Pundit integration looks for the current user in `context[:current_user]`. You can override this by implementing `#pundit_user` on your custom query context class. For example:

```ruby
# app/graphql/query_context.rb
class QueryContext < GraphQL::Query::Context
  def pundit_user
    # Lookup `context[:viewer]` instead:
    self[:viewer]
  end
end
```

Then be sure to hook up your custom class in the schema:

```ruby
class MySchema < GraphQL::Schema
  context_class(QueryContext)
end
```

Then, the Pundit integration will use your `def pundit_user` to get the current user at runtime.

---

### Guides Authorization Scoping (`guides/authorization/scoping.md`)

# Scoping

_Scoping_ is a complementary consideration to authorization. Rather than checking "can this user see this thing?", scoping takes a list of items filters it to the subset which is appropriate for the current viewer and context.

For similar features, see [Pundit scopes](https://github.com/varvet/pundit#scopes) and [Cancan's `.accessible_by`](https://github.com/CanCanCommunity/cancancan/blob/develop/docs/fetching_records.md).

## `scope:` option

Fields accept a `scope:` option to enable (or disable) scoping, for example:

```ruby
field :products, [Types::Product], scope: true
# Or
field :all_products, [Types::Product], scope: false
```

For __list__ and __connection__ fields, `scope: true` is the default. For all other fields, `scope: false` is the default. You can override this by using the `scope:` option.

## `.scope_items(items, ctx)` method

Type classes may implement `.scope_items(items, ctx)`. This method is called when a field has `scope: true`. For example,

```ruby
field :products, [Types::Product] # has `scope: true` by default
```

Will call:

```ruby
class Types::Product < Types::BaseObject
  def self.scope_items(items, context)
    # filter items here
  end
end
```

The method should return a new list with only the appropriate items for the current `context`.

## Bypassing object-level authorization

If you know that any items returned from `.scope_items` should be visible to the current client, you can skip the normal `.authorized?(obj, ctx)` checks by configuring `reauthorize_scoped_objects(false)` in your type definition. For example:

```ruby
class Types::Product < Types::BaseObject
  # Check that singly-loaded objects are visible to the current viewer
  def self.authorized?(object, context)
    super && object.visible_to?(context[:viewer])
  end

  # Filter any list to only include objects that are visible to the current viewer
  def self.scope_items(items, context)
    items = super(items, context)
    items.visible_for(context[:viewer])
  end

  # If an object of this type was returned from `.scope_items`,
  # don't call `.authorized?` with it.
  reauthorize_scoped_objects(false)
end
```

---

### Guides Authorization Visibility (`guides/authorization/visibility.md`)

# Visibility

With GraphQL-Ruby, it's possible to _hide_ parts of your schema from some users. This isn't exactly part of the GraphQL spec, but it's roughly within the bounds of the spec.

Here are some reasons you might want to hide parts of your schema:

- You don't want non-admin users to know about administration functions of the schema.
- You're developing a new feature and want to make a gradual release to only a few users first.

## Hiding Parts of the Schema

To start limiting visibility of your schema, add the plugin:

```ruby
class MySchema < GraphQL::Schema
  # ...
  use GraphQL::Schema::Visibility # see below for options
end
```

Then, you can customize the visibility of parts of your schema by reimplementing various `visible?` methods:

- Type classes have a `.visible?(context)` class method
- Fields and arguments have a `#visible?(context)` instance method
- Enum values have `#visible?(context)` instance method
- Mutation classes have a `.visible?(context)` class method

These methods are called with the query context, based on the hash you pass as `context:`. If the method returns false, then that member of the schema will be treated as though it doesn't exist for the entirety of the query. That is:

- In introspection, the member will _not_ be included in the result
- In normal queries, if a query references that member, it will return a validation error, since that member doesn't exist

## Visibility Profiles

You can use named profiles to cache your schema's visibility modes. For example:

```ruby
use GraphQL::Schema::Visibility, profiles: {
  # mode_name => example_context_hash
  public: { public: true },
  beta: { public: true, beta: true },
  internal_admin: { internal_admin: true }
}
```

Then, you can run queries with `context[:visibility_profile]` equal to one of the pre-defined profiles. When you do, GraphQL-Ruby will create a cached set of types for named profile. `.visible?` will only be called with the context hash passed to `profiles: ...`.

The profile contexts passed to `profiles` will have `visibility_profile: ...` added to them, then they're frozen by GraphQL-Ruby.

### Preloading profiles

By default, GraphQL-Ruby will preload all named visibility profiles when `Rails.env.production?` is present and true. You can manually set this option by passing `use ... preload: true` (or `false`). Enable preloading in production to reduce latency of the first request to each visibility profile. Disable preloading in development to speed up application boot.

### Dynamic profiles

When you provide named visibility profiles, `context[:visibility_profile]` is required for query execution. You can also permit dynamic visibility for queries which _don't_ have that key set by passing `use ..., dynamic: true`. You could use this to support backwards compatibility or when visibility calculations are too complex to predefine.

When no named profiles are defined, all queries use dynamic visibility.

## Object Visibility

Let's say you're working on a new feature which should remain secret for a while. You can implement `.visible?` in a type:

```ruby
class Types::SecretFeature < Types::BaseObject
  def self.visible?(context)
    # only show it to users with the secret_feature enabled
    super && context[:viewer].feature_enabled?(:secret_feature)
  end
end
```

(Always call `super` to inherit the default behavior.)

Now, the following bits of GraphQL will return validation errors:

- Fields that return `SecretFeature`, eg `query { findSecretFeature { ... } }`
- Fragments on `SecretFeature`, eg `Fragment SF on SecretFeature`

And in introspection:

- `__schema { types { ... } }` will not include `SecretFeature`
- `__type(name: "SecretFeature")` will return `nil`
- Any interfaces or unions which normally include `SecretFeature` will _not_ include it
- Any fields that return `SecretFeature` will be excluded from introspection

## Field Visibility

```ruby
class Types::BaseField < GraphQL::Schema::Field
  # Pass `field ..., require_admin: true` to hide this field from non-admin users
  def initialize(*args, require_admin: false, **kwargs, &block)
    @require_admin = require_admin
    super(*args, **kwargs, &block)
  end

  def visible?(ctx)
    # if `require_admin:` was given, then require the current user to be an admin
    super && (@require_admin ? ctx[:viewer]&.admin? : true)
  end
end
```

For this to work, the base field class must be [configured with other GraphQL types](/type_definitions/extensions.html#customizing-fields).

## Argument Visibility

```ruby
class Types::BaseArgument < GraphQL::Schema::Argument
  # If `require_logged_in: true` is given, then this argument will be hidden from logged-out viewers
  def initialize(*args, require_logged_in: false, **kwargs, &block)
    @require_logged_in = require_logged_in
    super(*args, **kwargs, &block)
  end

  def visible?(ctx)
    super && (@require_logged_in ? ctx[:viewer].present? : true)
  end
end
```

For this to work, the base argument class must be [configured with other GraphQL types](/type_definitions/extensions.html#customizing-arguments).

## Opting Out

By default, GraphQL-Ruby always runs visibility checks. You can opt out of this by adding to your schema class:

```ruby
class MySchema < GraphQL::Schema
  # ...
  # Opt out of GraphQL-Ruby's visibility feature:
  use GraphQL::Schema::AlwaysVisible
end
```

For big schemas, this can be a worthwhile speed-up.

## Migration Notes

[GraphQL::Schema::Visibility](rdoc-ref:GraphQL::Schema::Visibility) is a _new_ implementation of visibility in GraphQL-Ruby. It has some slight differences from the previous implementation ([GraphQL::Schema::Warden](rdoc-ref:GraphQL::Schema::Warden)):

- `Visibility` speeds up Rails app boot because it doesn't require all types to be loaded during boot and only loads types as they are used by queries.
- `Visibility` supports predefined, reusable visibility profiles which speeds up queries using complicated `visible?` checks.
- `Visibility` hides types differently in a few edge cases:
  - Previously, `Warden` hid interface and union types which had no possible types. `Visibility` doesn't check possible types (in order to support performance improvements), so those types must return `false` for `visible?` in the same cases where all possible types were hidden. Otherwise, that interface or union type will be visible but have no possible types.
  - When an object type is connected to the schema as a field return type or a union member, and also implements and interface, if the object type's _other_ connection(s) to the schema are hidden, then it won't appear as an implementer of that interface unless it's registered with `orphan_types` (either by the schema or interface). `Warden` used a "global" map of types so it could discover object types in this case, but `Visibility` doesn't have that global map. (Since time of writing, `Visibility` _does_ have some global type tracking, so maybe this could be fixed.)
- When `Visibility` is used, several (Ruby-level) Schema introspection methods don't work because the caches they draw on haven't been calculated (`Schema.references_to`, `Schema.union_memberships`). If you're using these, please get in touch so that we can find a way forward.
- If you programmatically generate your GraphQL schema (instead of using plain-Ruby `.rb` files), you must ensure that generation is threadsafe. Otherwise, definitions may be malformed in Rails development mode when multithreaded. (Rails eager-loads all files in production mode, so this concern doesn't exist there.)

### Migration Mode

You can use `use GraphQL::Schema::Visibility, ... migration_errors: true` to enable migration mode. In this mode, GraphQL-Ruby will make visibility checks with _both_ `Visibility` and `Warden` and compare the result, raising a descriptive error when the two systems return different results. As you migrate to `Visibility`, enable this mode in test to find any unexpected discrepancies.

Sometimes, there's a discrepancy that is hard to resolve but doesn't make any _real_ difference in application behavior. To address these cases, you can use these flags in `context`:

- `context[:visibility_migration_running] = true` is set in the main query context.
- `context[:visibility_migration_warden_running] = true` is set in the _duplicate_ context which is passed to a `Warden` instance.
- If you set `context[:skip_migration_error] = true`, then no migration error will be raised for that query.

You can use these flags to conditionally handle edge cases that should be ignored in testing.

---

### Guides Changesets Definition (`guides/changesets/definition.md`)

# Defining Changesets

After [installing Changeset integrations](/changesets/installation) in your schema, you can create Changesets which modify parts of the schema. Changesets extend `GraphQL::Enterprise::Changeset` and include a `release` string. Once a Changeset class is defined, it can be referenced with `added_in: ...` or `removed_in: ...` configurations in the schema.

__Note:__ Before GraphQL-Enterprise 1.3.0, Changesets were configured with `modifies ...` blocks. These blocks are still supported and you can find the documentation for that API [on GitHub](https://github.com/rmosolgo/graphql-ruby/blob/v2.0.22/guides/changesets/definition.md).


## Changeset Classes

This Changeset will be available to any client whose `context[:changeset_version]` is on or after `2020-12-01`:

```ruby
# app/graphql/changesets/deprecate_recipe_flag.rb
class Changesets::DeprecateRecipeTags < GraphQL::Enterprise::Changeset
  release "2020-12-01"
end
```

Additionally, Changesets must be [released](/changesets/releases) for their changes to be published.

## Publishing with `added_in:`

New things can be published in a changeset by adding `added_in: SomeChangeset` to their configuration. For example, to add a new argument to a field:

```ruby
field :search_recipes, [Types::Recipe] do
  argument :query, String
  argument :tags, [Types::RecipeTag], required: false, added_in: Changesets::AddRecipeTags
end
```

You can also provide a _replacement_ implementation by using `added_in:`. When a new definition has the same name as an existing definition, it implicitly replaces the previous definition in new versions of the API. For example:

```ruby
field :rating, Integer, "A 1-5 score for this recipe" # This definition will be superseded by the following one
field :rating, Float, "A 1.0-5.0 score for this recipe", added_in: Changesets::FloatingPointRatings
```

Here, a new implementation for `rating` will be used when clients requests an API version that includes `Changesets::FloatingPointRatings`. (If the client requests a version _before_ that changeset, then the preceding implementation would be used instead.)

## Removing with `removed_in:`

A `removed_in:` configuration removes something in the named changeset. For example, these enum values are replaced with more clearly-named ones:

```ruby
class Types::RecipeTag < Types::BaseEnum
  # These are replaced by *_HEAT below:
  value :SPICY, removed_in: Changesets::ClarifyHeatTags
  value :MEDIUM, removed_in: Changesets::ClarifyHeatTags
  value :MILD, removed_in: Changesets::ClarifyHeatTags
  # These new tags are more clear:
  value :SPICY_HEAT, added_in: Changesets::ClarifyHeatTags
  value :MEDIUM_HEAT, added_in: Changesets::ClarifyHeatTags
  value :MILD_HEAT, added_in: Changesets::ClarifyHeatTags
end
```

If something has been defined several times, a `removed_in:` configuration removes _all_ definitions:

```ruby
class Mutations::SubmitRecipeRating < Mutations::BaseMutation
  # This is replaced in future API versions by the following argument
  argument :rating, Integer
  # This replaces the previous, but in another future version,
  # it is removed completely (and so is the previous one)
  argument :rating, Float, added_in: Changesets::FloatingPointRatings, removed_in: Changesets::RemoveRatingsCompletely
end
```

## Examples

See below for the different kind of modifications you can make in a changeset:

- [Fields](#fields): adding, modifying, and removing fields
- [Arguments](#arguments): adding, modifying, and removing arguments
- [Enum values](#enum-values): adding, modifying, and removing arguments
- [Unions](#unions): adding or removing object types from a union
- [Interfaces](#interfaces): adding or removing interface implementations from object types
- [Types](#types): changing one type definition for another
- [Runtime](#runtime): choosing a behavior at runtime based on the current request and changeset

### Fields

To add or redefine a field, use `field(..., added_in: ...)`, including all configuration values for the new implementation (see the [GraphQL::Schema::Field](rdoc-ref:GraphQL::Schema::Field) API). The definition given here will override the previous definition (if there was one) whenever this Changeset applies.

```ruby
class Types::Recipe < Types::BaseObject
  # This new field is available when `context[:changeset_version]`
  # is on or after the release date of `AddRecipeTags`
  field :tags, [Types::RecipeTag], added_in: Changeset::AddRecipeTags
end
```

To remove a field, add a `removed_in: ...` configuration to the last definition of the field:

```ruby
class Types::Recipe < Types::BaseObject
  # Even after migrating to floating point values,
  # the "rating" feature never took off,
  # so we removed it entirely eventually.
  field :rating, Integer
  field :rating, Float, added_in: Changeset::FloatingPointRatings,
    removed_in: Changeset::RemoveRatings
end
```

When a field is removed, queries that request that field will be invalid, unless the client has requested a previous API version where the field is still available.

### Arguments

You can add, redefine, or remove arguments that belong to fields, input objects, or resolvers. Use `added_in: ...` to provide a new (or updated) definition for an argument, for example:

```ruby
class Types::RecipesFilter < Types::BaseInputObject
  argument :rating, Integer
  # This new definition is available when
  # the client's `context[:changeset_version]` includes `FloatingPointRatings`
  argument :rating, Float, added_in: Changesets::FloatingPointRatings
end
```

To remove an argument entirely, add a `removed_in: ...` configuration to the last definition. It will remove _all_ implementations for that argument. For example:

```ruby
class Mutations::SubmitRating < Mutations::BaseMutation
  # Remove this because it's irrelevant:
  argument :phone_number, String, removed_in: Changesets::StopCollectingPersonalInformation
end
```

When arguments are removed, the schema will reject any queries which use them unless the client has requested a previous API version where the argument is still allowed.

### Enum Values

With Changesets, you can add, redefine, or remove enum values. To add a new value (or provide a new implementation for a value), include `added_in:` in the `value(...)` configuration:

```ruby
class Types::RecipeTag < Types::BaseEnum
  # This enum will accept and return `KETO` only when the client's API version
  # includes `AddKetoDietSupport`'s release date.
  value :KETO, added_in: Changesets::AddKetoDietSupport
end
```

Values can be removed with `removed_in:`, for example:

```ruby
class Types::RecipeTag < Types::BaseEnum
  # Old API versions will serve this value;
  # new versions won't accept it or return it.
  value :GRAPEFRUIT_DIET, removed_in: Changesets::RemoveLegacyDiets
end
```

When enum values are removed, they won't be accepted as input and they won't be allowed as return values from fields unless the client has requested a previous API version where those values are still allowed.

### Unions

You can add to or remove from a union's possible types. To release a new union member, include `added_in:` in the `possible_types` configuration:

```ruby
class Types::Cookable < Types::BaseUnion
 possible_types Types::Recipe, Types::Ingredient
 # Add this to the union when clients opt in to our new feature:
 possible_types Types::Cuisine, added_in: Changeset::ReleaseCuisines
```

To remove a member from a union, move it to a `possible_types` call with `removed_in: ...`:

```ruby
# Stop including this in the union in new API versions:
possible_types Types::Chef, removed_in: Changeset::LessChefHype
```

When a possible type is removed, it will not be associated with the union type in introspection queries or schema dumps.

### Interfaces

You can add to or remove from an object type's interface definitions. To add one or more interface implementations, use `implements(..., added_in:)`. This will add the interface and its fields to the object whenever this Changeset is active, for example:

```ruby
class Types::Recipe < Types::BaseObject
  # Add this new implementation in new API versions only:
  implements Types::RssSubject, added_in: Changesets::AddRssSupport
end
```


To remove one or more more interface implementations, add `removed_in:` to the `implements ...` configuration, for example:

```ruby
  implements Types::RssSubject,
    added_in: Changesets::AddRssSupport,
    # Sadly, nobody seems to want to use this,
    # so we removed it all:
    removed_in: Changesets::RemoveRssSupport
```

When an interface implementation is removed, then the interface will not be associated with the object in introspection queries or schema dumps. Also, any fields inherited from the interface will be hidden from clients. (If the object defines the field itself, it will still be visible.)

### Types

Using Changesets, it's possible to define a new type using the same name as an old type. (Only one type per name is allowed for each query, but different queries can use different types for the same name.)

First, to define two types with the same name, make two different type definitions. One of them will have to use `graphql_name(...)` to specify the conflicting type name. For example, to migrate an enum type to an object type, define two types:

```ruby
# app/graphql/types/legacy_recipe_flag.rb

# In the old version of the schema, "recipe tags" were limited to defined set of values.
# This enum was renamed from `Types::RecipeTag`, then `graphql_name("RecipeTag")`
# was added for GraphQL.
class Types::LegacyRecipeTag < Types::BaseEnum
  graphql_name "RecipeTag"
  # ...
end
```

```ruby
# app/graphql/types/recipe_flag.rb

# But in the new schema, each tag is a full-fledged object with fields of its own
class Types::RecipeTag < Types::BaseObject
  field :name, String, null: false
  field :is_vegetarian, Boolean, null: false
  # ...
end
```

Then, add or update fields or arguments to use the _new_ type instead of the old one. For example:

```diff
  class Types::Recipe < Types::BaseObject

# Change this definition to point at the newly-renamed _legacy_ type
# (It's the same type definition, but the Ruby class has a new name)
-   field :tags, [Types::RecipeTag]
+   field :tags, [Types::LegacyRecipeTag]

# And add a new field for the new type:
+   field :tags, [Types::RecipeTag], added_in: Changesets::MigrateRecipeTagToObject
  end
```

With that Changeset, `Recipe.tags` will return an object type instead of an enum type. Clients requesting older versions will still receive enum values from that field.

The resolver will probably need an update, too, for example:

```ruby
class Types::Recipe < Types::BaseObject
  # Here's the original definition which returns enum values:
  field :tags, [Types::LegacyRecipeTag], null: false
  # Here's the new definition which replaces the previous one on new API versions:
  field :tags, [Types::RecipeTag], null: false, added_in: Changesets::MigrateRecipeTagToObject

  def flags
    all_flag_objects = object.flag_objects
    if Changesets::MigrateRecipeTagToObject.active?(context)
      # Here's the new behavior, returning full objects:
      all_flag_objects
    else
      # Convert this to enum values, for legacy behavior:
      all_flag_objects.map { |f| f.name.upcase }
    end
  end
end
```

That way, legacy clients will continue to receive enum values while new clients will receive objects.

## Runtime

While a query is running, you can check if a changeset applies by using its `.active?(context)` method. For example:

```ruby
class Types::Recipe
  field :flag, Types::RecipeFlag, null: true

  def flag
    # Check if this changeset applies to the current request:
    if Changesets::DeprecateRecipeFlag.active?(context)
      Stats.count(:deprecated_recipe_flag, context[:viewer])
    end
    # ...
  end
end
```

Besides observability, you can use a runtime check when a resolver needs to pick a different behavior depending on the API version.

After defining a changeset, add it to the schema to [release it](/changesets/releases).

---

### Guides Changesets Installation (`guides/changesets/installation.md`)

# Installing Changesets

Changesets require some updates to the schema (to define changesets) and some updates to your controller (to receive version headers from clients).

## Schema Setup

To get started with [GraphQL-Enterprise](https://graphql.pro/enterprise) Changesets, you have to add them to your schema. They're added in several places:

- To support versioning arguments, add the `ArgumentIntegration` to your base argument:

    ```ruby
    # app/graphql/types/base_argument.rb
    class Types::BaseArgument < GraphQL::Schema::Argument
      include GraphQL::Enterprise::Changeset::ArgumentIntegration
    end
    ```

    Also, make sure that your `BaseField`, `BaseInputObject`, `BaseResolver`, and `BaseMutation` have `argument_class(Types::BaseArgument)` configured in them.

- To support versioning fields, add the `FieldIntegration` to your base field:

    ```ruby
    # app/graphql/types/base_field.rb
    class Types::BaseField < GraphQL::Schema::Field
      include GraphQL::Enterprise::Changeset::FieldIntegration
      argument_class(Types::BaseArgument)
    end
    ```

    Also, make sure that your `BaseObject`, `BaseInterface`, and `BaseMutation` have `field_class(Types::BaseField)` configured in them.

- To support versioning enum values, add the `EnumValueIntegration` to your base enum value:

    ```ruby
    # app/graphql/types/base_enum_value.rb
    class Types::BaseEnumValue < GraphQL::Schema::EnumValue
      include GraphQL::Enterprise::Changeset::EnumValueIntegration
    end
    ```

    Also, make sure that your `BaseEnum` has `enum_value_class(Types::BaseEnumValue)` configured in it.

- To support versioning union memberships and interface implementations, add the `TypeMembershipIntegration` to your base type membership:

    ```ruby
    # app/graphql/types/base_type_membership.rb
    class Types::BaseTypeMembership < GraphQL::Schema::TypeMembership
      include GraphQL::Enterprise::Changeset::TypeMembershipIntegration
    end
    ```

    Also, make sure that your `BaseUnion` and `BaseInterface` have `type_membership_class(Types::BaseTypeMembership)` configured in it. (`TypeMembership`s are used by GraphQL-Ruby to link object types to the union types they belong to and the interfaces they implement. By using a custom type membership class, you can make objects belong (or _not_ belong) to unions or interfaces, depending on the API version.)

Once those integrations are set up, you're ready to [write a changeset](/changesets/definition) and start [releasing API versions](/changesets/releases)!

## Controller Setup

Additionally, your controller must pass `context[:changeset_version]` when running queries. To provide this, update your controller:

```ruby
class GraphqlController < ApplicationController
  def execute
    context = {
      # ...
      changeset_version: request.headers["API-Version"], # <- Your header here. Choose something for API clients to pass.
    }
    result = MyAppSchema.execute(..., context: context)
    # ...
  end
end
```

In the example above, `API-Version: ...` will be parsed from the incoming request and used as `context[:changeset_version]`.

If `context[:changeset_version]` is `nil`, then _no_ changesets will apply to that request.

Now that Changesets are installed, read on to [define some changesets](/changesets/definition).

---

### Guides Changesets Releases (`guides/changesets/releases.md`)

# Releasing Changesets

To be available to clients, Changesets added to the schema with `use GraphQL::Enterprise::Changeset::Release changesets_dir: "..."`:

```ruby
class MyAppSchema < GraphQL::Schema
  # Add this before root types so that newly-added types are also added to the schema
  use GraphQL::Enterprise::Changeset::Release, changesets_dir: "app/graphql/changesets"

  query(...)
  mutation(...)
  subscription(...)
end
```

This attaches each Changeset defined in `app/graphql/changesets/*.rb` to the schema. (It assumes Rails conventions, where an underscored file like `app/graphql/changesets/add_some_feature.rb` contains a class like `Changesets::AddSomeFeature`.)

> **Warning:**
>
> Add `GraphQL::Enterprise::Changeset::Release` _before_ hooking up your root `query(...)`, `mutation(...)`, and `subscription(...)` types. Otherwise, the schema may not find links to types in new schema versions.
>


Alternatively, Changesets can be explicitly attached using `changesets: [...]`, for example:

```ruby
class MyAppSchema < GraphQL::Schema
  use GraphQL::Enterprise::Changeset::Release, changesets: [
    Changesets::DeprecateRecipeFlag,
    Changesets::RemoveRecipeFlag,
  ]
  # ...
end
```

Only changesets in the directory (or in the array) will be shown to clients. The `release ...` configuration in the changeset will be compared to `context[:changeset_version]` to determine if the changeset applies to the current request.

## Inspecting Releases

To preview releases, you can create schema dumps by passing `context: { changeset_version: ... }` to [Schema.to_definition](rdoc-ref:GraphQL::Schema.to_definition).

For example, to see how the schema looks with `API-Version: 2021-06-01`:

```ruby
schema_sdl = MyAppSchema.to_definition(context: { changeset_version: "2021-06-01"})
# The GraphQL schema definition for the schema at version "2021-06-01":
puts schema_sdl
```

To make sure schema versions don't change unexpectedly, use the techniques described in the [Schema structure guide](/testing/schema_structure).

### Introspection Methods

You can also inspect a schema's changesets programmatically. `GraphQL::Enterprise` adds a `Schema.changesets` method which returns a `Set` of changeset classes:

```ruby
MySchema.changesets
# #<Set: {AddNewFeature, RemoveOldFeature}>
```

Additionally, each changeset has a `.changes` method describing its modifications:

```ruby
AddNewFeature.changes
# [
#   #<GraphQL::Enterprise::Changeset::Change: ...>,
#   #<GraphQL::Enterprise::Changeset::Change: ...>,
#   #<GraphQL::Enterprise::Changeset::Change: ...>,
#   ...
# ]
```

Each `Change` object responds to:

- `.member`, the part of the schema that was modified
- `.type`, the kind of modification (`:addition` when something new is added, `:removal` when a member is removed or replaced with a new definition)

---

### Guides Dataloader Adopting (`guides/dataloader/adopting.md`)

# Dataloader vs. GraphQL-Batch

[GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) solves the same problem as [`GraphQL::Batch`](https://github.com/shopify/graphql-batch). There are a few major differences between the modules:


- __Concurrency Primitive:__ GraphQL-Batch uses `Promise`s from [`promise.rb`](https://github.com/lgierth/promise.rb); GraphQL::Dataloader uses Ruby's [`Fiber` API](https://ruby-doc.org/core-3.0.0/Fiber.html). These primitives dictate how batch loading code is written (see below for comparisons).
- __Maturity:__ Frankly, GraphQL-Batch is about as old as GraphQL-Ruby, and it's been in production at Shopify, GitHub, and others for many years. GraphQL::Dataloader is new, and although Ruby has supported `Fiber`s since 1.9, they still aren't widely used.
- __Scope:__ It's not currently possible to use `GraphQL::Dataloader` _outside_ GraphQL.

The incentive in writing `GraphQL::Dataloader` was to leverage `Fiber`'s ability to _transparently_ pause and resume work, which removes the need for `Promise`s (and removes the resulting complexity in the code). Additionally, `GraphQL::Dataloader` should _eventually_ support Ruby 3.0's `Fiber.scheduler` API, which runs I/O in the background by default.

## Comparison: Fetching a single object

In this example, a single object is batch-loaded to satisfy a GraphQL field.

- With __GraphQL-Batch__, you call a loader, which returns a `Promise`:

  ```ruby
  record_promise = Loaders::Record.load(1)
  ```

  Then, under the hood, GraphQL-Ruby manages the promise (using its `lazy_resolve` feature, upstreamed from GraphQL-Batch many years ago). GraphQL-Ruby will call `.sync` on it when no further execution is possible; `promise.rb` implements `Promise#sync` to execute the pending work.

- With __GraphQL::Dataloader__, you get a source, then call `.load` on it, which may pause the current Fiber, but it returns the requested object.

  ```ruby
  dataloader.with(Sources::Record).load(1)
  ```

  Since the requested object is (eventually) returned from `.load`, Nothing else is required.

## Comparison: Fetching objects in sequence (dependent)

In this example, one object is loaded, then another object is loaded _based on_ the first one.

- With __GraphQL-Batch__, `.then { ... }` is used to join dependent code blocks:

  ```ruby
  Loaders::Record.load(1).then do |record|
    Loaders::OtherRecord.load(record.other_record_id)
  end
  ```

  That call returns a `Promise`, which is stored by GraphQL-Ruby, and finally `.sync`ed.

- With __GraphQL-Dataloader__, `.load(...)` returns the requested object (after a potential `Fiber` pause), so no other method calls are necessary:

  ```ruby
  record = dataloader.with(Sources::Record).load(1)
  dataloader.with(Sources::OtherRecord).load(record.other_record_id)
  ```

## Comparison: Fetching objects concurrently (independent)

Sometimes, you need multiple _independent_ records to perform a calculation. Each record is loaded, then they're combined in some bit of work.

- With __GraphQL-Batch__, `Promise.all(...)` is used to wait for several pending loads:

  ```ruby
  promise_1 = Loaders::Record.load(1)
  promise_2 = Loaders::OtherRecord.load(2)
  Promise.all([promise_1, promise_2]).then do |record, other_record|
    do_something(record, other_record)
  end
  ```

  If the objects are loaded from the same loader, then `.load_many` also works:

  ```ruby
  Loaders::Record.load_many([1, 2]).then do |record, other_record|
    do_something(record, other_record)
  end
  ```

- With __GraphQL::Dataloader__, each request is registered with `.request(...)` (which never pauses the Fiber), then data is loaded with `.load` (which will pause the Fiber as needed):

  ```ruby
  # first, make some requests
  request_1 = dataloader.with(Sources::Record).request(1)
  request_2 = dataloader.with(Sources::OtherRecord).request(2)
  # then, load the objects and do something
  record = request_1.load
  other_record = request_2.load
  do_something(record, other_record)
  ```

  If the objects come from the same `Source`, then `.load_all` will return the objects directly:

  ```ruby
  record, other_record = dataloader.with(Sources::Record).load_all([1, 2])
  do_something(record, other_record)
  ```

---

### Guides Dataloader Async Dataloader (`guides/dataloader/async_dataloader.md`)

# Async Source Execution

`AsyncDataloader` will run [GraphQL::Dataloader::Source#fetch](rdoc-ref:GraphQL::Dataloader::Source#fetch) calls in parallel, so that external service calls (like database queries or network calls) don't have to wait in a queue.

To use `AsyncDataloader`, hook it up in your schema _instead of_ `GraphQL::Dataloader`:

```diff
- use GraphQL::Dataloader
+ use GraphQL::Dataloader::AsyncDataloader
```

__Also__, add [the `async` gem](https://github.com/socketry/async) to your project, for example:

```
bundle add async
```

Now, [GraphQL::Dataloader::AsyncDataloader](rdoc-ref:GraphQL::Dataloader::AsyncDataloader) will create `Async::Task` instances instead of plain `Fiber`s and the `async` gem will manage parallelism.

For a demonstration of this behavior, see: [https://github.com/rmosolgo/rails-graphql-async-demo](https://github.com/rmosolgo/rails-graphql-async-demo)

_You can also implement [manual parallelism](/dataloader/parallelism) using `dataloader.yield`._

## Rails

For Rails, you'll need **Rails 7.1**, which properly supports fiber-based concurrency, and you'll also want to configure Rails to use Fibers for isolation:

```ruby
class Application < Rails::Application
  # ...
  config.active_support.isolation_level = :fiber
end
```
### ActiveRecord Connections

You can use Dataloader's [Fiber lifecycle hooks](/dataloader/dataloader#fiber-lifecycle-hooks) to improve ActiveRecord connection handling:

- In Rails < 7.2, connections are not reused when a Fiber exits; instead, they're only reused when a request or background job finishes. You can add manual `release_connection` calls to improve this.
- With `isolation_level = :fiber`, new Fibers don't inherit `connected_to ...` settings from their parent fibers.

Altogether, it can be improved like this:

```ruby
def get_fiber_variables
  vars = super
  # Collect the current connection config to pass on:
  vars[:connected_to] = {
    role: ActiveRecord::Base.current_role,
    shard: ActiveRecord::Base.current_shard,
    prevent_writes: ActiveRecord::Base.current_preventing_writes
  }
  vars
end

def set_fiber_variables(vars)
  connection_config = vars.delete(:connected_to)
  # Reset connection config from the parent fiber:
  ActiveRecord::Base.connecting_to(**connection_config)
  super(vars)
end

def cleanup_fiber
  super
  # Release the current connection
  ActiveRecord::Base.connection_pool.release_connection
end
```

Modify the example according to your database configuration and abstract class hierarchy.

## Other Options

You can also manually implement parallelism with Dataloader. See the [Dataloader Parallelism](/dataloader/parallelism) guide for details.

---

### Guides Dataloader Dataloader (`guides/dataloader/dataloader.md`)

# Dataloader

[GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) instances are created for each query (or multiplex) and they:

- Cache [Source](/dataloader/sources) instances for the duration of GraphQL execution
- Run pending Fibers to resolve data requirements and continue GraphQL execution

During a query, you can access the dataloader instance with:

- [GraphQL::Query::Context#dataloader](rdoc-ref:GraphQL::Query::Context#dataloader) (`context.dataloader`, anywhere that query context is available)
- [GraphQL::Schema::Object#dataloader](rdoc-ref:GraphQL::Schema::Object#dataloader) (`dataloader` inside a resolver method)
- [GraphQL::Schema::Resolver#dataloader](rdoc-ref:GraphQL::Schema::Resolver#dataloader) (`dataloader` inside `def resolve` of a Resolver, Mutation, or Subscription class.)

## Fiber Lifecycle Hooks

Under the hood, `Dataloader` creates Fibers as-needed and uses them to run GraphQL and load data from `Source` classes. You can hook into these Fibers through several lifecycle hooks. To implement these hooks, create a custom subclass and provide new implementation for these methods:

```ruby
class MyDataloader < GraphQL::Dataloader # or GraphQL::Dataloader::AsyncDataloader
  # ...
end
```

Then, use your customized dataloader instead of the built-in one:

```diff
  class MySchema < GraphQL::Schema
-   use GraphQL::Dataloader
+   use MyDataloader
  end
```

- __[GraphQL::Dataloader#get_fiber_variables](rdoc-ref:GraphQL::Dataloader#get_fiber_variables)__ is called before creating a Fiber. By default, it returns a hash containing the parent Fiber's variables (from `Thread.current[...]`). You can add to this hash in your own implementation of this method.
- __[GraphQL::Dataloader#set_fiber_variables](rdoc-ref:GraphQL::Dataloader#set_fiber_variables)__ is called inside the new Fiber. It's passed the hash returned from `get_fiber_variables`. You can use this method to initialize "global" state inside the new Fiber.
- __[GraphQL::Dataloader#cleanup_fiber](rdoc-ref:GraphQL::Dataloader#cleanup_fiber)__ is called just before a Dataloader Fiber exits. You can use this methods to teardown any state that you prepared in `set_fiber_variables`.

---

### Guides Dataloader Parallelism (`guides/dataloader/parallelism.md`)

# Manual Parallelism

You can coordinate with [GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) to run tasks in the background. To do this, call `dataloader.yield` inside `Source#fetch` after kicking off your task. For example:

```ruby
def fetch(ids)
  # somehow queue up a background query,
  # see examples below
  future_result = async_query_for(ids)
  # return control to the dataloader
  dataloader.yield
  # dataloader will come back here
  # after calling other sources,
  # now wait for the value
  future_result.value
end
```

_Alternatively, you can use [AsyncDataloader](/dataloader/async_dataloader) to automatically background I/O inside `Source#fetch` calls._

## Example: Rails load_async

You can use Rails's `load_async` method to load `ActiveRecord::Relation`s in the background. For example:

```ruby
class Sources::AsyncRelationSource < GraphQL::Dataloader::Source
  def fetch(relations)
    relations.each(&:load_async) # start loading them in the background
    dataloader.yield # hand back to GraphQL::Dataloader
    relations.each(&:load) # now, wait for the result, returning the now-loaded relation
  end
end
```

You could call that source from a GraphQL field method:

```ruby
field :direct_reports, [Person]

def direct_reports
  # prepare an ActiveRecord::Relation:
  direct_reports = Person.where(manager: object)
  # pass it off to the source:
  dataloader
    .with(Sources::AsyncRelationSource)
    .load(direct_reports)
end
```

## Example: Rails async calculations

In a Dataloader source, you can run Rails async calculations in the background while other work continues. For example:

```ruby
class Sources::DirectReportsCount < GraphQL::Dataloader::Source
  def fetch(users)
    # Start the queries in the background:
    promises = users.map { |u| u.direct_reports.async_count }
    # Return to GraphQL::Dataloader:
    dataloader.yield
    # Now return the results, waiting if necessary:
    promises.map(&:value)
  end
end
```

Which could be used in a GraphQL field:

```ruby
field :direct_reports_count, Int

def direct_reports_count
  dataloader.with(Sources::DirectReportsCount).load(object)
end
```

## Example: Concurrent::Future

You could use `concurrent-ruby` to put work in a background thread. For example, using `Concurrent::Future`:

```ruby
class Sources::ExternalDataSource < GraphQL::Dataloader::Source
  def fetch(urls)
    # Start some I/O-intensive work:
    futures = urls.map do |url|
      Concurrent::Future.execute {
        # Somehow fetch and parse data:
        get_remote_json(url)
      }
    end
    # Yield back to GraphQL::Dataloader:
    dataloader.yield
    # Dataloader has done what it can,
    # so now return the value, waiting if necessary:
    futures.map(&:value)
  end
end
```

---

### Guides Dataloader Sources (`guides/dataloader/sources.md`)

# Sources

_Sources_ are what [GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) uses to fetch data from external services.

## Source Concepts

Sources are classes that inherit from `GraphQL::Dataloader::Source`. A Source _must_ implement `def fetch(keys)` to return a list of objects, one for each of the given keys. A source _may_ implement `def initialize(...)` to accept other batching parameters.

Sources will receive two kinds of inputs from `GraphQL::Dataloader`:

- _keys_, which correspond to objects requested by the application.

  Keys are passed to `def fetch(keys)`, which must return an object (or `nil`) for each of `keys`, in the same order as `keys`.

  Under the hood, each Source instance maintains a `key => object` cache.

- _batch parameters_, which are the basis of batched groups. For example, if you're loading records from different database tables, the table name would be a batch parameter.

  Batch parameters are given to `dataloader.with(source_class, *batch_parameters)`, and the default is _no batch parameters_. When you define a source, you should add the batch parameters to `def initialize(...)` and store them in instance variables.

  (`dataloader.with(source_class, *batch_parameters)` returns an instance of `source_class` with the given batch parameters -- but it might be an instance which was cached by `dataloader`.)

  Additionally, batch parameters are used to de-duplicate Source initializations during a query run. `.with(...)` calls that have the same batch parameters will use the same Source instance under the hood. To customize how Sources are de-duplicated, see [GraphQL::Dataloader::Source.batch_key_for](rdoc-ref:GraphQL::Dataloader::Source.batch_key_for).

## Example: Loading Strings from Redis by Key

The simplest source might fetch values based on their keys. For example:

```ruby
# app/graphql/sources/redis_string.rb
class Sources::RedisString < GraphQL::Dataloader::Source
  REDIS = Redis.new
  def fetch(keys)
    # Redis's `mget` will return a value for each key with a `nil` for any not-found key.
    REDIS.mget(*keys)
  end
end
```

This loader could be used in GraphQL like this:

```ruby
some_string = dataloader.with(Sources::RedisString).load("some_key")
```

Calls to `.load(key)` will be batched, and when `GraphQL::Dataloader` can't go any further, it will dispatch a call to `def fetch(keys)` above.

## Example: Loading ActiveRecord Objects by ID

To fetch ActiveRecord objects by ID, the source should also accept the _model class_ as a batching parameter. For example:

```ruby
# app/graphql/sources/active_record_object.rb
class Sources::ActiveRecordObject < GraphQL::Dataloader::Source
  def initialize(model_class)
    @model_class = model_class
  end

  def fetch(ids)
    records = @model_class.where(id: ids)
    # return a list with `nil` for any ID that wasn't found
    ids.map { |id| records.find { |r| r.id == id.to_i } }
  end
end
```

This source could be used for any `model_class`, for example:

```ruby
author = dataloader.with(Sources::ActiveRecordObject, ::User).load(1)
post = dataloader.with(Sources::ActiveRecordObject, ::Post).load(1)
```

## Example: Batched Calculations

Besides fetching objects, Sources can return values from batched calculations. For example, a system could batch up checks for who a user follows:

```ruby
# for a given user, batch checks to see whether this user follows another user.
# (The default `user.followings.where(followed_user_id: followed).exists?` would cause N+1 queries.)
class Sources::UserFollowingExists < GraphQL::Dataloader::Source
  def initialize(user)
    @user = user
  end

  def fetch(handles)
    # Prepare a `SELECT id FROM users WHERE handle IN(...)` statement
    user_ids = ::User.where(handle: handles).select(:id)
    # And use it to filter this user's followings:
    followings = @user.followings.where(followed_user_id: user_ids)
    # Now, for followings that _actually_ hit a user, get the handles for those users:
    followed_users = ::User.where(id: followings.select(:followed_user_id))
    # Finally, return a result set, with one entry (true or false) for each of the given `handles`
    handles.map { |h| !!followed_users.find { |u| u.handle == h }}
  end
end
```

It could be used like this:

```ruby
is_following = dataloader.with(Sources::UserFollowingExists, context[:viewer]).load(handle)
```

After all requests were batched, `#fetch` will return a Boolean result to `is_following`.

## Example: Loading in a background thread

Inside `Source#fetch(keys)`, you can call `dataloader.yield` to return control to the Dataloader. This way, it will proceed loading other Sources (if there are any), then return the source that yielded.

A simple example, spinning up a new Thread:

```ruby
def fetch(keys)
  # spin up some work in a background thread
  thread = Thread.new {
    fetch_external_data(keys)
  }
  # return control to the dataloader
  dataloader.yield
  # at this point,
  # the dataloader has tried everything else and come back to this source,
  # so block if necessary:
  thread.value
end
```

See the [parallelism guide](/dataloader/parallelism) for details about this approach.

## Filling the Dataloader Cache

If you load records from the database, you can use them to populate a source's cache by using [Dataloader::Source#merge](rdoc-ref:GraphQL::Dataloader::Source#merge). For example:

```ruby
# Build a `{ key => value }` map to populate the cache
comments_by_id = post.comments.each_with_object({}) { |comment, hash| hash[comment.id] = comment }
# Merge the map into the source's cache
dataloader.with(Sources::ActiveRecordObject, Comment).merge(comments_by_id)
```

After that, any calls to `.load(id)` will use those already-loaded records if they're available.

## De-duplicating equivalent objects

Sometimes, _different_ objects in the application should load the same object from `fetch`. You can customize this behavior by implementing `def result_key_for(key)` in your application. For example, to map records from your ORM to their database ID:

```ruby
# Load the `created_by` person for a record from our database
class CreatedBySource < GraphQL::Dataloader::Source
  def result_key_for(key)
    key.id # Use the record's ID to deduplicate different `.load` calls
  end

  # Fetch a `person` for each of `records`, based on their created_by_id
  def fetch(records)
    PersonService.find_each(records.map(&:created_by_id))
  end
end
```

In this case, `records` will include the _first_ object for each unique `record.id` -- subsequent records with the same `.id` will be assumed to be duplicates. Under the hood, the `Source` will cache the result based on the record's `id`.

Alternatively, you could use this to make the `Source` retain each incoming object, even when they would _otherwise_ be treated as duplicates. (This would come in handy when you need `def fetch` to mutate each object). For example, to treat _every_ incoming object as distinct:

```ruby
def result_key_for(record)
  record.object_id # even if the records are equivalent, handle each distinct Ruby object separately
end
```

---

### Guides Dataloader Testing (`guides/dataloader/testing.md`)

# Testing

There are a few techniques for testing your [GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) setup.

## Integration Tests

One important feature of `Dataloader` is how it manages database access while GraphQL runs queries. You can test that by listening for database queries while running queries, for example, with ActiveRecord:


```ruby
def test_active_record_queries_are_batched_and_cached
  # set up a listener function
  database_queries = 0
  callback = lambda {|_name, _started, _finished, _unique_id, _payload| database_queries += 1 }

  query_str = <<-GRAPHQL
  {
    a1: author(id: 1) { name }
    a2: author(id: 2) { name }
    b1: book(id: 1) { author { name } }
    b2: book(id: 2) { author { name } }
  }
  GRAPHQL

  # Run the query with the listener
  ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
    MySchema.execute(query_str)
  end

  # One query for authors, one query for books
  assert_equal 2, database_queries
end
```

You could also make specific assertions on the queries that are run (see the [`sql.active_record` docs](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#active-record)). For other frameworks and databases, check your ORM or library for instrumentation options.

## Testing Dataloader Sources

You can also test `Dataloader` behavior outside of GraphQL using [GraphQL::Dataloader.with_dataloading](rdoc-ref:GraphQL::Dataloader.with_dataloading). For example, let's say you have a `Sources::ActiveRecord` source defined like so:

```ruby

module Sources
  class User < GraphQL::Dataloader::Source
    def fetch(ids)
      records = User.where(id: ids)
      # return a list with `nil` for any ID that wasn't found, so the shape matches
      ids.map { |id| records.find { |r| r.id == id.to_i } }
    end
  end
end
```

You can test it like so:

```ruby
def test_it_fetches_objects_by_id
  user_1, user_2, user_3 = 3.times.map { User.create! }

  database_queries = 0
  callback = lambda {|_name, _started, _finished, _unique_id, _payload| database_queries += 1 }

  ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
    GraphQL::Dataloader.with_dataloading do |dataloader|
      req1 = dataloader.with(Sources::ActiveRecord).request(user_1.id)
      req2 = dataloader.with(Sources::ActiveRecord).request(user_2.id)
      req3 = dataloader.with(Sources::ActiveRecord).request(user_3.id)
      req4 = dataloader.with(Sources::ActiveRecord).request(-1)

      # Validate source's matching up of records
      expect(req1.load).to eq(user_1)
      expect(req2.load).to eq(user_2)
      expect(req3.load).to eq(user_3)
      expect(req4.load).to be_nil
    end
  end

  assert_equal 1, database_queries, "All users were looked up at once"
end
```

---

### Guides Defer Graphiql (`guides/defer/graphiql.md`)

# Use with GraphiQL

You can use `@defer` and `@stream` with [GraphiQL](https://github.com/graphql/graphiql/blob/main/packages/graphiql/README.md), an in-browser IDE.

<img src="/defer/defer-graphiql-gif.gif"  alt="Using @defer with GraphiQL" style="max-width: 100%" />

## Incremental responses

If you're using the proposed `incremental: ...` response syntax ([proposal](https://github.com/graphql/graphql-spec/pull/742), [Ruby support](/defer/setup.html#example-rails-with-apollo-client)), you'll need a custom "fetcher" function to handle the `incremental: ...` part of the response. For example:

```js
import { meros } from "meros"; // for handling multipart responses

const customFetcher = async function* (graphqlParams, fetcherOpts) {
  // Make the initial fetch
  var result = await fetch("/graphql", {
    method: "POST",
    body: JSON.stringify(graphqlParams),
    headers: {
      'content-type': 'application/json',
    }
  }).then((r) => {
    // Use meros to turn multipart responses into streams
    return meros(r, { multiple: true })
  })

  if (!isAsyncIterable(result)) {
    // Return plain responses as promises
    return result.json()
  } else {
    // Handle multipart responses one chunk at a time
    for await (const chunk of result) {
      yield chunk.map(part => {
        // Move the incremental part of the response into top-level
        // This assumes there's only one `incremental` entry
        // which is currently true for GraphQL-Pro's @defer implementation
        var newJson = {...part.body}
        if (newJson.incremental) {
          newJson.data = newJson.incremental[0].data
          newJson.path = newJson.incremental[0].path
          delete newJson.incremental
        }
        return newJson
      });
    }
  }
}

// Helper for checking for a multipart response:
function isAsyncIterable(input) {
  return (
      typeof input === "object" &&
      input !== null &&
      (
        input[Symbol.toStringTag] === "AsyncGenerator" ||
        (Symbol.asyncIterator && Symbol.asyncIterator in input)
      )
    );
}

```

Hopefully a new GraphiQL version will support this out of the box; follow the [issue on GitHub](https://github.com/graphql/graphiql/issues/3470).

---

### Guides Defer Setup (`guides/defer/setup.md`)

# Server Setup

Before using `@defer` in queries, you have to:

- Update `graphql` and `graphql-pro` gems
- Add `@defer` to your GraphQL schema
- Update your HTTP handlers (eg, Rails controllers) to send streaming responses
- Optionally, customize `@defer` to work with GraphQL-Batch

You can also see a [full Rails & Apollo-Client demo](https://github.com/rmosolgo/graphql_defer_example).

## Updating the gems

GraphQL-Ruby 1.9+ and GraphQL-Pro 1.10+ are required:

```ruby
gem "graphql", "~>1.9"
gem "graphql-pro", "~>1.10"
```

And then install them:

```
$ bundle update graphql graphql-pro
```

## Adding `@defer` to your schema

Then, add `GraphQL::Pro::Defer` to your schema as a plugin:

```ruby
class MySchema < GraphQL::Schema
  use GraphQL::Pro::Defer
end
```

This will:

- Attach a [custom directive](/type_definitions/directives) called `@defer`
- Add instrumentation to queries to track deferred work and execute it later

## Sending streaming responses

Many web frameworks have support for streaming responses, for example:

- Rails has [ActionController::Live](https://api.rubyonrails.org/classes/ActionController/Live.html)
- Sinatra has [Sinatra::Streaming](http://sinatrarb.com/contrib/streaming.html)
- Hanami::Controller can [stream responses](https://github.com/hanami/controller#streamed-responses)

See below for how to integrate GraphQL's deferred patches with a streaming response API.

To investigate support with a web framework, please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=Server+support+for+%40defer+with+...&body=) or email `support@graphql.pro`.

### Checking for deferrals

When a query has any `@defer`ed fields, you can check for `context[:defer]`:

```ruby
if context[:defer]
  # some fields were `@defer`ed
else
  # normal GraphQL, no `@defer`
end
```

### Working with deferrals

To handle deferrals, you can enumerate over `context[:defer]`, for example:

```ruby
context[:defer].each do |deferral|
  # do something with the `deferral`, eg
  # stream_to_client(deferral.to_h)
end
```

The initial result is _also_ present in the deferrals, so you can treat it just like a patch.

Each deferred patch has a few methods for building a response:

- `.to_h` returns a hash with `path:`, `data:`, and/or `errors:`. (There is no `path:` for the root result.)
- `.to_http_multipart(incremental: true)` returns a string which works with Apollo client's `@defer` support. (Use `incremental: true` to format patches for the forthcoming spec.)
- `.path` returns the path to this patch in the response
- `.data` returns successfully-resolved results of the patch
- `.errors` returns an array of errors, if there were any

Calling `.data` or `.errors` on a deferral will resume GraphQL execution until the patch is complete.

### Example: Rails with Apollo Client

In this example, a Rails controller will stream HTTP Multipart patches to the client, in Apollo Client's supported format.

```ruby
class GraphqlController < ApplicationController
  # Support `response.stream` below:
  include ActionController::Live

  def execute
    # ...
    result = MySchema.execute(query, variables: variables, context: context, operation_name: operation_name)

    # Check if this is a deferred query:
    if (deferred = result.context[:defer])
      # Required for Rack 2.2+, see https://github.com/rack/rack/issues/1619
      response.headers['Last-Modified'] = Time.now.httpdate
      # Use built-in `stream_http_multipart` with Apollo-Client & ActionController::Live
      deferred.stream_http_multipart(response, incremental: true)
    else
      # Return a plain, non-deferred result
      render json: result
    end
  ensure
    # Always make sure to close the stream
    response.stream.close
  end
end
```

You can also investigate a [full Rails & Apollo-Client demo](https://github.com/rmosolgo/graphql_defer_example)

## With GraphQL-Batch

`GraphQL-Batch` is a third-party data loading library that wraps GraphQL-Ruby execution. Deferred resolution happens outside the normal execution flow, so to work with GraphQL-Batch, you have to customize `GraphQL::Pro::Defer` a bit. Also, you'll need GraphQL-Pro `v1.24.6` or later. Here's a custom `Defer` implementation:

```ruby
# app/graphql/directives/defer.rb
module Directives
  # Modify the library's `@defer` implementation to work with GraphQL-Batch
  class Defer < GraphQL::Pro::Defer
    def self.resolve(obj, arguments, context, &block)
      # While the query is running, store the batch executor to re-use later
      context[:graphql_batch_executor] ||= GraphQL::Batch::Executor.current
      super
    end

    class Deferral < GraphQL::Pro::Defer::Deferral
      def resolve
        # Before calling the deferred execution,
        # set GraphQL-Batch back up:
        prev_executor = GraphQL::Batch::Executor.current
        GraphQL::Batch::Executor.current ||= @context[:graphql_batch_executor]
        super
      ensure
        # Clean up afterward:
        GraphQL::Batch::Executor.current = prev_executor
      end
    end
  end
end
```

And update your schema to use your custom defer implementation:

```ruby
# Use our GraphQL-Batch-compatible defer:
use Directives::Defer
```
## Next Steps

Read about [client usage](/defer/usage) of `@defer`.

---

### Guides Defer Stream (`guides/defer/stream.md`)

# Stream

`@stream` works very much like `@defer`, except it only applies to list fields. When a field has `@stream` and it returns a list, then each item in the list is returned to the client as a patch. `@stream` is described in a [proposal to the GraphQL specification](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md).

__Note:__ `@stream` was added in GraphQL-Pro 1.21.0 and requires GraphQL-Ruby 1.13.6+.

## Installation

To support `@stream` in your schema, add it with `use GraphQL::Pro::Stream`:

```ruby
class MySchema < GraphQL::Schema
  # ...
  use GraphQL::Pro::Stream
end
```

Additionally, you should update your controller to handle deferred parts of the response. See the [@defer setup guide](/defer/setup#sending-streaming-responses) for details. (`@stream` uses the same deferral pipeline as `@defer`, so the same setup instructions apply.)

## Usage

After that, you can include `@stream` in your queries, for example:

```ruby
{
  # Send each movie in its own patch:
  nowPlaying @stream {
    title
    director { name }
  }
}
```

If `@stream` is applied to non-list fields, it's ignored.

`@stream` supports several arguments:

- `if: Boolean = true`: when `false`, the list is _not_ streamed. Instead, all items are returned synchronously.
- `label: String`: if present, the given string is returned in patches as `"label": "..."`
- `initialCount: Int = 0`: this number of list items are returned synchronously. (If the list is shorter than `initialCount`, then the whole list is returned synchronously.)

---

### Guides Defer Usage (`guides/defer/usage.md`)

# Usage

`@defer` is a [GraphQL directive](https://graphql.org/learn/queries/#directives) which instructs the server to execute the field in a special way:

```graphql
query GetPlayerInfo($handle: String!){
  player(handle: $handle) {
    name
    # Send this field later, to avoid slowing down the initial response:
    topScore(from: 2000, to: 2020) @defer
  }
}
```

The directives `@skip` and `@include` are built into any GraphQL server and client, but `@defer` requires special attention.

Apollo-Client [currently supports the @defer directive](https://www.apollographql.com/docs/react/data/defer/).

`@defer` also accepts a `label:` option which will be included in outgoing patches when it's present in the query (eg, `@defer(label: "patch1")`).

Want to use `@defer` with another client? Please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=Client+support+for+%40defer+with+...&body=) or email `support@graphql.pro` and we'll dig in!

## Next Steps

[Set up your server](/defer/setup) to support `@defer`.

---

### Guides Development Development (`guides/development/development.md`)

# Development

So, you want to hack on GraphQL Ruby! Here are some tips for getting started.

- [Setup your development environment](#setup)
- [Run the tests to verify your setup](#running-the-tests)
- [Debug with pry](#debugging-with-pry)
- [Run the benchmarks to test performance in your environment](#running-the-benchmarks)
- [Coding guidelines for working on your contribution](#coding-guidelines)
- Special tools for building the lexer and parser
- [Building and publishing the GraphQL Ruby website](#website)
- [Versioning describes how changes are managed and released](#versioning)
- [Releasing Gem versions](#releasing)

## Setup

Get your own copy of graphql-ruby by forking [`rmosolgo/graphql-ruby` on GitHub](https://github.com/rmosolgo/graphql-ruby) and cloning your fork.

Then, install the dependencies:

- Install SQLite3 and MongoDB (eg, `brew install sqlite && brew tap mongodb/brew && brew install mongodb-community`)
- `bundle install`
- `rake compile # If you get warnings at this step, you can ignore them.`
- Optional: [Ragel](https://www.colm.net/open-source/ragel/) is required to build the lexer

## Running the Tests

### Unit tests

You can run the tests with

```
bundle exec rake        # tests & Rubocop
bundle exec rake test   # tests only
```

You can run a __specific file__ with `TEST=`:

```
bundle exec rake test TEST=spec/graphql/query_spec.rb
# run tests in `query_spec.rb` only
```

You can focus on a __specific example__ with `focus`:

```ruby
focus
it "does something cool" do
  # ...
end
```

Then, only `focus`ed tests will run:

```
bundle exec rake test
# only the focused test will be run
```

(This is provided by `minitest-focus`.)

### Integration tests

You need to pick a specific gemfile from gemfiles/ to run integration tests. For example:

```
BUNDLE_GEMFILE=gemfiles/rails_6.1.gemfile bundle install
BUNDLE_GEMFILE=gemfiles/rails_6.1.gemfile bundle exec rake test TEST=spec/integration/rails/graphql/relay/array_connection_spec.rb
```

### GraphQL-CParser tests

To test the `graphql_cparser` gem, you have to build the binary first:

```
bundle exec rake build_ext
```

Then, run the test suite with `GRAPHQL_CPARSER=1`:

```
GRAPHQL_CPARSER=1 bundle exec rake test
```

(Add `TEST=` to pick a certain file.)


### Other tests

There are system tests for checking ActionCable behavior, use:

```
bundle exec rake test:system
```

And JavaScript tests:

```
bundle exec rake test:js
```

## Gemfiles, Gemfiles, Gemfiles

`graphql-ruby` has several gemfiles to ensure support for various Rails versions. You can specify a gemfile with `BUNDLE_GEMFILE`, eg:

```
BUNDLE_GEMFILE=gemfiles/rails_5.gemfile bundle exec rake test
```

## Debugging with Pry

[`pry`](https://pry.github.io/) is included with GraphQL-Ruby's development setup to help with debugging.

To pause execution in Ruby code, add:

```ruby
binding.pry
```

Then, the program will pause and your terminal will become a Ruby REPL. Feel free to use `pry` in your development process!

## Running the Benchmarks

This project includes some Rake tasks to record benchmarks:

```sh
$ bundle exec rake -T | grep bench:
rake bench:profile         # Generate a profile of the introspection query
rake bench:query           # Benchmark the introspection query
rake bench:validate        # Benchmark validation of several queries
```

You can save results by sending the output into a file:

```sh
$ bundle exec rake bench:validate > before.txt
$ cat before.txt
# ...
# --> benchmark output here
```

If you want to check performance, create a baseline by running these tasks before your changes. Then, make your changes and run the tasks again and compare your results.

Keep these points in mind when using benchmarks:

- The results are hardware-specific: computers with different hardware will have different results. So don't compare your results to results from other computers.
- The results are environment-specific: CPU and memory availability are affected by other processes on your computer. So try to create similar environments for your before-and-after testing.

## Coding Guidelines

GraphQL-Ruby uses a thorough test suite to make sure things work reliably day-after-day. Please include tests that describe your changes, for example:

- If you contribute a bug fix, include a test for the code that _was_ broken (and is now fixed)
- If you contribute a feature, include tests for all intended uses of that feature
- If you modify existing behavior, update the tests to cover all intended behaviors for that code

Don't fret about coding style or organization.  There's a minimal Rubocop config in `.rubocop.yml` which runs during CI. You can run it manually with `bundle exec rake rubocop`.

## Website

To update the website, update the `.md` files in `guides/`.

Install the optional documentation dependencies and build the site locally:

```sh
BUNDLE_WITH=docs bundle install
bundle exec rake docs:build
bundle exec rake docs:rdoc:serve
```

Then visit `http://127.0.0.1:8808`. Run the documentation checks before submitting a change:

```sh
bundle exec rake docs:check
bundle exec rake docs:build_twice
```

See the [documentation maintenance guide](/development/documentation_maintenance) for API links, GraphQL code blocks, redirects, and release documentation.

### API Docs

The GraphQL-Ruby website has a rendered version of each published gem's API docs. Generate a local copy with:

```
$ bundle exec rake "docs:rdoc:build_version[1.8.0]"
```

The output is written to `tmp/rdoc-api/1.8.0/`; the release workflow publishes it under `/api-doc/1.8.0/` while preserving older versions.

## Versioning

GraphQL-Ruby does _not_ attempt to deliver "semantic versioning" for the reasons described in `jashkenas`'
s post, ["Why Semantic Versioning Isn't"](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e). Instead, the following scheme is used as a guideline:

- Version numbers consist of three parts, `MAJOR.MINOR.PATCH`
- __`PATCH`__ version indicates bug fixes or small features for specific use cases. Ideally, you can upgrade patch versions with only a quick skim of the changelog.
- __`MINOR`__ version indicates significant additions, internal refactors, or small breaking changes. When upgrading a minor version, check the changelog for any new features or breaking changes which apply to your system. The changelog will always include an upgrade path for any breaking changes. Minor versions may also include deprecation warnings to warn about upcoming breaking changes.
- __`MAJOR`__ version indicates significant breaking changes. Do not expect code to run without some modification, especially if the code yielded deprecation warnings.

This policy is inspired by the [Ruby 2.1.0+ version policy](https://www.ruby-lang.org/en/news/2013/12/21/ruby-version-policy-changes-with-2-1-0/).

Pull requests and issues may be tagged with a [GitHub milestone](https://github.com/rmosolgo/graphql-ruby/milestones) to denote when they'll be released.

The [changelog](https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG.md) should always contain accurate and thorough information so that users can upgrade. If you have trouble upgrading based on the changelog, please open an issue on GitHub.

## Releasing

GraphQL-Ruby doesn't have a strict release schedule. If you think it should, consider opening an issue to share your thoughts.

To cut a release:

- Update `CHANGELOG.md` for the new version:
  - Add a new heading for the new version, and paste the four categories of changes into the new section
  - Open the GitHub milestone corresponding to the new version
  - Check each pull request and put it in the category (or categories) that it belongs in
    - If a change affects the default behavior of GraphQL-Ruby in a disruptive way, add it to `## Breaking Changes` and include migration notes if possible
    - Include the PR number beside the change description for future reference
- Update `lib/graphql/version.rb` with the new version number
- Commit changes to master
- Push changes to GitHub: `git push origin master`. GitHub Actions will update the website.
- Release to RubyGems: `bundle exec rake release`. This will also push the tag to GitHub which will kick off a GitHub Actions job to update the API docs.
- Celebrate 🎊  !

---

### Guides Development Documentation Maintenance (`guides/development/documentation_maintenance.md`)

# Documentation maintenance

GraphQL-Ruby documentation is generated with RDoc and the Aliki generator. API comments live beside the Ruby code, while tutorials and cross-cutting explanations remain Markdown pages in `guides/`.

Install the optional documentation dependencies and build a local site with:

```sh
BUNDLE_WITH=docs bundle install
bundle exec rake docs:build
```

The generated site is written to `tmp/rdoc-site`. To preview it, run `bundle exec rake docs:rdoc:serve` and open `http://127.0.0.1:8808`.

When adding or moving a guide, update `docs/guide_metadata.yml`; it controls sidebar order and the GraphQL-Pro and GraphQL-Enterprise callouts.

Run the documentation checks before submitting a change:

```sh
bundle exec rake docs:check
bundle exec rake docs:build_twice
```

Use `rdoc-ref:GraphQL::Schema#execute` (or the corresponding class-method form) for links from comments and guides. Add a short API comment when a public method is missing from the generated index. Dynamic methods should use RDoc directives such as `:method:` and `:call-seq:` rather than introducing new YARD tags.

Use a fenced `graphql` block for GraphQL examples:

````markdown
```graphql
query Viewer {
  viewer { id }
}
```
````

When a page or API moves, add an entry to `docs/redirects.yml` so the old URL remains usable. Run `bundle exec ruby tool/docs/link_checker.rb --root tmp/rdoc-site --strict` to verify the redirect and its fragment.

Release builds use `bundle exec rake "docs:rdoc:build_version[VERSION]"` to generate a versioned API site under `tmp/rdoc-api/VERSION`. The publish workflow copies that directory into `gh-pages/api-doc/VERSION` without removing older versions.

---

### Guides Development Faq (`guides/development/faq.md`)

# FAQ

Returning Route URLs
====================
With GraphQL there is less of a need to include resource URLs to other REST resources, however sometimes you want to use Rails routing to include a URL as one of your fields. A common use case would be to build HTML format URLs to render a link in your React UI. In that case you can pass the request to your context, so that the helpers are able to build full URLs based on the incoming host, port and protocol.

Example
-------
```ruby
class Types::UserType < Types::BaseObject
  include ActionController::UrlFor
  include Rails.application.routes.url_helpers
  # Needed by ActionController::UrlFor to extract the host, port, protocol etc. from the current request
  def request
    context[:request]
  end
  # Needed by Rails.application.routes.url_helpers, it will then use the url_options defined by ActionController::UrlFor
  def default_url_options
    {}
  end
  
  field :profile_url, String, null: false
  def profile_url
    user_url(object)
  end
end

# In your GraphQL controller, add the request to `context`:
MySchema.execute(
  params[:query],
  variables: params[:variables],
  context: {
    request: request
  },
)
```

Returning ActiveStorage blob URLs
=================================
If you are using ActiveStorage and need to return a URL to an attachment blob, you will find that using `Rails.application.routes.url_helpers.rails_blob_url` alone will throw an exception since Rails won't know what host, port or protocol to use in it.
You can include `ActiveStorage::SetCurrent` in your GraphQL controller to pass on this information into your resolvers.

Example
=======

```ruby
class GraphqlController < ApplicationController
  include ActiveStorage::SetCurrent
  ...
end

class Types::UserType < Types::BaseObject
  field :picture_url, String, null: false
  def picture_url
    Rails.application.routes.url_helpers.rails_blob_url(
      object.picture,
      protocol: ActiveStorage::Current.url_options[:protocol],
      host: ActiveStorage::Current.url_options[:host],
      port: ActiveStorage::Current.url_options[:port]
    )
  end
end
```

---

### Guides Development Getting Started (`guides/development/getting_started.md`)

# Getting Started

## Installation

You can install `graphql` from RubyGems by adding to your application's `Gemfile`:

```ruby
# Gemfile
gem "graphql"
```

Then, running `bundle install`:

```sh
$ bundle install
```

## Getting Started

On Rails, you can get started with a few [GraphQL generators](https://rmosolgo.github.io/graphql-ruby/schema/generators#graphqlinstall):

```sh
# Add graphql-ruby boilerplate and mount graphiql in development
$ rails g graphql:install
# You may need to run bundle install again, as by default graphiql-rails is added on installation.
$ bundle install
# Make your first object type
$ rails g graphql:object Post title:String rating:Int comments:[Comment]
```

Or, you can build a GraphQL server by hand:

- Define some types
- Connect them to a schema
- Execute queries with your schema

### Declare types

Types describe objects in your application and form the basis for [GraphQL's type system](https://graphql.org/learn/schema/#type-system).

```ruby
# app/graphql/types/post_type.rb
module Types
  class PostType < Types::BaseObject
    description "A blog post"
    field :id, ID, null: false
    field :title, String, null: false
    # fields should be queried in camel-case (this will be `truncatedPreview`)
    field :truncated_preview, String, null: false
    # Fields can return lists of other objects:
    field :comments, [Types::CommentType],
      # And fields can have their own descriptions:
      description: "This post's comments, or null if this post has comments disabled."
  end
end

# app/graphql/types/comment_type.rb
module Types
  class CommentType < Types::BaseObject
    field :id, ID, null: false
    field :post, PostType, null: false
  end
end
```

### Build a Schema

Before building a schema, you have to define an [entry point to your system, the "query root"](https://graphql.org/learn/schema/#the-query-mutation-and-subscription-types):

```ruby
class QueryType < GraphQL::Schema::Object
  description "The query root of this schema"

  field :post, resolver: Resolvers::PostResolver
end
```

Define how this field is resolved by creating a resolver class:

```ruby
# app/graphql/resolvers/post_resolver.rb
module Resolvers
  class PostResolver < BaseResolver
    type Types::PostType, null: false
    argument :id, ID

    def resolve(id:)
      ::Post.find(id)
    end
  end
end
```

Then, build a schema with `QueryType` as the query entry point:

```ruby
class Schema < GraphQL::Schema
  query Types::QueryType
end
```

This schema is ready to serve GraphQL queries! [Browse the guides](/guides) to learn about other GraphQL Ruby features.

### Execute queries

You can execute queries from a query string:

```ruby
query_string = "
{
  post(id: 1) {
    id
    title
    truncatedPreview
  }
}"
result_hash = Schema.execute(query_string)
# {
#   "data" => {
#     "post" => {
#        "id" => 1,
#        "title" => "GraphQL is nice"
#        "truncatedPreview" => "GraphQL is..."
#     }
#   }
# }
```

See [Executing Queries](/queries/executing_queries) for more information about running queries on your schema.

## Use with Relay

If you're building a backend for [Relay](https://facebook.github.io/relay/), you'll need:

- A JSON dump of the schema, which you can get by sending [`GraphQL::Introspection::INTROSPECTION_QUERY`](https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/introspection/introspection_query.rb)
- Relay-specific helpers for GraphQL, see the [Connection guide](/pagination/connection_concepts), [Mutation guide](/mutations/mutation_classes), and [Object Identification guide](/schema/object_identification).

## Use with Apollo Client

[Apollo Client](https://www.apollographql.com/) is a full featured, simple to use GraphQL client with convenient integrations for popular view layers. You don't need to do anything special to connect Apollo Client to a `graphql-ruby` server.

## Use with GraphQL.js Client

[GraphQL.js Client](https://github.com/f/graphql.js) is a tiny client that is platform- and framework-agnostic. It works well with `graphql-ruby` servers, since GraphQL requests are simple query strings transported over HTTP.

---

### Guides Development Related Projects (`guides/development/related_projects.md`)

# Related Projects

Want to add something? Please open a pull request [on GitHub](https://github.com/rmosolgo/graphql-ruby)!

## Code

- `graphql-ruby` + Rails demo ([src](https://github.com/rmosolgo/graphql-ruby-demo) / [heroku](https://graphql-ruby-demo.herokuapp.com))
- `graphql-ruby` + Sinatra demo ([src](https://github.com/robinjmurphy/ruby-graphql-server-example) / [heroku](https://ruby-graphql-server-example.herokuapp.com/))
- [`graphql-batch`](https://github.com/shopify/graphql-batch), a batched query execution strategy
- [`graphql-cache`](https://github.com/stackshareio/graphql-cache), a resolver-level caching solution
- [`graphql-devise`](https://github.com/graphql-devise/graphql_devise), a gql interface to handle authentication with Devise
- [`graphql-docs`](https://github.com/gjtorikian/graphql-docs), a tool to automatically generate static HTML documentation from your GraphQL implementation
- [`graphql-metrics`](https://github.com/Shopify/graphql-metrics), a plugin to extract fine-grain metrics of GraphQL queries received by your server
- [`graphql-stitching`](https://github.com/gmac/graphql-stitching-ruby), tools to combine multiple local and remote schemas into a single graph that queries as one
- [`graphql-groups`](https://github.com/hschne/graphql-groups), a DSL to define group- and aggregation queries with graphql-ruby
- Rails Helpers:
  - [`graphql-activerecord`](https://github.com/goco-inc/graphql-activerecord)
  - [`graphql-rails-resolve`](https://github.com/colepatrickturner/graphql-rails-resolver)
  - [`graphql-query-resolver`](https://github.com/nettofarah/graphql-query-resolver), a graphql-ruby add-on to minimize N+1 queries.
  - [`graphql-rails_logger`](https://github.com/jetruby/graphql-rails_logger), a logger which allows you to inspect GraphQL queries in a more readable format.
  - [`apollo_upload_server-ruby`](https://github.com/jetruby/apollo_upload_server-ruby), a middleware which allows you to upload files with GraphQL and multipart/form-data using [`apollo-upload-client`](https://github.com/jaydenseric/apollo-upload-client) library on front-end.
  - [`graphql-sources`](https://github.com/ksylvest/graphql-sources) a collection of common GraphQL [sources](https://graphql-ruby.org/dataloader/sources.html) to simplify using `ActiveRecord`, `ActiveStorage`, `Rails.cache`, and more.
  - [`graphql-filters`](https://github.com/moku-io/graphql-filters), a DSL to define fully typed filters for list fields.
- [`search_object_graphql`](https://github.com/rstankov/SearchObjectGraphQL), a DSL for defining search resolvers for GraphQL.
- [`action_policy-graphql`](https://github.com/palkan/action_policy-graphql), an integration for using [`action_policy`](https://github.com/palkan/action_policy) as an authorization framework for GraphQL applications.
- [`graphql_rails`](https://github.com/samesystem/graphql_rails), Rails way GraphQL build tool
- [`graphql-rails-generators`](https://github.com/ajsharp/graphql-rails-generators), Generate graphql-ruby mutations, types and input types from your ActiveRecord models.
- [`graphql-ruby-fragment_cache`](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache), a tool for caching response fragments.
- [`graphql-ruby-persisted_queries`](https://github.com/DmitryTsepelev/graphql-ruby-persisted_queries), the implementation of [Apollo persisted queries](https://github.com/apollographql/apollo-link-persisted-queries).
- [`rubocop-graphql`](https://github.com/DmitryTsepelev/rubocop-graphql), [rubocop](https://github.com/rubocop-hq/rubocop) extension for enforcing best practices.
- [`apollo-federation-ruby`](https://github.com/Gusto/apollo-federation-ruby), a Ruby implementation of the Apollo Federation [subgraph spec](https://www.apollographql.com/docs/federation/subgraph-spec/).

## Blog Posts

-  Building a blog in GraphQL and Relay on Rails [Introduction](https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-getting-started-955a49d251de), [Part 1]( https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-creating-types-and-schema-b3f9b232ccfc), [Part 2](https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-first-relay-powered-react-component-cb3f9ee95eca)
- https://medium.com/@khor/relay-facebook-on-rails-8b4af2057152
- https://blog.jacobwgillespie.com/from-rest-to-graphql-b4e95e94c26b#.4cjtklrwt
- https://jonsimpson.ca/parallel-graphql-resolvers-with-futures/
- Active Storage meets GraphQL: [Direct uploads](https://evilmartians.com/chronicles/active-storage-meets-graphql-direct-uploads) and [Exposing attachment URLs](https://evilmartians.com/chronicles/active-storage-meets-graphql-pt-2-exposing-attachment-urls)
- [Exposing permissions in GraphQL APIs with Action Policy](https://evilmartians.com/chronicles/exposing-permissions-in-graphql-apis-with-action-policy)
- [Reporting non-nullable violations in graphql-ruby properly](https://evilmartians.com/chronicles/reporting-non-nullable-violations-in-graphql-ruby-properly)
- [How to GraphQL with Ruby, Rails, Active Record, and no N+1](https://evilmartians.com/chronicles/how-to-graphql-with-ruby-rails-active-record-and-no-n-plus-one)

## Screencasts

- [GraphQL Basics in Rails 5](https://rubyplus.com/episodes/271-GraphQL-Basics-in-Rails-5)

## Presentations
- [Rescuing Legacy Codebases with GraphQL](https://speakerdeck.com/nettofarah/rescuing-legacy-codebases-with-graphql-1) by [@nettofarah](https://twitter.com/nettofarah)

## Tutorials
- [How To GraphQL](https://www.howtographql.com/graphql-ruby/0-introduction/) by [@rstankov](https://github.com/rstankov)

- [GraphQL Ruby CRUD Tutorial](https://www.blook.pub/books/graphql-rails-tutorial) by [@kohheepeace](https://twitter.com/kohheepeace)

- Rails/GraphQL + React/Apollo Tutorial ([Part 1](https://evilmartians.com/chronicles/graphql-on-rails-1-from-zero-to-the-first-query), [Part 2](https://evilmartians.com/chronicles/graphql-on-rails-2-updating-the-data), [Part 3](https://evilmartians.com/chronicles/graphql-on-rails-3-on-the-way-to-perfection)) by [@evilmartians](https://twitter.com/evilmartians)

---

### Guides Errors Error Handling (`guides/errors/error_handling.md`)

# Error Handling

You can configure your schema to rescue application errors during field resolution. Errors during batch loading will also be rescued.

Thanks to [`@exAspArk`](https://github.com/exaspark) for the [`graphql-errors`](https://github.com/exAspArk/graphql-errors) gem which inspired this behavior and [`@thiago-sydow`](https://github.com/thiago-sydow) who [suggested](https://github.com/rmosolgo/graphql-ruby/issues/2139#issuecomment-524913594) an implementation like this.

## Add error handlers

Handlers are added with `rescue_from` configurations in the schema:

```ruby
class MySchema < GraphQL::Schema
  # ...

  rescue_from(ActiveRecord::RecordNotFound) do |err, obj, args, ctx, field|
    # Raise a graphql-friendly error with a custom message
    raise GraphQL::ExecutionError, "#{field.type.unwrap.graphql_name} not found"
  end

  rescue_from(SearchIndex::UnavailableError) do |err, obj, args, ctx, field|
    # Log the error
    Bugsnag.notify(err)
    # replace it with nil
    nil
  end
end
```

The handler is called with several arguments:

- __`err`__ is the error that was raised during field execution, then rescued
- __`obj`__ is the object which was having a field resolved against it
- __`args`__ is the Hash of arguments passed to the resolver
- __`ctx`__ is the query context
- __`field`__ is the [GraphQL::Schema::Field](rdoc-ref:GraphQL::Schema::Field) instance for the field where the error was rescued

Inside the handler, you can:

- Raise a GraphQL-friendly [GraphQL::ExecutionError](rdoc-ref:GraphQL::ExecutionError) to return to the user
- Re-raise the given `err` to crash the query and halt execution. (The error will propagate to your application, eg, the controller.)
- Report some metrics from the error, if applicable
- Return a new value to be used for the error case (if not raising another error)

---

### Guides Errors Execution Errors (`guides/errors/execution_errors.md`)

The GraphQL specification [allows for a top-level `"errors"` key](https://graphql.github.io/graphql-spec/June2018/#sec-Errors) in the response which may contain information about what went wrong during execution. For example:

```ruby
{
  "errors" => [ ... ]
}
```

The response may include _both_ `"data"` and `"errors"` in the case of a partial success:

```ruby
{
  "data" => { ... } # parts of the query that ran successfully
  "errors" => [ ... ] # errors that prevented some parts of the query from running
}
```

## When to Use Top-Level Errors

In general, top-level errors should only be used for exceptional circumstances when a developer should be made aware that the system had some kind of problem.

For example, the GraphQL specification says that when a non-null field returns `nil`, an error should be added to the `"errors"` key. This kind of error is not recoverable by the client. Instead, something on the server should be fixed to handle this case.

When you want to notify a client some kind of recoverable issue, consider making error messages part of the schema, for example, as in [mutation errors](/mutations/mutation_errors).

## Adding Errors to the Array

In GraphQL-Ruby, you can add entries to this array by raising `GraphQL::ExecutionError` (or a subclass of it), for example:

```ruby
raise GraphQL::ExecutionError, "Can't continue with this query"
```

When this error is raised, its `message` will be added to the `"errors"` key and GraphQL-Ruby will automatically add the `line`, `column` and `path` to it. So, the above error might be:

```ruby
{
  "errors" => [
    {
      "message" => "Can't continue with this query",
      "locations" => [
        {
          "line" => 2,
          "column" => 10,
        }
      ],
      "path" => ["user", "login"],
    }
  ]
}
```

## Customizing Error JSON

The default error JSON includes `"message"`, `"locations"` and `"path"`. The [forthcoming version](https://spec.graphql.org/draft/#example-fce18) of the GraphQL spec recommends putting custom data in the `"extensions"` key of the error JSON.

You can customize this in two ways:

- Pass `extensions:` when raising an error, for example:
  ```ruby
  raise GraphQL::ExecutionError.new("Something went wrong", extensions: { "code" => "BROKEN" })
  ```
  In this case, `"extensions" => { "code" => "BROKEN" }` will be added to the error JSON.

- Override `#to_h` in a subclass of `GraphQL::ExecutionError`, for example:
  ```ruby
  class ServiceUnavailableError < GraphQL::ExecutionError
    def to_h
      super.merge({ "extensions" => {"code" => "SERVICE_UNAVAILABLE"} })
    end
  end
  ```
  Now, `"extensions" => { "code" => "SERVICE_UNAVAILABLE" }` will be added to the error JSON.

---

### Guides Errors Type Errors (`guides/errors/type_errors.md`)

# Type Errors

The GraphQL specification _requires_ certain assumptions to hold true when executing a query. However, it's possible that some code would violate that assumption, resulting in a type error.

Here are two type errors that you can customize in GraphQL-Ruby:

- A field with `null: false` returned `nil`
- A field returned a value as a union or interface, but that value couldn't be resolved to a member of that union or interface.

You can specify behavior in these cases by defining a [Schema.type_error](rdoc-ref:GraphQL::Schema.type_error) hook:

```ruby
class MySchema < GraphQL::Schema
  def self.type_error(err, query_ctx)
    # Handle a failed runtime type coercion
  end
end
```

It is called with an instance of [GraphQL::UnresolvedTypeError](rdoc-ref:GraphQL::UnresolvedTypeError) or [GraphQL::InvalidNullError](rdoc-ref:GraphQL::InvalidNullError) and the query context (a [GraphQL::Query::Context](rdoc-ref:GraphQL::Query::Context)).

If you don't specify a hook, you get the default behavior:

- Unexpected `nil`s add an error the response's `"errors"` key
- Unresolved Union / Interface types raise [GraphQL::UnresolvedTypeError](rdoc-ref:GraphQL::UnresolvedTypeError)

An object that fails type resolution is treated as `nil`.

---

### Guides Execution Migration (`guides/execution/migration.md`)

# Migrating to Execution::Next

This guide includes tips for migrating your schema configuration and production traffic to the new engine.

## Migration Philosophy

`Execution::Next` is designed to run alongside the previous engine so that the same schema can run queries _both_ ways. This supports an incremental migration and live toggling in production.

First, update your schema to include the necessary [field configurations](/execution/next#field-configurations). If you implement new class methods in your Object type classes, you can also migrate instance methods to call "up" to those class methods, preserving a single source of truth:

```ruby
field :unpublished_posts, [Types::Post], resolve_each: true

# Support batching:
def self.unpublished_posts(object, context)
  object.posts.where(published: false).order("created_at DESC")
end

# Support legacy in a DRY way by calling the class method:
def unpublished_posts
  self.class.unpublished_posts(object, context)
end
```

Test your new configurations in CI by running a new build which calls `execution_next` instead of `execution`, for example:

```ruby
# test_helpers.rb
def run_graphql(...)
  if ENV["GRAPHQL_EXECUTION_NEXT"]
    MyAppSchema.execute_next(...)
  else
    MyAppSchema.execute(...)
  end
end
```

Adopting a feature flag system (described below) can also make this easier.

When all tests pass on `.execute_next`, you're ready to try it out in production.

## Migration and Clean-Up Script

`graphql_migrate_execution` is a command-line development tool that can automate many common GraphQL-Ruby field resolver patterns.

Check out its docs and try out: <https://rmosolgo.github.io/graphql_migrate_execution/>

## Production Considerations

There are two categories of problems when migrating:

- Some schema misconfigurations may only be detected at runtime.
- The engine may have bugs. (It's brand-new code trying to emulate 10 years of incremental development!)

When migrating, these possibilities should be considered from three different angles. Using the new engine may...

- ...raise errors in new ways.
- ...return a different result than the old engine.
- ...perform worse than the old engine, especially because of different database access patterns.

To mitigate these possibilities, use dynamic release tools in production like feature flags and experiments.

### Feature Flags

You should use a feature flagging system so that you can shift traffic between old and new runtime engines without redeploying. A good feature-flagging system supports percentage-based flags, so that you can send 1% of traffic to new code while the other 99% uses existing code. After it runs without issues, you can increase the percentage. Or, if you discover issues in production (errors or performance), you can turn it back to 0% while you troubleshoot the problem.

For example:

```ruby
# app/controllers/graphql_controller.rb
exec_method = use_graphql_next? ? :execute_next : :execute
result = MyAppSchema.public_send(exec_method, query_string, context: { ... }, variables: { ... })
render json: result
```

[Flipper](https://github.com/flippercloud/flipper) is a great gem for feature flags. You could also roll your own or pick a third-party service.

__Before__ using `.execute_next` to produce results for production traffic, you might want to run an experiment as described below.

### Experiments

While the two runtime engines _should_ return identical responses, it's possible that `.execute_next` will return a different result than `.execute` due to gem bugs or schema misconfigurations. You can check for this using an "experiment" system in your application which runs _both_ execution engines and compares the result (for __queries only__!).

You'll want to use feature flagging to run the experiment on a subset of traffic, since it comes with performance overhead.

Here's some example code for a setup like this:

```ruby
# app/controllers/graphql_controller.rb
result = MySchema.execute(...)

# Use a dynamic flag, eg Flipper. This should always be true in development and test.
if use_graphql_next_experiment?
  if !query_string.include?("mutation") && !query_string.include?("subscription") # easy way of checking for queries, could possibly have false negatives
    batched_result = MySchema.execute_next(...)
    if batched_result.to_h != result.to_h
      # Log this mismatch somehow here, avoiding potential PII/passwords:
      BugTracker.report <<~TXT
        A GraphQL query returned a non-identical response. Sanitized query string:

        #{result.query.sanitized_query_string}

        User: #{current_user.id}
        # Other context info here...
      TXT
    end
  end
end
```

See [Scientist](https://github.com/github/scientist) for a full-blown production experimentation system.

## Combining feature flags and experiments

A fully-managed rollout would include two flags:

- `use_graphql_next_experiment?`: when true, build an `.execute_next` response and compare it to the `.execute` response. But _always_ return the `.execute` response.
- `use_graphql_next?`: when true, use `.execute_next` and don't call `.execute` at all

This gives you full control over how production traffic is executed without needing to redeploy. You can always turn them down to 0% to get the current behavior.

Here's some example code:

```ruby
if use_graphql_next? # again, use a dynamic feature flag
  result = MySchema.execute_next(...)
else
  result = MySchema.execute(...)
  if use_graphql_next_experiment?
    # Continue running the comparison experiment
  end
end

render json: result.to_h
```

## Compatibility Notes

`Execution::Next`'s new structure means that some GraphQL-Ruby features behave differently (or aren't supported at all, at least not yet). They are discussed one-by-one below.

### Implicit Field Resolution

The _default_, _implicit_ field resolution behavior has changed. Previously, when a field didn't have a specified method or hash key, GraphQL-Ruby would try a combination of `object.public_send(...)` and `object[...]` to resolve it. In `Execution::Next`, GraphQL-Ruby tries `object.public_send(field_sym)` unless another configuration is provided. This removes a lot of overhead from field execution.

Consider a field like this:

```ruby
field :title, String
```

Previously, GraphQL-Ruby would check `type_object.respond_to?(:title)`, `object.respond_to?(:title)`, `object.is_a?(Hash)`. `object.key?(:title)` and `object.key?("title")`.

Now, GraphQL-Ruby simply calls `object.title` and allows the `NoMethodError` to bubble up if one is raised.

### Interface Resolver Methods

Resolver methods are now class methods instead of instance methods. In order to make this work in interface modules, they must be defined in a `resolver_methods do ... end` block, for example:


```ruby
module Node
  include BaseInterface

  field :id, ID, resolve_each: true

  resolver_methods do
    # This will define `def self.id` on Object types that implement this interface
    def id(object, context)
      GlobalId.new(object).to_s
    end
  end

  # Backwards compat instance method:
  def id
    self.class.id(object, context)
  end
end
```

Methods defined in `resolver_methods { ... }` will be copied into Object type classes as _class methods_, so they'll be available for `resolve_{each|static|batch}` fields.

### Query Analyzers, including complexity 🟡

Support is identical; this runs before execution using the exact same code.

TODO: accessing loaded arguments inside analyzers may turn out to be slightly different; it still calls legacy code.

### Authorization, Scoping

`def (self.)authorized?` and `def self.scope_items` will be called as needed during execution.

One incompatibility:

- Argument `#authorized?` _will_ be called if the argument wasn't present in the query but a default value is used. `Execution::Next` doesn't create the metadata necessary to skip authorization in that case. A work-around might be to check if the value is equal to the default value in `def authorized?` and permit it if it is. If this is a blocker for you, please open an issue on GitHub and we can check it out.

### Visibility, including Changesets

Visibility works exactly as before; both runtime modules call the same methods to get type information from the schema.

### Dataloader

Dataloader runs with new execution, but when migrating from instance methods to batch-level class methods, you may need to use [Schema::Member::HasDataloader#dataload_all](rdoc-ref:GraphQL::Schema::Member::HasDataloader#dataload_all) instead of `.dataload`.

### Tracing

Fully supported, but some legacy hooks are _not_ called. Implement the new hooks instead (existing runtime already calls these new hooks). Not called are:

- `execute_field`, `execute_field_lazy`: use `begin_execute_field`, `end_execute_field` instead. (These may be called multiple times when Dataloader pauses or a GraphQL-Batch promise is returned)
- `execute_query`, `execute_query_lazy`: use `execute_multiplex` for a top-level hook instead. (Single queries are always executed in a multiplex of size = 1.)
- `resolve_type`, `authorized`: use `{begin,end}_resolve_type` and `{begin,end}_authorized` instead. (May be called multiple times for Dataloader etc.)

Additionally, `object` parameters to those methods will receive an _Array_ of `objects` instead.

### Lazy resolution (GraphQL-Batch)

Lazy resolution runs in the new execution (GraphQL-Batch is supported). When migrating to class methods, you may need to update your library method calls to work on a set of inputs rather than a single input.

### `current_path` ❌

This is not supported because the new runtime doesn't actually produce `current_path`.

It is theoretically possible to support this but it will be a ton of work. If you use this for core runtime functions, please share your use case in a GitHub issue and we can investigate future options.

### Scoped context ❌

This is currently implemented with `current_path`. Another implementation is probably possible but not implemented yet. Please open an issue to discuss.

### `@defer` 🟡

`@defer` is supported with an implementation difference that _probably_ doesn't affect your application: previously, `@defer` worked by pausing and resuming the _same `GraphQL::Query` instance_. However, with `Execution::Next`, `@defer` takes a different approach. Instead, when a `GraphQL::Query` encounters `@defer`, it notes the location in the document and stops executing that branch. Later, when you request the deferred result, that branch of the query is resumed using a new instance of `GraphQL::Query::Partial`.

This might matter if you're modifying `context` at runtime because those new instances _also_ have fresh `Query::Context` instances. The original query context _will_ get copied into the `@defer` branches using `Query::Context.new(**original_query.context.to_h)`, so any custom values will be available. But if you _assign new keys_ after the context is copied, those keys won't appear when running later `@defer`ed branches.

To handle this, you can refactor how you accumulate data during execution. Instead of `||=`'ing into `context[...]` during execution, assign a new accumulator object _before_ starting the query, then call methods on that object to make any necessary state changes. That new object _will_ be copied into `@defer` partials, and since the object is shared between the different branches, any necessary state changes will still be "seen" everywhere.

If this gives you trouble, please feel free to email me or open an issue on GitHub to discuss a migration strategy.

##### GraphQL-Batch support

When using `Execution::Next`, no custom code is required to support `graphql-batch` -- support is built-in.

### `@stream`

`@stream` is supported.

See the not above about how `@defer` no longer _resumes_ the original, top-level query. The same thing applies to `@stream`.

`GraphQL::Pro::Stream` now lazily streams Enumerators. If you were using the (undocumented) `GraphQL::Pro::FutureStream`, you can switch to `GraphQL::Pro::Stream` _after_ migrating to `Execution::Next`. (Once all your traffic uses the new execution module, you'll get the same runtime behavior from `GraphQL::Pro::Stream`.)

### ObjectCache

Supported completely.

### Custom Directives ❌

There is some implementation in the code right now but it's not stable. Please open an issue to discuss.

Query-level directives are not implemented yet, but will be. Please open an issue if you have a use case for this.

### `as:`

`as:` is applied: arguments are passed into Ruby methods by their `as:` names instead of their GraphQL names.

### `loads:` 🟡

`loads:` is handled as previously, __except__ that custom `def load_...` methods are _not_ called.

### `prepare:` 🟡

Procs are called as before.

Methods that depend on a runtime `object` (such as a type instance or Mutation class) are _not_ called, because arguments are prepared before objects are ready.

### `validates:` 🟡

Built-in validators are supported. Custom validators will always receive `nil` as the `object`. (`object` is no longer available; this API will probably change before this is fully released.)

### Field Extensions 🟡

Field extension methods are called with new arguments:

- `objects:` instead of `object:`, with an Array
- `values:` instead of `value:`, with an Array

You can support both types of calls in your methods by changing the signature to `object: nil, objects: nil` (and `value: nil, values: nil`), then checking which argument was passed.

### Resolver classes (including Mutations and Subscriptions) 🟡

Resolver classes are called, but with slightly different semantics:

- `#ready?` is still called, but after arguments are loaded. It's now a useless method and will probably be deprecated.
- `def load_...` methods are not called; instead, arguments are passed to the top-level `Schema.object_from_id` hook.

### Field `extras:`, including `lookahead`

`:ast_node` and `:lookahead` are already implemented. Others are possible -- please raise an issue if you need one. `extras: [:current_path]` is not possible.

### `raw_value` 🟡

Supported, but the `raw_value` call must be made on `context`, for example:

```ruby
field :values, SomeObjectType, resolve_static: true

def self.values(context)
  context.raw_value(...)
end
```

### Errors and `rescue_from` 🟡

Raising `GraphQL::ExecutionError` and adding `rescue_from` handlers are supported

Returning an array of `GraphQL::ExecutionError` instances is not supported anymore.

`extras: [:execution_errors]` and `context.add_error` are not supported anymore.

### Connection fields

Connection arguments are automatically handled and connection wrapper objects are automatically applied to arrays and relations.

### Custom Introspection

This _works_ but if you want custom authorization or any lazy values, see notes about that compatibility.

If you're reimplementing default values, you'll need to add the corresponding `resolve_static: true` or `resolve_each: true` configurations. See the built-in type definitions under `GraphQL::Introspection` to get these configurations.

### Multiplex

To use the new engine to run a multiplex, use `MyAppSchema.multiplex_next(...)` with the same arguments.

### GraphQL::Current 🟡

`current_field` doesn't work; `dataloader_source` works. `current_operation_name` doesn't work.

This will be fixed soon but may require opt-in to avoid needless overhead.

### `fallback_value:` ❌

`fallback_value:` is not supported in Execution::Next. It's not implemented because of the overhead it adds to resultion. You'll have to implement it by hand in resolvers.

`graphql_migrate_execution` creates a resolver that _always_ returns the `fallback_value`. This might be right in some cases, but you'll probably have to implement your own method, like:

```ruby
field :name, String, fallback_value: "Anonymous", resolve_each: :resolve_name

def resolve_name(object, context)
  if object.respond_to?(:name)
    object.name
  elsif (is_h = object.is_a?(Hash)) && object.key?(:name)
    object[:name]
  elsif is_h && object.key?("name")
    object["name"]
  else
    "Anonymous"
  end
end
```

## GraphQL::Backtrace

Doesn't support Execution::Next, but it's probably not necessary. `Execution::Next` includes the field name in error messages and doesn't generate crazy-long stack traces because of its design.

---

### Guides Execution Next (`guides/execution/next.md`)

# New Execution Module

GraphQL-Ruby has a new execution engine, [GraphQL::Execution::Next](rdoc-ref:GraphQL::Execution::Next). It's much faster and less memory-consuming than the existing execution engine, but requires some care in migrating.

This feature is in heavy development, so if you give it a try and run into any problems, please open an issue on GitHub!

## Background

Breadth-first GraphQL execution (or, "execution batching") is an algorithmic paradigm developed by Shopify to address problems of scale when resolving large lists and nested sets. Rather than paying field-level overhead costs (resolver calls, instrumentation, lazy promises, etc) for every field _of every resolved object_, the pattern instead incurs these costs only once per field selection and runs the corresponding breadth of objects with no additional overhead.

The original proof-of-concept of Shopify's core algorithm and white paper notes can be found in [graphql-breadth-exec](https://github.com/gmac/graphql-breadth-exec). That prototype matured into Shopify's proprietary _GraphQL Cardinal_ execution engine that now runs much of their core traffic.

GraphQL-Ruby brings these breadth-first design principles to the open-source community with several novel techniques for implementing GraphQL:

- Fields are resolved breadth-first using implicitly batched resolvers. These run longer and hotter on application logic with no execution overhead.
- Batched resolvers may bind entire load sets to a single lazy promise to dramatically reduce promise bloat.
- Error handling is optimized into a second pass that only runs when errors actually occur.
- Stack profiling becomes much more organized with a linear flow and aggregate field spans, rather than fields getting split up across subtree repetitions.
- The engine is driven by enqueuing rather than recursion, which shrinks stack traces and reduces memory usage.

Breadth-first patterns can produce dramatic results in responses with a high degree of repetition: it's not uncommon to see breadth batching run __15x__ faster and use __75% less__ memory than classic GraphQL Ruby execution. However – gains are relative. A flat tree with no lists will see little difference. A list of 2 resolving one field each will see a small gain, while a list of 100 resolving ten fields each will likely see dramatic results.

The downside is that many of GraphQL-Ruby's "bonus features" -- those that go beyond the behavior described in the GraphQL Specification -- add non-trivial overhead when used. So, the task ahead is to "lift the ceiling" of performance in GraphQL-Ruby while retaining as much compatibility as possible and supporting a gradual transition to this new runtime engine.

## Enabling Execution::Next

The new execution engine is enabled with two steps:

- Add the plugin to your schema with `use GraphQL::Execution::Next`
- Call `MySchema.execute_next(...)` instead of `MySchema.execute(...)`. It takes the same arguments.

See [compatibility notes](/execution/migration#compatibility-notes) for updating your schema to run queries with the new engine.

You can also add `..., as_default: true` to use `execute_next` by default. In that case, call `execute_legacy` if you need the old runtime.

## Field configurations

The new runtime engine supports several field resolution configurations out of the box.

### Method calls (default, `method:`)

These fields call `object.#{field_name}`. This is the default, and the method name can be overridden with `method: ...`:

```ruby
field :title, String # calls object.title
field :title, String, method: :get_title_somehow # calls object.get_title_somehow
```

### Hash keys (`hash_key:`)

These fields call `object[hash_key]`, configured with `hash_key: ...`.

```ruby
field :title, String, hash_key: :title # calls object[:title]
field :title, String, hash_key: "title" # calls object["title"]
```

**Note:** new execution doesn't "fall back" to hash key lookups, and it doesn't try strings when Symbols are given. The existing runtime engine does that, but it has been excluded for performance reasons. To get the old resolution behavior, you can code it like:

```ruby
field :title, String, resolve_each: true

def self.title(object, context)
  # For example, try a symbol key first, then a string:
  object[:title] || object["title"]
end
```

### Per-object (`resolve_each:`)

These fields use a _class method_ to produce a result for each object, configured with `resolve_each:`.

```ruby
field :title, String, resolve_each: true do # calls `self.title(...)` below
  argument :language, Types::Language, required: false, default_value: "EN"
end

def self.title(object, context, language:)
  # Assuming this makes no database lookups or other external service calls:
  object.localization.get(:title, language:)
end
```

The default method is the same as the field name symbol. You can also provide a custom method:

```ruby
# Avoid a conflict with Ruby's built-in `Class#name`:
field :name, String, resolve_each: :get_name

def self.get_name(object, context)
  # ...
end
```


Under the hood, GraphQL-Ruby calls `objects.map { ... }`, calling this class method.


‼️ __Don't use this__ if your logic calls external services or databases (including with Dataloader). If you do, your I/O will be sequential instead of batched. Use `resolve_batch:` or `resolve_static:` instead, see below.

### Global (`resolve_static:`)

Fields that use a _class method_ to produce a single result shared by all objects, configured with `resolve_static:`. The method does _not_ receive any `object`, only `context`:

```ruby
field :posts_count, Integer, resolve_static: :count_all_posts do
  argument :include_unpublished, Boolean, required: false, default_value: false
end

def self.count_all_posts(context, include_unpublished:)
  posts = Post.all
  if !include_unpublished
    posts = posts.published
  end
  posts.count
end
```

Under the hood, GraphQL-Ruby calls `Array.new(objects.size, static_result)`.

### Batch resolvers (`resolve_batch:`)

This is a high-performance option for when you need to do I/O to generate results. By working with a batch of objects, you can greatly reduce the framework overhead in preparing a result.

These fields use a _class method_ to map parent objects to field results, configured with `resolve_batch:`:

```ruby
field :title, String, resolve_batch: true do # calls self.title below
  argument :language, Types::Language, required: false, default_value: "EN"
end

def self.title(objects, context, language:)
  # This is equivalent to plain `field :title, ...`, but for example:
  objects.map { |obj| obj.title(language:) }
end
```

This is especially useful when batching Dataloader calls:

```ruby
class Types::Comment < BaseObject
  field :author_rating, Integer, resolve_batch: true

  def self.author_rating(objects, context)
    authors = context.dataload_all_records(objects, :author)
    context.dataload_all(Sources::AuthorRating, authors)
  end
end
```

By default, it calls a class method matching the field name. You can customize this configuration, too:

```ruby
field :author_rating, Integer, resolve_batch: :calculate_rating # calls `self.calculate_rating(objects, context)`

def self.calculate_rating(objects, context)
  # ...
end
```

### Dataloader

`Execution::Next` supports field configuration shorthands for common dataloader usage. Under the hood, these make sure data fetching is batched and cached.

`method:` can be added to any hash-style `dataload:` config to call a public method on the loaded value.

#### Sources

Use a custom dataloader source from your application:

```ruby
class Types::CommentType
  # Equivalent to `dataload(Sources::CommentRating, object)`
  field :rating, Integer, dataload: Sources::CommentRating

  # `using:`: A method to call to get a value to pass to dataloader
  # `by: [...]`: An array of arguments to pass on to dataloader
  #
  # Equivalent to `dataload(Sources::ReadingDuration, :comment, object.body)
  field :reading_duration, Integer, dataload: { with: Sources::ReadingDuration, using: :body, by: [:comment] }

  # `method:`: A method to call on the loaded value
  #
  # Equivalent to `dataload(Sources::CommentRating, object).to_s`
  field :rating_string, String, dataload: { with: Sources::CommentRating, method: :to_s }
```

#### Rails Associations

Load ActiveRecord associations using [GraphQL::Dataloader::ActiveRecordAssociationSource](rdoc-ref:GraphQL::Dataloader::ActiveRecordAssociationSource):

```ruby
class Types::CommentType < Types::BaseObject
  # Equivalent to `dataload_association(:post)`
  field :post, Types::Post, dataload: { association: true }
  # Equivalent to `dataload_association(:user)
  field :author, Types::Post, dataload: { association: :user }
  # Equivalent to `dataload_association(:author)&.name`
  field :author_name, String, null: true, dataload: { association: :author, method: :name }
end
```

#### Rails Records

Load ActiveRecord associations using [GraphQL::Dataloader::ActiveRecordSource](rdoc-ref:GraphQL::Dataloader::ActiveRecordSource).

```ruby
class Types::SearchResult < Types::BaseObject
  # Equivalent to `dataload_record(Post, object.post_id)`
  field :post, Types::Post, dataload: { model: Post, using: :post_id }
  # Equivalent to `dataload_record(User, object.created_by_handle, find_by: :handle)`
  field :author, Types::User, dataload: { model: User, using: :created_by_handle, find_by: :handle }
  # Equivalent to `dataload_record(Post, object.post_id)&.title`
  field :post_title, String, null: true, dataload: { model: Post, using: :post_id, method: :title }
end
```

### Legacy instance methods

`resolve_legacy_instance_method:`

There is _partial_ support for instance methods on Object type classes, for now. It will be deprecated and removed soon.

‼️ Don't use legacy instance methods with Dataloader. It will be sequential, not batched. ‼️

```ruby
field :title, String, resolve_legacy_instance_method: true do
  argument :language, Types::Language, required: false, default_value: "EN"
end

def title(language:)
  # Assuming this makes no database lookups or other external service calls:
  object.localization.get(:title, language:)
end
```

Under the hood, GraphQL-Ruby calls `objects.map { ... }`, calling this instance method. It adds significant overhead because GraphQL-Ruby initializes the object type class.


### `true` shorthand

There is also a `true` shorthand: when one of the `resolve_...:` configurations is passed as `true` (ie, `resolve_batch: true`, `resolve_each: true`, `resolve_static: true`, or `resolve_legacy_instance_method: true`), then the Symbol field name is used as the class method. For example:

```ruby
field :posts_count, Integer, resolve_static: true

def self.posts_count(context)
  Post.all.count
end
```

## Migration

Read about migrating in the [Migration Doc](/execution/migration).

---

### Guides Fields Arguments (`guides/fields/arguments.md`)

# Arguments

Arguments are part of the GraphQL::Schema::Argument API. The complete
reference, including nullability, defaults, deprecation, aliasing,
preprocessing, camelization, and supported input types, is maintained with
the implementation:

[GraphQL::Schema::Argument](rdoc-ref:GraphQL::Schema::Argument)

Use that API reference for the exact argument(...) parameters and runtime
behavior. This guide remains the entry point for field arguments and connects
the topic to the [fields](/fields/introduction) and
[validation](/fields/validation) guides.

---

### Guides Fields Introduction (`guides/fields/introduction.md`)

# Field definitions

Field definitions are part of the GraphQL::Schema::Field API. The complete
reference, including names, return types, descriptions, resolution behavior,
arguments, extras, and default options, is maintained with the implementation:

[GraphQL::Schema::Field](rdoc-ref:GraphQL::Schema::Field)

Use that API reference when you need the exact field(...) parameters and
runtime behavior. This guide remains the entry point for the fields section
and links to the related [objects](/type_definitions/objects),
[interfaces](/type_definitions/interfaces), and
[arguments](/fields/arguments) guides.

## Field Names

See the [Field API reference](rdoc-ref:GraphQL::Schema::Field).

## Field Return Type

See the [Field API reference](rdoc-ref:GraphQL::Schema::Field).

## Field Documentation

See the [Field API reference](rdoc-ref:GraphQL::Schema::Field).

## Field Resolution

See the [Field API reference](rdoc-ref:GraphQL::Schema::Field).

## Field Arguments

See the [Field API reference](rdoc-ref:GraphQL::Schema::Field).

## Extra Field Metadata

See the [Field API reference](rdoc-ref:GraphQL::Schema::Field).

## Field Parameter Default Values

See the [Field API reference](rdoc-ref:GraphQL::Schema::Field).

---

### Guides Fields Limits (`guides/fields/limits.md`)

# Limits

## List Fields

Always limit the number of items which can be returned from a list field. For example, use a `limit:` argument and make sure it's not too big. The `prepare:` function provides a convenient place to cap the number of items:

```ruby
field :items, [Types::ItemType] do
  # Cap the number of items at 30
  argument :limit, Integer, default_value: 20, prepare: ->(limit, ctx) {[limit, 30].min}
end

def items(limit:)
  object.items.limit(limit)
end
```

This way, you won't hit your database for 1000 items!

## Connections

Connections accept a [`max_page_size` option](/pagination/using_connections#max-page-size) which limits the number of nodes.

---

### Guides Fields Resolvers (`guides/fields/resolvers.md`)

# Resolvers

A [GraphQL::Schema::Resolver](rdoc-ref:GraphQL::Schema::Resolver) is a container for field signature and resolution logic. It can be attached to a field with the `resolver:` keyword:

```ruby
# Use the resolver class to execute this field
field :pending_orders, resolver: PendingOrders
```

Under the hood, [GraphQL::Schema::Mutation](rdoc-ref:GraphQL::Schema::Mutation) is a specialized subclass of `Resolver`.

## First, ask yourself ...

Do you really need a `Resolver`? Putting logic in a Resolver has some downsides:

- Since it's coupled to GraphQL, it's harder to test than a plain ol' Ruby object in your app
- Since the base class comes from GraphQL-Ruby, it's subject to upstream changes which may require updates in your code

Here are a few alternatives to consider:

- Put display logic (sorting, filtering, etc.) into a plain ol' Ruby class in your app, and test that class
- Hook up that object with a method, for example:

```ruby
field :recommended_items, [Types::Item], null: false
def recommended_items
  ItemRecommendation.new(user: context[:viewer]).items
end
```

- If you have lots of arguments to share, use a class method to generate fields, for example:

```ruby
# Generate a field which returns a filtered, sorted list of items
def self.items_field(name, override_options, &block)
  # Prepare options
  default_field_options = { type: [Types::Item], null: false }
  field_options = default_field_options.merge(override_options)
  # Create the field
  field(name, **field_options) do
    argument :order_by, Types::ItemOrder, required: false
    argument :category, Types::ItemCategory, required: false
    # Allow an override block to add more arguments
    instance_eval(&block) if block_given?
  end
end

# Then use the generator to create a field:
items_field(:recommended_items) do |field|
  field.argument :similar_to_product_id, ID, required: false
end
# Implement the field
def recommended_items
  # ...
end
```

As a matter of code organization, that class method could be put in a module and shared between different classes that need it.

- If you need the _same_ logic shared between several objects, consider using a Ruby module and its `self.included` hook, for example:

```ruby
module HasRecommendedItems
  def self.included(child_class)
    # attach the field here
    child_class.field(:recommended_items, [Types::Item], null: false)
  end

  # then implement the field
  def recommended_items
    # ...
  end
end

# Add the field to some objects:
class Types::User < BaseObject
  include HasRecommendedItems # adds the field
end
```

- If the module approach looks good to you, also consider [Interfaces](/type_definitions/interfaces). They also share behavior between objects (since they're just modules that get included, after all), and they expose that commonality to clients via introspection.

## When do you really need a resolver?

So, if there are other, better options, why does `Resolver` exist? Here are a few specific advantages:

- __Isolation__. A `Resolver` is instantiated for each call to the field, so its instance variables are private to that object. If you need to use instance variables for some reason, this helps. You have a guarantee that those values won't hang around when the work is done.
- __Complex Schema Generation__. `RelayClassicMutation` (which is a `Resolver` subclass) generates input types and return types for each mutation. Using a `Resolver` class makes it easier to implement, share and extend this code generation logic.

## Using `resolver`

Use the base resolver class:

```ruby
module Resolvers
  class RecommendedItems < BaseResolver
    type [Types::Item], null: false
    description "Items this user might like"

    argument :order_by, Types::ItemOrder, required: false
    argument :category, Types::ItemCategory, required: false

    def resolve(order_by: nil, category: nil)
      # call your application logic here:
      recommendations = ItemRecommendation.new(
        viewer: context[:viewer],
        recommended_for: object,
        order_by: order_by,
        category: category,
      )
      # return the list of items
      recommendations.items
    end
  end
end
```

And attach it to your field:

```ruby
class Types::User < Types::BaseObject
  field :recommended_items, resolver: Resolvers::RecommendedItems
end
```

Since the `Resolver` lifecycle is managed by the GraphQL runtime, the best way to test it is to execute GraphQL queries and check the results.

### Nesting resolvers of the same type

You may run into cyclical loading issues when using a resolver within the definition of the type the resolver returns e.g.

```ruby
# app/graphql/types/query_type.rb

module Types
  class QueryType < Types::BaseObject
    field :tasks, resolver: Resolvers::TasksResolver
  end
end

# app/graphql/types/task_type.rb

module Types
  class TaskType < Types::BaseObject
    field :title, String, null: false
    field :tasks, resolver: Resolvers::TasksResolver
  end
end

# app/graphql/resolvers/tasks_resolver.rb

module Resolvers
  class TasksResolver < BaseResolver
    type [Types::TaskType], null: false

    def resolve
      []
    end
  end
end
```

The above can produce the following error: `Failed to build return type for Task.tasks from nil: Unexpected type input:  (NilClass)`.

A simple solution is to express the type as a string in the resolver:

```ruby
module Resolvers
  class TasksResolver < BaseResolver
    type "[Types::TaskType]", null: false

    def resolve
      []
    end
  end
end
```

In doing so, you can defer the loading of the type class until the nested resolver has already been loaded.

# Extensions

In cases when you want your resolvers to add some extensions to the field they resolve, you can use `extension` method, which accepts extension class and options. Multiple extensions can be configured for a single resolver.

```ruby
class GreetingExtension < GraphQL::Schema::FieldExtension
  def resolve(object:, arguments:, **rest)
    name = yield(object, arguments)
    "#{options[:greeting]}, #{name}!"
  end
end

class ResolverWithExtension < BaseResolver
  type String, null: false

  extension GreetingExtension, greeting: "Hi"

  def resolve
    "Robert"
  end
end
```

---

### Guides Fields Validation (`guides/fields/validation.md`)

# Validation

Arguments can be validated at runtime using built-in or custom validators.

Validations are configured in `argument(...)` calls on fields or input objects:

```ruby
argument :home_phone, String,
  description: "A US phone number",
  validates: { format: { with: /\d{3}-\d{3}-\d{4}/ } }
```

or, `validates required: { ... }` inside a `field ... do ... end` block:

```ruby
field :comments, [Comment],
  description: "Find comments by author ID or author name" do
  argument :author_id, ID, required: false
  argument :author_name, String, required: false
  # Either `authorId` or `authorName` must be provided by the client, but not both:
  validates required: { one_of: [:author_id, :author_name] }
end
```

Validations can be provided with a keyword (`validates: { ... }`) or with a method inside the configuration block (`validates ...`).

## Built-In Validations

See each validator's API docs for details:

- `length: { maximum: ..., minimum: ..., is: ..., within: ... }` [Schema::Validator::LengthValidator](rdoc-ref:GraphQL::Schema::Validator::LengthValidator)
- `format: { with: /.../, without: /.../ }` [Schema::Validator::FormatValidator](rdoc-ref:GraphQL::Schema::Validator::FormatValidator)
- `numericality: { greater_than:, greater_than_or_equal_to:, less_than:, less_than_or_equal_to:, other_than:, odd:, even: }` [Schema::Validator::NumericalityValidator](rdoc-ref:GraphQL::Schema::Validator::NumericalityValidator)
- `inclusion: { in: [...] }` [Schema::Validator::InclusionValidator](rdoc-ref:GraphQL::Schema::Validator::InclusionValidator)
- `exclusion: { in: [...] }` [Schema::Validator::ExclusionValidator](rdoc-ref:GraphQL::Schema::Validator::ExclusionValidator)
- `required: { one_of: [...] }` [Schema::Validator::RequiredValidator](rdoc-ref:GraphQL::Schema::Validator::RequiredValidator)
- `allow_blank: true|false` [Schema::Validator::AllowBlankValidator](rdoc-ref:GraphQL::Schema::Validator::AllowBlankValidator)
- `allow_null: true|false` [Schema::Validator::AllowNullValidator](rdoc-ref:GraphQL::Schema::Validator::AllowNullValidator)
- `all: { ... }` [Schema::Validator::AllValidator](rdoc-ref:GraphQL::Schema::Validator::AllValidator)

Some of the validators accept customizable messages for certain validation failures; see the API docs for examples.

`allow_blank:` and `allow_null:` may affect other validations, for example:

```ruby
validates: { format: { with: /\A\d{4}\Z/ }, allow_blank: true }
```

Will permit any String containing four digits, or the empty string (`""`) if Rails is loaded. (GraphQL-Ruby checks for `.blank?`, which is usually defined by Rails.)

Alternatively, they can be used alone, for example:

```ruby
argument :id, ID, required: false, validates: { allow_null: true }
```

Will permit any query that passes `id: null`.

Validation options may also be passed as procs that accept no parameters, for example:

```ruby
validates :title, String, validates: { format: { with: -> { Flipper.enabled?(:new_title_format) ? NEW_TITLE_FORMAT : /.*/ }}
```

## Custom Validators

You can write custom validators, too. A validator is a class that extends `GraphQL::Schema::Validator`. It should implement:

- `def initialize(..., **default_options)` to accept any validator-specific options and pass along the defaults to `super(**default_options)`
- `def validate(_empty, context, value)` which is called at runtime to validate `value`. It may return a String error message or an Array of Strings. GraphQL-Ruby will add those messages to the top-level `"errors"` array along with runtime context information.

Then, custom validators can be attached either:

- directly, passed to `validates`, like `validates: { MyCustomValidator => { some: :options }`
- by keyword, if the keyword is registered with `GraphQL::Schema::Validator.install(:custom, MyCustomValidator)`. (That would support `validates: { custom: { some: :options }})`.)

Validators are initialized when the schema is constructed (at application boot), and `validate(...)` is called while executing the query. There's one `Validator` instance for each configuration on each field, argument, or input object. (`Validator` instances aren't shared.)

---

### Guides Javascript Client Apollo Subscriptions (`guides/javascript_client/apollo_subscriptions.md`)

# Apollo Subscriptions

GraphQL-Ruby's JavaScript client includes several kinds of support for Apollo Client:

- Apollo Link (2.x, 3.x):
  - [Overview](#apollo-link)
  - [Pusher](#apollo-link--pusher)
  - [Ably](#apollo-link--ably)
  - [ActionCable](#apollo-link--actioncable)
- Apollo 1.x:
  - [Overview](#apollo-1)
  - [Pusher](#apollo-1--pusher)
  - [ActionCable](#apollo-1--actioncable)

## Apollo Link

Apollo Links are used by Apollo client 2.x and 3.x.

## Apollo Link -- Pusher

`graphql-ruby-client` includes support for subscriptions with Pusher and ApolloLink.

To use it, add `PusherLink` before your `HttpLink`.

For example:

```js
// Load Apollo stuff
import { ApolloClient, HttpLink, ApolloLink, InMemoryCache } from "@apollo/client";
// Load PusherLink from graphql-ruby-client
import PusherLink from 'graphql-ruby-client/subscriptions/PusherLink';

// Load Pusher and create a client
import Pusher from "pusher-js"
var pusherClient = new Pusher("your-app-key", { cluster: "us2" })

// Make the HTTP link which actually sends the queries
const httpLink = new HttpLink({
  uri: '/graphql',
  credentials: 'include'
});

// Make the Pusher link which will pick up on subscriptions
const pusherLink = new PusherLink({pusher: pusherClient})

// Combine the two links to work together
const link = ApolloLink.from([pusherLink, httpLink])

// Initialize the client
const client = new ApolloClient({
  link: link,
  cache: new InMemoryCache()
});
```

This link will check responses for the `X-Subscription-ID` header, and if it's present, it will use that value to subscribe to Pusher for future updates.

If you're using [compressed payloads](/subscriptions/pusher_implementation#payload-compression), configure a `decompress:` function, too:

```javascript
// Add `pako` to the project for gunzipping
import pako from "pako"

const pusherLink = new PusherLink({
  pusher: pusherClient,
  decompress: function(compressed) {
    // Decode base64
    const data = atob(compressed)
      .split('')
      .map(x => x.charCodeAt(0));
    // Decompress
    const payloadString = pako.inflate(new Uint8Array(data), { to: 'string' });
    // Parse into an object
    return JSON.parse(payloadString);
  }
})
```

## Apollo Link -- Ably

`graphql-ruby-client` includes support for subscriptions with Ably and ApolloLink.

To use it, add `AblyLink` before your `HttpLink`.

For example:

```js
// Load Apollo stuff
import { ApolloClient, HttpLink, ApolloLink, InMemoryCache } from '@apollo/client';
// Load Ably subscriptions link
import AblyLink from 'graphql-ruby-client/subscriptions/AblyLink'
// Load Ably and create a client
const Ably = require("ably")
const ablyClient = new Ably.Realtime({ key: "your-app-key" })

// Make the HTTP link which actually sends the queries
const httpLink = new HttpLink({
  uri: '/graphql',
  credentials: 'include'
});

// Make the Ably link which will pick up on subscriptions
const ablyLink = new AblyLink({ably: ablyClient})

// Combine the two links to work together
const link = ApolloLink.from([ablyLink, httpLink])

// Initialize the client
const client = new ApolloClient({
  link: link,
  cache: new InMemoryCache()
});
```

This link will check responses for the `X-Subscription-ID` header, and if it's present, it will use that value to subscribe to Ably for future updates.

For your __app key__, make a key with "Subscribe" and "Presence" privileges and use that:



## Apollo Link -- ActionCable

`graphql-ruby-client` includes support for subscriptions with ActionCable and ApolloLink.

To use it, construct a split link that routes:

- subscription queries to an `ActionCableLink`; and
- other queries to an `HttpLink`

For example:

```js
import { ApolloClient, HttpLink, ApolloLink, InMemoryCache } from '@apollo/client';
import { createConsumer } from '@rails/actioncable';
import ActionCableLink from 'graphql-ruby-client/subscriptions/ActionCableLink';

const cable = createConsumer()

const httpLink = new HttpLink({
  uri: '/graphql',
  credentials: 'include'
});

const hasSubscriptionOperation = ({ query: { definitions } }) => {
  return definitions.some(
    ({ kind, operation }) => kind === 'OperationDefinition' && operation === 'subscription'
  )
}

const link = ApolloLink.split(
  hasSubscriptionOperation,
  new ActionCableLink({cable}),
  httpLink
);

const client = new ApolloClient({
  link: link,
  cache: new InMemoryCache()
});
```

Note that for Rails 5, the ActionCable client package is `actioncable`, not `@rails/actioncable`.

## Apollo 1

`graphql-ruby-client` includes support for Apollo 1 client subscriptions over [Pusher](/subscriptions/pusher_implementation) or [ActionCable](/subscriptions/action_cable_implementation).

To use it, require `subscriptions/addGraphQLSubscriptions` and call the function with your network interface and transport client (example below).

See the [Subscriptions guide](/subscriptions/overview) for information about server-side setup.

### Apollo 1 -- Pusher

Pass `{pusher: pusherClient}` to use Pusher:

```js
// Load Pusher and create a client
var Pusher = require("pusher-js")
var pusherClient = new Pusher(appKey, options)

// Add subscriptions to the network interface with the `pusher:` options
import addGraphQLSubscriptions from "graphql-ruby-client/subscriptions/addGraphQLSubscriptions"
addGraphQLSubscriptions(myNetworkInterface, {pusher: pusherClient})

// Optionally, add persisted query support:
var OperationStoreClient = require("./OperationStoreClient")
RailsNetworkInterface.use([OperationStoreClient.apolloMiddleware])
```

If you're using [compressed payloads](/subscriptions/pusher_implementation#payload-compression), configure a `decompress:` function, too:

```javascript
// Add `pako` to the project for gunzipping
import pako from "pako"

addGraphQLSubscriptions(myNetworkInterface, {
  pusher: pusherClient,
  decompress: function(compressed) {
    // Decode base64
    const data = btoa(compressed)
    // Decompress
    const payloadString = pako.inflate(data, { to: 'string' })
    // Parse into an object
    return JSON.parse(payloadString);
  }
})
```

### Apollo 1 -- ActionCable

By passing `{cable: cable}`, all `subscription` queries will be routed to ActionCable.

For example:

```js
// Load ActionCable and create a consumer
var ActionCable = require('@rails/actioncable')
var cable = ActionCable.createConsumer()
window.cable = cable

// Load ApolloClient and create a network interface
var apollo = require('apollo-client')
var RailsNetworkInterface = apollo.createNetworkInterface({
 uri: '/graphql',
 opts: {
   credentials: 'include',
 },
 headers: {
   'X-CSRF-Token': $("meta[name=csrf-token]").attr("content"),
 }
});

// Add subscriptions to the network interface
import addGraphQLSubscriptions from "graphql-ruby-client/subscriptions/addGraphQLSubscriptions"
addGraphQLSubscriptions(RailsNetworkInterface, {cable: cable})

// Optionally, add persisted query support:
var OperationStoreClient = require("./OperationStoreClient")
RailsNetworkInterface.use([OperationStoreClient.apolloMiddleware])
```

---

### Guides Javascript Client Graphiql Subscriptions (`guides/javascript_client/graphiql_subscriptions.md`)

# GraphiQL Subscriptions

After setting up your server, you can integrate subscriptions into [GraphiQL](https://github.com/graphql/graphiql/tree/main/packages/graphiql#readme), the in-browser GraphQL IDE.

## Adding GraphiQL to your app

To get started, make a page for rendering GraphiQL, for example:

```html

<div id="root" style="height: 100vh;"></div>
```

Then, install GraphiQL (eg, `yarn add graphiql`) and add JavaScript code to import GraphiQL and render it on your page:

```js
import { GraphiQL } from 'graphiql'
import React from 'react'
import { createRoot } from 'react-dom/client'
import 'graphiql/graphiql.css'
import { createGraphiQLFetcher } from '@graphiql/toolkit'

const fetcher = createGraphiQLFetcher({ url: '/graphql' })
const root = createRoot(document.getElementById('root'))
root.render(<GraphiQL fetcher={fetcher}/>)
```

After that, you should be able to load the page in your app and see the GraphiQL editor.

## Ably

To integrate [Ably subscriptions](/subscriptions/ably_implementation), use `createAblyFetcher`,  for example:

```js
import Ably from "ably"
import createAblyFetcher from 'graphql-ruby-client/subscriptions/createAblyFetcher'

// Initialize a client
// the key must have "subscribe" and "presence" permissions
const ably = new Ably.Realtime({ key: "your.application.key" })

// Initialize a new fetcher and pass it to GraphiQL below
var fetcher = createAblyFetcher({ ably: ably, url: "/graphql" })
const root = createRoot(document.getElementById('root'))
root.render(<GraphiQL fetcher={fetcher} />)
```

Under the hood, it will use `window.fetch` to send GraphQL operations to the server, then listen for `X-Subscription-ID` headers in responses. To customize its HTTP requests, you can pass a `fetchOptions:` object or a custom `fetch:` function to `createAblyFetcher({ ... })`.

## Pusher

To integrate [Pusher subscriptions](/subscriptions/pusher_implementation), use `createPusherFetcher`,  for example:

```js
import Pusher from "pusher-js"
import createPusherFetcher from 'graphql-ruby-client/subscriptions/createPusherFetcher'

// Initialize a client
const pusher = new Pusher("your-app-key", { cluster: "your-cluster" })

// Initialize a new fetcher and pass it to GraphiQL below
var fetcher = createPusherFetcher({ pusher: pusher, url: "/graphql" })
const root = createRoot(document.getElementById('root'))
root.render(<GraphiQL fetcher={fetcher} />)
```

Under the hood, it will use `window.fetch` to send GraphQL operations to the server, then listen for `X-Subscription-ID` headers in responses. To customize its HTTP requests, you can pass a `fetchOptions:` object or a custom `fetch:` function to `createPusherFetcher({ ... })`.

## ActionCable

To integrate [ActionCable subscriptions](/subscriptions/action_cable_implementation), use `createActionCableFetcher`,  for example:

```js
import { createConsumer } from "@rails/actioncable"
import createActionCableFetcher from 'graphql-ruby-client/subscriptions/createActionCableFetcher';

// Initialize a client
const actionCable = createConsumer()

// Initialize a new fetcher and pass it to GraphiQL below
var fetcher = createActionCableFetcher({ consumer: actionCable, url: "/graphql" })
const root = createRoot(document.getElementById('root'))
root.render(<GraphiQL fetcher={fetcher} />)
```

Under the hood, it will split traffic: it will send `subscription { ... }` operations via ActionCable and send queries and mutations via HTTP `POST` using `window.fetch`. To customize its HTTP requests, you can pass a `fetchOptions:` object or a custom `fetch:` function to `createActionCableFetcher({ ... })`.

---

### Guides Javascript Client Relay Subscriptions (`guides/javascript_client/relay_subscriptions.md`)

# Relay Subscriptions

`graphql-ruby-client` includes three kinds of support for subscriptions with Relay Modern:

- [Pusher](#pusher)
- [Ably](#ably)
- [ActionCable](#actioncable)

To use it, require `graphql-ruby-client/subscriptions/createRelaySubscriptionHandler` and call the function with your client and optionally, your OperationStoreClient.

__Note:__ For Relay <11, use `import { createLegacyRelaySubscriptionHandler } from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler"` instead; the signature changed in Relay 11.


See the [Subscriptions guide](/subscriptions/overview) for information about server-side setup.

## Pusher

Subscriptions with [Pusher](/subscriptions/pusher_implementation) require two things:

- A client from the [`pusher-js` library](https://github.com/pusher/pusher-js)
- A [`fetchOperation` function](#fetchoperation-function) for sending the `subscription` operation to the server

### Pusher client

Pass `pusher:` to get Subscription updates over Pusher:

```js
// Load the helper function
import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler"

// Prepare a Pusher client
var Pusher = require("pusher-js")
var pusherClient = new Pusher(appKey, options)

// Create a fetchOperation, see below for more details
function fetchOperation(operation, variables, cacheConfig) {
  return fetch(...)
}

// Create a Relay Modern-compatible handler
var subscriptionHandler = createRelaySubscriptionHandler({
  pusher: pusherClient,
  fetchOperation: fetchOperation
})

// Create a Relay Modern network with the handler
var network = Network.create(fetchQuery, subscriptionHandler)
```

### Compressed Payloads

If you're using [compressed payloads](/subscriptions/pusher_implementation#payload-compression), configure a `decompress:` function, too:

```javascript
// Add `pako` to the project for gunzipping
import pako from "pako"

var subscriptionHandler = createRelaySubscriptionHandler({
  pusher: pusherClient,
  fetchOperation: fetchOperation,
  decompress: function(compressed) {
    // Decode base64
    const data = btoa(compressed)
    // Decompress
    const payloadString = pako.inflate(data, { to: 'string' })
    // Parse into an object
    return JSON.parse(payloadString);
  }
})
```

## Ably

Subscriptions with [Ably](/subscriptions/ably_implementation) require two things:

- A client from the [`ably-js` library](https://github.com/ably/ably-js)
- A [`fetchOperation` function](#fetchoperation-function) for sending the `subscription` operation to the server

### Ably client

Pass `ably:` to get Subscription updates over Ably:

```js
// Load the helper function
import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler"

// Load Ably and create a client
const Ably = require("ably")
const ablyClient = new Ably.Realtime({ key: "your-app-key" })

// create a fetchOperation, see below for more details
function fetchOperation(operation, variables, cacheConfig) {
  return fetch(...)
}

// Create a Relay Modern-compatible handler
var subscriptionHandler = createRelaySubscriptionHandler({
  ably: ablyClient,
  fetchOperation: fetchOperation
})

// Create a Relay Modern network with the handler
var network = Network.create(fetchQuery, subscriptionHandler)
```

## ActionCable

With this configuration, `subscription` queries will be routed to [ActionCable](/subscriptions/action_cable_implementation).

For example:

```js
// Require the helper function
import createRelaySubscriptionHandler from "graphql-ruby-client/subscriptions/createRelaySubscriptionHandler")
// Optionally, load your OperationStoreClient
var OperationStoreClient = require("./OperationStoreClient")

// Create a Relay Modern-compatible handler
var subscriptionHandler = createRelaySubscriptionHandler({
  cable: createConsumer(...),
  operations: OperationStoreClient,
})

// Create a Relay Modern network with the handler
var network = Network.create(fetchQuery, subscriptionHandler)
```

## With Relay Persisted Queries

If you're using Relay's built-in [persisted query support](https://relay.dev/docs/guides/persisted-queries/), you can pass `clientName:` to the handler in order to build IDs that work with the [OperationStore](/operation_store/overview.html). For example:

```js
var subscriptionHandler = createRelaySubscriptionHandler({
  cable: createConsumer(...),
  clientName: "web-frontend", // This should match the one you use for `sync`
})

// Create a Relay Modern network with the handler
var network = Network.create(fetchQuery, subscriptionHandler)
```

Then, the ActionCable handler will use Relay's provided operation IDs to interact with the OperationStore.

## fetchOperation function

The `fetchOperation` function can be extracted from your `fetchQuery` function. Its signature is:

```js
// Returns a promise from `fetch`
function fetchOperation(operation, variables, cacheConfig) {
  return fetch(...)
}
```

- `operation`, `variables`, and `cacheConfig` are the first three arguments to the `fetchQuery` function.
- The function should call `fetch` and return the result (a Promise of a `Response`).

For example, `Environment.js` may look like:

```js
// This function sends a GraphQL query to the server
const fetchOperation = function(operation, variables, cacheConfig) {
  const bodyValues = {
    variables,
    operationName: operation.name,
  }
  const useStoredOperations = process.env.NODE_ENV === "production"
  if (useStoredOperations) {
    // In production, use the stored operation
    bodyValues.operationId = OperationStoreClient.getOperationId(operation.name)
  } else {
    // In development, use the query text
    bodyValues.query = operation.text
  }
  return fetch('http://localhost:3000/graphql', {
    method: 'POST',
    opts: {
      credentials: 'include',
    },
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(bodyValues),
  })
}

// `fetchQuery` uses `fetchOperation`, but returns a Promise of JSON
const fetchQuery = (operation, variables, cacheConfig, uploadables) => {
  return fetchOperation(operation, variables, cacheConfig).then(response => {
    return response.json()
  })
}

// Subscriptions uses the same `fetchOperation` function for initial subscription requests
const subscriptionHandler = createRelaySubscriptionHandler({pusher: pusherClient, fetchOperation: fetchOperation})
// Combine them into a `Network`
const network = Network.create(fetchQuery, subscriptionHandler)
```

Since `OperationStoreClient` is in the `fetchOperation` function, it will apply to all GraphQL operations.

---

### Guides Javascript Client Sync (`guides/javascript_client/sync.md`)

# OperationStore Sync

JavaScript support for GraphQL projects using [graphql-pro](https://graphql.pro)'s `OperationStore` for persisted queries.

- [`sync` CLI](#sync-utility)
- [Relay <2 support](#use-with-relay-2)
- [Relay 2+ support](#use-with-relay-persisted-output)
- [Apollo Client support](#use-with-apollo-client)
- [Apollo Link support](#use-with-apollo-link)
- [Apollo Codegen Support](#use-with-apollo-codegen)
- [Apollo Android support](#use-with-apollo-android)
- [Apollo Persisted Queries Support](#use-with-apollo-persisted-queries)
- [Plain JS support](#use-with-plain-javascript)
- [Authorization](#authorization)

See the [OperationStore guide](/operation_store/overview) for server-side setup.

## `sync` utility

This package contains a command line utility, `graphql-ruby-client sync`:

```
$ graphql-ruby-client sync # ...
Authorizing with HMAC
Syncing 4 operations to http://myapp.com/graphql/operations...
  3 added
  1 not modified
  0 failed
Generating client module in app/javascript/graphql/OperationStoreClient.js...
✓ Done!
```

`sync` Takes several options:

option | description
--------|----------
`--url` | [Sync API](/operation_store/getting_started.html#add-routes) url
`--path` | Local directory to search for `.graphql` / `.graphql.js` files
`--relay-persisted-output` | Path to a `.json` file from `relay-compiler ... --persist-output`
`--apollo-codegen-json-output` | Path to a `.json` file from `apollo client:codegen ... --target json`
`--apollo-android-operation-output` | Path to an `OperationOutput.json` file from Apollo Android
`--client` | Client ID ([created on server](/operation_store/client_workflow))
`--secret` | Client Secret ([created on server](/operation_store/client_workflow))
`--outfile` | Destination for generated code
`--outfile-type` | What kind of code to generate (`js` or `json`)
`--header={key}:{value}` | Add a header to the outgoing HTTP request (may be repeated)
`--add-typename` | Add `__typename` to all selection sets (for use with Apollo Client)
`--verbose` | Output some debug information
`--changeset-version` | Set a [Changeset Version](/changesets/installation#controller-setup) when syncing these queries. (`context[:changeset_version]` will also be required at runtime, when running these stored operations.)
`--dump-payload` | A file to write the HTTP Post payload into, or if no filename is passed, then the payload will be written to stdout.

You can see these and a few others with `graphql-ruby-client sync --help`.

## Use with Relay <2

`graphql-ruby-client` can persist queries from `relay-compiler` using the embedded `@relayHash` value. (This was created in Relay before 2.0.0. See below for Relay 2.0+.)

To sync your queries with the server, use the `--path` option to point to your `__generated__` directory, for example:

```bash
# sync a Relay project
$ graphql-ruby-client sync --path=src/__generated__  --outfile=src/OperationStoreClient.js --url=...
```

Then, the generated code may be integrated with Relay's [Network Layer](https://relay.dev/docs/guides/network-layer/):

```js
// ...
// require the generated module:
const OperationStoreClient = require('./OperationStoreClient')

// ...
function fetchQuery(operation, variables, cacheConfig, uploadables) {
  const requestParams = {
    variables,
    operationName: operation.name,
  }

  if (process.env.NODE_ENV === "production")
    // In production, use the stored operation
    requestParams.operationId = OperationStoreClient.getOperationId(operation.name)
  } else {
    // In development, use the query text
    requestParams.query = operation.text,
  }

  return fetch('/graphql', {
    method: 'POST',
    headers: { /*...*/ },
    body: JSON.stringify(requestParams),
  }).then(/* ... */);
}

// ...
```

(Only Relay Modern is supported. Legacy Relay can't generate static queries.)

## Use With Relay Persisted Output

To use Relay's persisted output, add a `"file": ...` to your project's [`persistConfig` object](https://relay.dev/docs/guides/persisted-queries/). For example:

```json
  "relay": {
    ...
    "persistConfig": {
      "file": "./persisted-queries.json"
    }
  },
```

Then, push Relay's generated queries to your OperationStore server with `--relay-persisted-output`:

```
$ graphql-ruby-client sync --relay-persisted-output=path/to/persisted-queries.json --url=...
```

In this case, `sync` _won't_ generate a JavaScript module because `relay-compiler` has already prepared its queries for persisted use. Instead, update your network layer to include the _client name_ and _operation id_ in the HTTP params:

```js
const operationStoreClientName = "MyRelayApp";

function fetchQuery(operation, variables,) {
  return fetch('/graphql', {
    method: 'POST',
    headers: {
      'content-type': 'application/json'
    },
    body: JSON.stringify({
      // Pass the client name and the operation ID, joined by `/`
      documentId: operationStoreClientName + "/" + operation.id,
      // query: operation.text, // this is now obsolete because text is null
      variables,
    }),
  }).then(response => {
    return response.json();
  });
}
```

(Inspired by https://relay.dev/docs/guides/persisted-queries/#network-layer-changes.)

Now, your Relay app will only send operation IDs over the wire to the server.

## Use with Apollo Client

Use the `--path` option to point at your `.graphql` files:

```
$ graphql-ruby-client sync --path=src/graphql/ --url=...
```

Then, load the generated module and add its `.apolloMiddleware` to your network interface with `.use([...])`:

```js
// load the generated module
var OperationStoreClient = require("./OperationStoreClient")

// attach it as middleware in production
// (in development, send queries to the server as normal)
if (process.env.NODE_ENV === "production") {
  MyNetworkInterface.use([OperationStoreClient.apolloMiddleware])
}
```

Now, the middleware will replace query strings with `operationId`s.

## Use with Apollo Link

Use the `--path` option to point at your `.graphql` files:

```
$ graphql-ruby-client sync --path=src/graphql/ --url=...
```

Then, load the generated module and add its `.apolloLink` to your Apollo Link:

```js
// load the generated module
var OperationStoreClient = require("./OperationStoreClient")

// Integrate the link to another link:
const link = ApolloLink.from([
  authLink,
  OperationStoreClient.apolloLink,
  httpLink,
])

// Create a client
const client = new ApolloClient({
  link: link,
  cache: new InMemoryCache(),
});
```

__Update the controller__: Apollo Link supports extra parameters _nested_ as `params[:extensions][:operationId]`, so update your controller to add that param to context:

```ruby
# app/controllers/graphql_controller.rb
context = {
  # ...
  # Support Apollo Link:
  operation_id: params[:extensions][:operationId]
}
```

Now, `context[:operation_id]` will be used to fetch a query from the database.

## Use with Apollo Codegen

Use `apollo client:codegen ... --target json` to build a JSON artifact containing your app's queries. Then, pass the path to that artifact to `graphql-ruby-client sync --apollo-codegen-json-output path/to/output.json ...`. `sync` will use Apollo-generated `operationId`s to populate the `OperationStore`.

Then, to use Apollo-style persisted query IDs, hook up the __Persisted Queries Link__ as described in [Apollo's documentation](https://www.apollographql.com/docs/react/api/link/persisted-queries/)

Finally, __update the controller__ to pass the Apollo-style persisted query ID as the operation ID:

```ruby
# app/controllers/graphql_controller.rb
context = {
  # ...
  # Support already-synced Apollo Persisted Queries:
  operation_id: params[:extensions][:operationId]
}
```

Now, Apollo-style persisted query IDs will be used to fetch operations from the server's `OperationStore`.

## Use with Apollo Android

Apollo Android's [generateOperationOutput option](https://www.apollographql.com/docs/android/advanced/persisted-queries/#operationoutputjson) builds an `OperationOutput.json` file which works with the OperationStore. To sync those queries, __use the `--apollo-android-operation-output` option__:

```sh
graphql-ruby-client sync --apollo-android-operation-output=path/to/OperationOutput.json --url=...
```

That way, the OperationStore will use the query IDs generated by Apollo Android.

On the server, you'll have to __update your controller__ to receive the client name and the operation ID. For example:

```ruby
# app/controllers/graphql_controller.rb
context = { ... }

# Check for an incoming operation ID from Apollo Client:
apollo_android_operation_id = request.headers["X-APOLLO-OPERATION-ID"]
if apollo_android_operation_id.present?
  # Check the incoming request to confirm that
  # it's your first-party client with stored operations
  client_name = # ...
  if client_name.present?
    # If we received an incoming operation ID
    # _and_ identified the client, run a persisted operation.
    context[:operation_id] = "#{client_name}/#{apollo_android_operation_id}"
  end
end
```

You may also have to __update your app__ to send an identifier, so that the server can determine the "client name" used with the operation store. (Apollo Android sends a query hash, but the operation store expects IDs in the form `#{client_name}/#{query_hash}`.)

## Use with Apollo Persisted Queries

Apollo client has a [Persisted Queries Link](https://www.apollographql.com/docs/react/api/link/persisted-queries/). You can use that link with GraphQL-Pro's [OperationStore](/operation_store/overview). First, create a manifest with [`generate-persisted-query-manifest`](https://www.apollographql.com/docs/react/api/link/persisted-queries/#1-generate-operation-manifests), then, pass the path to that file to `sync`:

```sh
$ graphql-ruby-client sync --apollo-persisted-query-manifest=path/to/manifest.json ...
```

Then, configure Apollo Client to [use your persisted query manifest](https://www.apollographql.com/docs/react/api/link/persisted-queries/#persisted-queries-implementation).

Finally, update your controller to receive the operation ID and pass it as `context[:operation_id]`:

```ruby
client_name = "..." # TODO: send the client name as a query param or header
persisted_query_hash = params[:extensions][:persistedQuery][:sha256Hash]
context = {
  # ...
  operation_id: "#{client_name}/#{persisted_query_hash}"
}
```

The `operation_id` will also need your client name. Using Apollo Client, you could send this as a [custom header](https://www.apollographql.com/docs/react/networking/basic-http-networking/#customizing-request-headers) or another way that works for your application (eg, session or user agent).

## Use with plain JavaScript

`OperationStoreClient.getOperationId` takes an operation name as input and returns the server-side alias for that operation:

```js
var OperationStoreClient = require("./OperationStoreClient")

OperationStoreClient.getOperationId("AppHomeQuery")       // => "my-frontend-app/7a8078c7555e20744cb1ff5a62e44aa92c6e0f02554868a15b8a1cbf2e776b6f"
OperationStoreClient.getOperationId("ProductDetailQuery") // => "my-frontend-app/6726a3b816e99b9971a1d25a1205ca81ecadc6eb1d5dd3a71028c4b01cc254c1"
```

Post the `operationId` in your GraphQL requests:

```js
// Lookup the operation name:
var operationId = OperationStoreClient.getOperationId(operationName)

// Include it in the params:
$.post("/graphql", {
  operationId: operationId,
  variables: queryVariables,
}, function(response) {
  // ...
})
```

## Authorization

`OperationStore` uses HMAC-SHA256 to [authenticate requests](/operation_store/access_control).

Pass the key to `graphql-ruby-client sync` as `--secret` to authenticate it:

```bash
$ export MY_SECRET_KEY= "abcdefg..."
$ graphql-ruby-client sync ... --secret=$MY_SECRET_KEY
# ...
Authenticating with HMAC
# ...
```

---

### Guides Javascript Client Urql Subscriptions (`guides/javascript_client/urql_subscriptions.md`)

# urql Subscriptions

GraphQL-Ruby currently supports using `urql` with the [ActionCable](/subscriptions/action_cable_implementation) and [Pusher implementation](/subscriptions/pusher_implementation).

## Pusher

```js
import SubscriptionExchange from "graphql-ruby-client/subscriptions/SubscriptionExchange"
import Pusher from "pusher"
import { Client, defaultExchanges, subscriptionExchange } from 'urql'

const pusherClient = new Pusher("your-app-key", { cluster: "us2" })
const forwardToPusher = SubscriptionExchange.create({ pusher: pusherClient })

const client = new Client({
  url: '/graphql',
  exchanges: [
    ...defaultExchanges,
    subscriptionExchange({
      forwardSubscription: forwardToPusher
    }),
  ],
});
```

## ActionCable

```js
import { createConsumer } from "@rails/actioncable";
import SubscriptionExchange from "graphql-ruby-client/subscriptions/SubscriptionExchange"

const actionCable = createConsumer('ws://127.0.0.1:3000/cable');
const forwardToActionCable = SubscriptionExchange.create({ consumer: actionCable })

const client = new Client({
  url: '/graphql',
  exchanges: [
    ...defaultExchanges,
    subscriptionExchange({
      forwardSubscription: forwardToActionCable
    }),
  ],
});
```

Want to use `urql` with another subscription backend? Please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=Using+urql+with+...&body=).

---

### Guides Language Tools C Parser (`guides/language_tools/c_parser.md`)

# C-based Parser

GraphQL-Ruby includes a plain-Ruby parser, but a faster parser is available as a C extension. To use it, add the [`graphql-c_parser` gem](https://rubygems.org/gems/graphql-c_parser) to your project, for example:

```ruby
bundle add graphql-c_parser
```

When `graphql-c_parser` is `require`d by your app, the C-based parser is installed as the default parser (as [GraphQL.default_parser](rdoc-ref:GraphQL.default_parser)). Bundler requires the library automatically, but you can also require it manually:

```ruby
require "graphql/c_parser"
```

This alternative parser is faster and uses less memory.

The library also adds `GraphQL.scan_with_c` and `GraphQL.parse_with_c` for calling the C-based parser directly.

---

### Guides Language Tools Visitor (`guides/language_tools/visitor.md`)

# AST Visitor

GraphQL code is usually contained in a string, for example:

```ruby
query_string = "query { user(id: \"1\") { userName } }"
```

You can perform programmatic analysis and modifications to GraphQL code using a three-step process:

- __Parse__ the code into an abstract syntax tree
- __Analyze/Modify__ the code with a visitor
- __Print__ the code back to a string

## Parse

[GraphQL.parse](rdoc-ref:GraphQL.parse) turns a string into a GraphQL document:

```ruby
parsed_doc = GraphQL.parse("{ user(id: \"1\") { userName } }")
# => #<GraphQL::Language::Nodes::Document ...>
```

Also, [GraphQL.parse_file](rdoc-ref:GraphQL.parse_file) parses the contents of the named file and includes a `filename` in the parsed document.

#### AST Nodes

The parsed document is a tree of nodes, called an _abstract syntax tree_ (AST). This tree is _immutable_: once a document has been parsed, those Ruby objects can't be changed. Modifications are performed by _copying_ existing nodes, applying changes to the copy, then making a new tree to hold the copied node. Where possible, unmodified nodes are retained in the new tree (it's _persistent_).

The copy-and-modify workflow is supported by a few methods on the AST nodes:

- `.merge(new_attrs)` returns a copy of the node with `new_attrs` applied. This new copy can replace the original node.
- `.add_{child}(new_child_attrs)` makes a new node with `new_child_attrs`, adds it to the array specified by `{child}`, and returns a copy whose `{children}` array contains the newly created node.

For example, to rename a field and add an argument to it, you could:

```ruby
modified_node = field_node
  # Apply a new name
  .merge(name: "newName")
  # Add an argument to this field's arguments
  .add_argument(name: "newArgument", value: "newValue")
```

Above, `field_node` is unmodified, but `modified_node` reflects the new name and new argument.

## Analyze/Modify

To inspect or modify a parsed document, extend [GraphQL::Language::Visitor](rdoc-ref:GraphQL::Language::Visitor) and implement its various hooks. It's an implementation of the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern). In short, each node of the tree will be "visited" by calling a method, and those methods can gather information and perform modifications.

In the visitor, each node class has a hook, for example:

- [GraphQL::Language::Nodes::Field](rdoc-ref:GraphQL::Language::Nodes::Field)s are routed to `#on_field`
- [GraphQL::Language::Nodes::Argument](rdoc-ref:GraphQL::Language::Nodes::Argument)s are routed to `#on_argument`

See the [GraphQL::Language::Visitor](rdoc-ref:GraphQL::Language::Visitor) API docs for a full list of methods.

Each method is called with `(node, parent)`, where:

- `node` is the AST node currently visited
- `parent` is the AST node above this node in the tree

The method has a few options for analyzing or modifying the AST:

#### Continue/Halt

To continue visiting, the hook should call `super`. This allows the visit to continue to `node`'s children in the tree, for example:

```ruby
def on_field(_node, _parent)
  # Do nothing, this is the default behavior:
  super
end
```

To _halt_ the visit, a method may skip the call to `super`. For example, if the visitor encountered an error, it might want to return early instead of continuing to visit.

#### Modify a Node

Visitor hooks are expected to return the `(node, parent)` they are called with. If they return a different node, then that node will replace the original `node`. When you call `super(node, parent)`, the `node` is returned. So, to modify a node and continue visiting:

- Make a modified copy of `node`
- Pass the modified copy to `super(new_node, parent)`

For example, to rename an argument:

```ruby
def on_argument(node, parent)
  # make a copy of `node` with a new name
  modified_node = node.merge(name: "renamed")
  # continue visiting with the modified node and parent
  super(modified_node, parent)
end
```

#### Delete a Node

To delete the currently-visited `node`, don't pass `node` to `super(...)`. Instead, pass a magic constant, `DELETE_NODE`, in place of `node`.

For example, to delete a directive:

```ruby
def on_directive(node, parent)
  # Don't pass `node` to `super`,
  # instead, pass `DELETE_NODE`
  super(DELETE_NODE, parent)
end
```

#### Insert a Node

Inserting nodes is similar to modifying nodes. To insert a new child into `node`, call one of its `.add_` helpers. This returns a copied node with a new child added. For example, to add a selection to a field's selection set:

```ruby
def on_field(node, parent)
  node_with_selection = node.add_selection(name: "emailAddress")
  super(node_with_selection, parent)
end
```

This will add `emailAddress` the fields selection on `node`.


(These `.add_*` helpers are wrappers around [GraphQL::Language::Nodes::AbstractNode#merge](rdoc-ref:GraphQL::Language::Nodes::AbstractNode#merge).)

## Print

The easiest way to turn an AST back into a string of GraphQL is [GraphQL::Language::Nodes::AbstractNode#to_query_string](rdoc-ref:GraphQL::Language::Nodes::AbstractNode#to_query_string), for example:

```ruby
parsed_doc.to_query_string
# => '{ user(id: "1") { userName } }'
```

You can also create a subclass of [GraphQL::Language::Printer](rdoc-ref:GraphQL::Language::Printer) to customize how nodes are printed.

---

### Guides Limiters Active Operations (`guides/limiters/active_operations.md`)

# Active Operation Limiter

`GraphQL::Enterprise::ActiveOperationLimiter` prevents clients from running too many GraphQL operations at the same time. It uses [Redis](/limiters/redis) to track currently-running operations.

## Why?

Some clients may suddently swamp a server with tons of requests, occupying all available Ruby processes and therefore interrupting service for other clients. This limiter aims to prevent that at the GraphQL level by halting queries when a client already has lots of queries running. That way, server processes will remain available for other clients' requests.

## Setup

To use this limiter, update the schema configuration and include `context[:limiter_key]` in your queries.

#### Schema Setup

To setup the schema, add `use GraphQL::Enterprise::ActiveOperationLimiter` with a default `limit:` value:

```ruby
class MySchema < GraphQL::Schema
  # ...
  use GraphQL::Enterprise::ActiveOperationLimiter,
    redis: Redis.new(...),
    # Or:
    # connection_pool: ...
    # redis_cluster: ...
    limit: 5
end
```

`limit: false` may also be given, which defaults to _no limit_ for this limiter.

It also accepts a `stale_request_seconds:` option. The limiter uses that value to clean up request data in case of a crash or other unexpected scenario.

Before requests will actually be halted, [soft mode](/limiters/deployment#soft-limits) must be disabled.

#### Query Setup

In order to limit clients, the limiter needs a client identifier for each GraphQL operation. By default, it checks `context[:limiter_key]` to find it:

```ruby
context = {
  viewer: current_user,
  # for example:
  limiter_key: logged_in? ? "user:#{current_user.id}" : "anon-ip:#{request.remote_ip}",
  # ...
}

result = MySchema.execute(query_str, context: context)
```

Operations with the same `context[:limiter_key]` will rate limited in the same buckets. A limiter key is required; if a query is run without one, the limiter will raise an error.

To provide a client identifier another way, see [Customization](#customization).

## Customization

`GraphQL::Enterprise::ActiveOperationLimiter` provides several hooks for customizing its behavior. To use these, make a subclass of the limiter and override methods as described:

```ruby
# app/graphql/limiters/active_operations.rb
class Limiters::ActiveOperations < GraphQL::Enterprise::ActiveOperationsLimiter
  # override methods here
end
```

The hooks are:

- `def limiter_key(query)` should return a string which identifies the current client for `query`.
- `def limit_for(key, query)` should return an integer or `nil`. If an integer is returned, that limit is applied for the current query. If `nil` is returned, no limit is applied to the current query.
- `def soft_limit?(key, query)` can be implemented to customize the application of "soft mode". By default, it checks a setting in redis.
- `def handle_redis_error(err)` is called when the limit rescues an error from Redis. By default, it's passed to `warn` and the query is _not_ halted.

## Instrumentation

While the limiter is installed, it adds some information to the query context about its operation. It can be accessed at `context[:active_operation_limiter]`:

```ruby
result = MySchema.execute(...)

pp result.context[:active_operation_limiter]
# {:key=>"user:123", :limit=>2, :soft=>false, :limited=>true}
```

It returns a Hash containing:

- `key: [String]`, the limiter key used for this query
- `limit: [Integer, nil]`, the limit applied to this query
- `soft: [Boolean]`, `true` if the query was run in "soft mode"
- `limited: [Boolean]`, `true` if the query exceeded the rate limit (but if `soft:` was also `true`, then the query was _not_ halted)

You could use this to add detailed metrics to your application monitoring system, for example:

```ruby
MyMetrics.increment("graphql.active_operation_limiter", tags: result.context[:active_operation_limiter])
```

---

### Guides Limiters Deployment (`guides/limiters/deployment.md`)

# Deploying Rate Limiters

Here are a few options for deploying GraphQL-Enterprise's rate limiters:


- The [Dashboard](#dashboard) shows some basic metrics about the limiter.
- [Soft limits](#soft-limits) start logging over-limit requests to the dashboard but don't actually halt traffic.
- [Subscriptions](#subscriptions) need extra consideration


## Dashboard

Once installed, your [GraphQL-Pro dashboard](/pro/dashboard) will include a simple metrics view:



To disable dashboard charts, add `use(... dashboard_charts: false)` to your configuration.

Also, the dashboard includes a link to enable or disable "soft mode":



When "soft mode" is enabled, limited requests are _not_ actually halted (although they are _counted_). When "soft mode" is disabled, any over-limit requests are halted.

For more detailed metrics, see the "Instrumentation" section of the documentation for each limiter.

## Soft Limits

By default, limiters don't actually halt queries; instead, they start out in "soft mode". In this mode:

- limited/unlimited requests are counted in the [Dashboard](#dashboard)
- but, no requests are actually halted

This mode is for assessing the impact of the limiter before it's applied to production traffic. Additionally, if you release the limiter but find that it's affecting production traffic adversely, you can re-enable "soft mode" to stop blocking traffic.

To disable "soft mode" and start limiting, use the [Dashboard](#dashboard) or re-implement some of the customization methods of the limiter.

You can also disable "soft mode" in Ruby:

```ruby
# Turn "soft mode" off for the ActiveOperationLimiter
MySchema.enterprise_active_operation_limiter.set_soft_limit(false)
# or, for RuntimeLimiter
MySchema.enterprise_runtime_limiter.set_soft_limit(false)
```


## Subscriptions

If you're using [PusherSubscriptions](/subscriptions/pusher_implementation) or [AblySubscriptions](/subscriptions/ably_implementation), then you'll need to accomodate subscriptions that were created _before_ you deployed the rate limiter. Those subscriptions are already stored in Redis and their contexts _don't_ include the required `limiter_key:` value.

To address this, you can customize the limiter(s) you're using to provide a default value in this case. For example:

```ruby
class CustomRuntimeLimiter < GraphQL::Enterprise::RuntimeLimiter
  def limiter_key(query)
    if query.subscription_update? && query.context[:limiter_key].nil?
      # This subscription was created before limiter_key was required,
      # so provide a value for it.
      # If `context` includes enough information to create a
      # "real" limiter key, you could also do that here.
      # In this case, we're providing a default flag:
      "legacy-subscription-update"
    else
      super
    end
  end

  def limit_for(key, query)
    if key == "legacy-subscription-update"
      nil # no limit in this case
    else
      super
    end
  end
end
```

With methods like that, any subscriptions created _before_ `limiter_key:` was required will not be subject to rate limits. Adjust those methods as needed for your application. Finally, be sure to attach your custom limiter in your schema, for example:


```ruby
# Use a custom subclass of GraphQL::Enterprise::RuntimeLimiter:
use CustomRuntimeLimiter, ...
```

---

### Guides Limiters Redis (`guides/limiters/redis.md`)

# Configuring Redis

Rate limiting requires a persistent Redis instance, just like [Sidekiq](https://github.com/mperham/sidekiq/wiki/Using-Redis) or the [Operation Store](/operation_store/redis_backend). Set `maxmemory-policy noeviction` in `redis.conf` to ensure that Redis doesn't silently drop keys when it reaches its memory limit.

## Memory Usage

Estimating memory usage depends on the string used to identify clients, since those are used in the Redis keys. Using 100-character client keys, the runtime limiter uses 400 bytes per client (two keys). Memory usage by the active operation limiter depends on the limit because each concurrent operation uses some memory; a higher limit permits more concurrent operations. With 10 active operations and a 100-character client key, the active operation limiter uses 350 bytes per client. Additionally, the limiters use up to 35kb for dashboards (2 limiters, for each one: 2x 60 per-minute keys, 24 hourly keys, and 30 daily keys @ 72 bytes per key).

By those estimates, 1 gigabyte of memory would support both rate limiters for over 1.4 million active clients.

## Connection Pool

`ActiveOperationsLimiter` and `RuntimeLimiter` support [ConnectionPool](https://github.com/mperham/connection_pool). To use it, pass `connection_pool:`:

```ruby
use GraphQL::Enterprise::RuntimeLimiter, # or ActiveOperationLimiter
  connection_pool: ConnectionPool.new(...) { ... }
  # ...
```

## Redis Cluster

`ActiveOperationsLimiter` and `RuntimeLimiter` support [`redis-cluster`](https://github.com/redis/redis-rb/tree/master/cluster). To use it, pass `redis_cluster:`:

```ruby
use GraphQL::Enterprise::RuntimeLimiter, # or ActiveOperationLimiter
  redis_cluster: Redis::Cluster.new(...)
  # ...
```

---

### Guides Limiters Runtime (`guides/limiters/runtime.md`)

# Runtime Limiter

`GraphQL::Enterprise::RuntimeLimiter` applies an upper bound to processing time consumed by a single client. It uses [Redis](/limiters/redis) track time with a [token bucket](https://en.wikipedia.org/wiki/Token_bucket) algorithm.

## Why?

This limiter prevents a single client from consuming too much processing time, regardless of whether it comes a burst of short-lived queries (which the [Active Operation Limiter](/limiters/active_operations) can prevent) or a small number of long-running queries. Unlike request counters or complexity calculations, the runtime limiter pays no attention to the structure of the incoming request. Instead, it simply measures the time spent on the request _as a whole_ and halts queries when a client consumes more than the limit.

## Setup

To use this limiter, update the schema configuration and include `context[:limiter_key]` in your queries.

### Schema Setup

To setup the schema, add `use GraphQL::Enterprise::RuntimeLimiter` with a default `limit_ms:` value:

```ruby
class MySchema < GraphQL::Schema
  # ...
  use GraphQL::Enterprise::RuntimeLimiter,
    redis: Redis.new(...),
    # Or:
    # connection_pool: ...
    # redis_cluster: ...
    limit_ms: 90 * 1000 # 90 seconds per minute
end
```

`limit_ms: false` may also be given, which defaults to _no limit_ for this limiter.

It also accepts a `window_ms:` option, which is the duration over which `limit_ms:` is added to a client's bucket. It defaults to `60_000` (one minute).

Before requests will actually be halted, [soft mode](/limiters/deployment#soft-limits) must be disabled.

### Query Setup

In order to limit clients, the limiter needs a client identifier for each GraphQL operation. By default, it checks `context[:limiter_key]` to find it:

```ruby
context = {
  viewer: current_user,
  # for example:
  limiter_key: logged_in? ? "user:#{current_user.id}" : "anon-ip:#{request.remote_ip}",
  # ...
}

result = MySchema.execute(query_str, context: context)
```

Operations with the same `context[:limiter_key]` will rate limited in the same buckets. A limiter key is required; if a query is run without one, the limiter will raise an error.

To provide a client identifier another way, see [Customization](#customization).

## Customization

`GraphQL::Enterprise::RuntimeLimiter` provides several hooks for customizing its behavior. To use these, make a subclass of the limiter and override methods as described:

```ruby
# app/graphql/limiters/runtime.rb
class Limiters::Runtime < GraphQL::Enterprise::RuntimeLimiter
  # override methods here
end
```

The hooks are:

- `def limiter_key(query)` should return a string which identifies the current client for `query`.
- `def limit_for(key, query)` should return an integer or `nil`. If an integer is returned, that limit is applied for the current query. If `nil` is returned, no limit is applied to the current query.
- `def soft_limit?(key, query)` can be implemented to customize the application of "soft mode". By default, it checks a setting in redis.
- `def handle_redis_error(err)` is called when the limit rescues an error from Redis. By default, it's passed to `warn` and the query is _not_ halted.

## Instrumentation

While the limiter is installed, it adds some information to the query context about its operation. It can be accessed at `context[:runtime_limiter]`:


```ruby
result = MySchema.execute(...)

pp result.context[:runtime_limiter]
# {:key=>"custom-key-9",
#  :limit_ms=>800,
#  :remaining_ms=>0,
#  :soft=>true,
#  :limited=>true,
#  :window_ms=>60_000}
```

It returns a Hash containing:

- `key: [String]`, the limiter key used for this query
- `limit_ms: [Integer, nil]`, the limit applied to this query
- `remaining_ms: [Integer, nil]`, the amount of time remaining in this client's bucket
- `soft: [Boolean]`, `true` if the query was run in "soft mode"
- `limited: [Boolean]`, `true` if the query exceeded the rate limit (but if `soft:` was also `true`, then the query was _not_ halted)
- `window_ms: [Integer]` the configured `window_ms:` for the limiter

You could use this to add detailed metrics to your application monitoring system, for example:

```ruby
MyMetrics.increment("graphql.runtime_limiter", tags: result.context[:runtime_limiter])
```

## Some Caveats

The limiter will not _interrupt_ a long-running field. Instead, it stops executing new fields after a client exceeds its allowed processing time. This is because interrupting arbitrary code may have unintended consequences for I/O operations, see ["Timeout: Ruby's most dangerous API"](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/).

Also, the limiter only checks remaining time at the _start_ of a query and it only decreases the remaining time at the _end_ of a query. This means that simulaneous queries may consume the remainder at the same time. Use the [Active Operation Limiter](/limiters/active_operations) to limit behavior in this regard. This implementation is basically a trade-off: more granular updates would require more communication with Redis which would add overhead to each request.

---

### Guides Mutations Mutation Authorization (`guides/mutations/mutation_authorization.md`)

# Mutation authorization

Before running a mutation, you probably want to do a few things:

- Make sure the current user has permission to try this mutation
- Load some objects from the database, using some `ID` inputs
- Check if the user has permission to modify those loaded objects

This guide describes how to accomplish that workflow with GraphQL-Ruby.

## Checking conditions before instantiating the mutation

```ruby
class UpdateUserMutation < BaseMutation
  # ...

  def resolve(update_user_input:, user:)
    # ...
  end

  def self.authorized?(obj, ctx)
    super && ctx[:viewer].present?
  end
end
```

## Checking the user permissions

Before loading any data from the database, you might want to see if the user has a certain permission level. For example, maybe only `.admin?` users can run `Mutation.promoteEmployee`.

This check can be implemented using the `#ready?` method in a mutation:

```ruby
class Mutations::PromoteEmployee < Mutations::BaseMutation
  def ready?(**args)
    # Called with mutation args.
    # Use keyword args such as employee_id: or **args to collect them
    if !context[:current_user].admin?
      raise GraphQL::ExecutionError, "Only admins can run this mutation"
    else
      # Return true to continue the mutation:
      true
    end
  end

  # ...
end
```

Now, when any non-`admin` user tries to run the mutation, it won't run. Instead, they'll get an error in the response.

Additionally, `#ready?` may return `false, { ... }` to return [errors as data](/mutations/mutation_errors.html#errors-as-data):

```ruby
def ready?
  if !context[:current_user].allowed?
    return false, { errors: ["You don't have permission to do this"]}
  else
    true
  end
end
```

## Loading and authorizing objects

Often, mutations take `ID`s as input and use them to load records from the database. GraphQL-Ruby can load IDs for you when you provide a `loads:` option.

In short, here's an example:


```ruby
class Mutations::PromoteEmployee < Mutations::BaseMutation
  # `employeeId` is an ID, Types::Employee is an _Object_ type
  argument :employee_id, ID, loads: Types::Employee

  # Behind the scenes, `:employee_id` is used to fetch an object from the database,
  # then the object is authorized with `Employee.authorized?`, then
  # if all is well, the object is injected here:
  def resolve(employee:)
    employee.promote!
  end
end
```

It works like this: if you pass a `loads:` option, it will:

- Automatically remove `_id` from the name and pass that name for the `as:` option
- Add a prepare hook to fetch an object with the given `ID` (using [Schema.object_from_id](rdoc-ref:GraphQL::Schema.object_from_id))
- Check that the fetched object's type matches the `loads:` type (using [Schema.resolve_type](rdoc-ref:GraphQL::Schema.resolve_type))
- Run the fetched object through its type's `.authorized?` hook (see [Authorization](/authorization/authorization))
- Inject it into `#resolve` using the object-style name (`employee:`)

In this case, if the argument value is provided by `object_from_id` doesn't return a value, the mutation will fail with an error.

Alternatively if your `ID` doesn't specify both class _and_ id, resolvers have a `load_#{argument}` method that can be overridden.

```ruby
argument :employee_id, ID, loads: Types::Employee

def load_employee(id)
  ::Employee.find(id)
end
```

If you don't want this behavior, don't use it. Instead, create arguments with type `ID` and use them your own way, for example:

```ruby
# No special loading behavior:
argument :employee_id, ID
```

## Can _this user_ perform _this action_?

Sometimes you need to authorize a specific user-object(s)-action combination. For example, `.admin?` users can't promote _all_ employees! They can only promote employees which they manage.

You can add this check by implementing a `#authorized?` method, for example:

```ruby
def authorized?(employee:)
  super && context[:current_user].manager_of?(employee)
end
```

When `#authorized?` returns `false` (or something falsey), the mutation will be halted. If it returns `true` (or something truthy), the mutation will continue.

#### Adding errors

To add errors as data (as described in [Mutation errors](/mutations/mutation_errors.html#errors-as-data)), return a value _along with_ `false`, for example:

```ruby
def authorized?(employee:)
  super && if context[:current_user].manager_of?(employee)
    true
  else
    return false, { errors: ["Can't promote an employee you don't manage"] }
  end
end
```

Alternatively, you can add top-level errors by raising `GraphQL::ExecutionError`, for example:

```ruby
def authorized?(employee:)
  super && if context[:current_user].manager_of?(employee)
    true
  else
    raise GraphQL::ExecutionError, "You can only promote your _own_ employees"
  end
end
```

In either case (returning `[false, data]` or raising an error), the mutation will be halted.

## Finally, doing the work

Now that the user has been authorized in general, data has been loaded, and objects have been validated in particular, you can modify the database using `#resolve`:

```ruby
def resolve(employee:)
  if employee.promote
    {
      employee: employee,
      errors: [],
    }
  else
    # See "Mutation Errors" for more:
    {
      errors: employee.errors.full_messages
    }
  end
end
```

---

### Guides Mutations Mutation Classes (`guides/mutations/mutation_classes.md`)

# Mutation Classes

GraphQL _mutations_ are special fields: instead of reading data or performing calculations, they may _modify_ the application state. For example, mutation fields may:

- Create, update or destroy records in the database
- Establish associations between already-existing records in the database
- Increment counters
- Create, modify or delete files
- Clear caches

These actions are called _side effects_.

Like all GraphQL fields, mutation fields:

- Accept inputs, called _arguments_
- Return values via _fields_

GraphQL-Ruby includes two classes to help you write mutations:

- [GraphQL::Schema::Mutation](rdoc-ref:GraphQL::Schema::Mutation), a bare-bones base class
- [GraphQL::Schema::RelayClassicMutation](rdoc-ref:GraphQL::Schema::RelayClassicMutation), a base class with a set of nice conventions that also supports the Relay Classic mutation specification.

Besides those, you can also use the plain [field API](/type_definitions/objects#fields) to write mutation fields.

## Example mutation class

If you used the [install generator](/schema/generators#graphqlinstall), a base mutation class will already have been generated for you. If that's not the case, you should add a base class to your application, for example:

```ruby
class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  # Add your custom classes if you have them:
  # This is used for generating payload types
  object_class Types::BaseObject
  # This is used for return fields on the mutation's payload
  field_class Types::BaseField
  # This is used for generating the `input: { ... }` object type
  input_object_class Types::BaseInputObject
end
```

Then extend it for your mutations:

```ruby
class Mutations::CreateComment < Mutations::BaseMutation
  null true
  argument :body, String
  argument :post_id, ID

  field :comment, Types::Comment
  field :errors, [String], null: false

  def resolve(body:, post_id:)
    post = Post.find(post_id)
    comment = post.comments.build(body: body, author: context[:current_user])
    if comment.save
      # Successful creation, return the created object with no errors
      {
        comment: comment,
        errors: [],
      }
    else
      # Failed save, return the errors to the client
      {
        comment: nil,
        errors: comment.errors.full_messages
      }
    end
  end
end
```

The `#resolve` method should return a hash whose symbols match the `field` names.

(See [Mutation Errors](/mutations/mutation_errors) for more information about returning errors.)

Also, you can configure `null(false)` in your mutation class to make the generated payload class non-null.

## Hooking up mutations

Mutations must be attached to the mutation root using the `mutation:` keyword, for example:

```ruby
class Types::Mutation < Types::BaseObject
  field :create_comment, mutation: Mutations::CreateComment
end
```

## Auto-loading arguments

In most cases, a GraphQL mutation will act against a given global relay ID. Loading objects from these global relay IDs can require a lot of boilerplate code in the mutation's resolver.

An alternative approach is to use the `loads:` argument when defining the argument:

```ruby
class Mutations::AddStar < Mutations::BaseMutation
  argument :post_id, ID, loads: Types::Post

  field :post, Types::Post

  def resolve(post:)
    post.star

    {
      post: post,
    }
  end
end
```

By specifying that the `post_id` argument loads a `Types::Post` object type, a `Post` object will be loaded via [`Schema.object_from_id`](/schema/definition.html#object-identification) with the provided `post_id`.

All arguments that end in `_id` and use the `loads:` method will have their `_id` suffix removed. For example, the mutation resolver above receives a `post` argument which contains the loaded object, instead of a `post_id` argument.

The `loads:` option also works with list of IDs, for example:

```ruby
class Mutations::AddStars < Mutations::BaseMutation
  argument :post_ids, [ID], loads: Types::Post

  field :posts, [Types::Post]

  def resolve(posts:)
    posts.map(&:star)

    {
      posts: posts,
    }
  end
end
```

All arguments that end in `_ids` and use the `loads:` method will have their `_ids` suffix removed and an `s` appended to their name. For example, the mutation resolver above receives a `posts` argument which contains all the loaded objects, instead of a `post_ids` argument.

In some cases, you may want to control the resulting argument name. This can be done using the `as:` argument, for example:

```ruby
class Mutations::AddStar < Mutations::BaseMutation
  argument :post_id, ID, loads: Types::Post, as: :something

  field :post, Types::Post

  def resolve(something:)
    something.star

    {
      post: something
    }
  end
end
```

In the above examples, `loads:` is provided a concrete type, but it also supports abstract types (i.e. interfaces and unions).

### Resolving the type of loaded objects

When `loads:` gets an object from [Schema.object_from_id](rdoc-ref:GraphQL::Schema.object_from_id), it passes that object to [Schema.resolve_type](rdoc-ref:GraphQL::Schema.resolve_type) to confirm that it resolves to the same type originally configured with `loads:`.

### Handling failed loads

If `loads:` fails to find an object or if the loaded object isn't resolved to the specified `loads:` type (using [Schema.resolve_type](rdoc-ref:GraphQL::Schema.resolve_type)), a [GraphQL::LoadApplicationObjectFailedError](rdoc-ref:GraphQL::LoadApplicationObjectFailedError) is raised and returned to the client.

You can customize this behavior by implementing `def load_application_object_failed` in your mutation class, for example:

```ruby
def load_application_object_failed(error)
  raise GraphQL::ExecutionError, "Couldn't find an object for ID: `#{error.id}`"
end
```

Or, if `load_application_object_failed` returns a new object, that object will be used as the `loads:` result.

### Handling unauthorized loaded objects

When an object is _loaded_ but fails its [`.authorized?` check](/authorization/authorization#object-authorization), a [GraphQL::UnauthorizedError](rdoc-ref:GraphQL::UnauthorizedError) is raised. By default, it's passed to [Schema.unauthorized_object](rdoc-ref:GraphQL::Schema.unauthorized_object) (see [Handling Unauthorized Objects](/authorization/authorization.html#handling-unauthorized-objects)). You can customize this behavior by implementing `def unauthorized_object(err)` in your mutation, for example:

```ruby
def unauthorized_object(error)
  # Raise a nice user-facing error instead
  raise GraphQL::ExecutionError, "You don't have permission to modify the loaded #{error.type.graphql_name}."
end
```

---

### Guides Mutations Mutation Errors (`guides/mutations/mutation_errors.md`)

# Mutation errors

How can you handle errors inside mutations? Let's explore a couple of options.

## Raising Errors

One way to handle an error is by raising, for example:

```ruby
def resolve(id:, attributes:)
  # Will crash the query if the data is invalid:
  Post.find(id).update!(attributes.to_h)
  # ...
end
```

Or:

```ruby
def resolve(id:, attributes:)
  if post.update(attributes)
    { post: post }
  else
    raise GraphQL::ExecutionError, post.errors.full_messages.join(", ")
  end
end
```

This kind of error handling _does_ express error state (either via `HTTP 500` or by the top-level `"errors"` key), but it doesn't take advantage of GraphQL's type system and can only express one error at a time. It works, but a stronger solution is to treat errors as data.

## Errors as Data

Another way to handle rich error information is to add _error types_ to your schema, for example:

```ruby
class Types::UserError < Types::BaseObject
  description "A user-readable error"

  field :message, String, null: false,
    description: "A description of the error"
  field :path, [String],
    description: "Which input value this error came from"
end
```

Then, add a field to your mutation which uses this error type:

```ruby
class Mutations::UpdatePost < Mutations::BaseMutation
  # ...
  field :errors, [Types::UserError], null: false
end
```

And in the mutation's `resolve` method, be sure to return `errors:` in the hash:

```ruby
def resolve(id:, attributes:)
  post = Post.find(id)
  if post.update(attributes)
    {
      post: post,
      errors: [],
    }
  else
    # Convert Rails model errors into GraphQL-ready error hashes
    user_errors = post.errors.map do |error|
      # This is the GraphQL argument which corresponds to the validation error:
      path = ["attributes", error.attribute.to_s.camelize(:lower)]
      {
        path: path,
        message: error.message,
      }
    end
    {
      post: post,
      errors: user_errors,
    }
  end
end
```

Now that the field returns `errors` in its payload, it supports `errors` as part of the incoming mutations, for example:

```graphql
mutation($postId: ID!, $postAttributes: PostAttributes!) {
  updatePost(id: $postId, attributes: $postAttributes) {
    # This will be present in case of success or failure:
    post {
      title
      comments {
        body
      }
    }
    # In case of failure, there will be errors in this list:
    errors {
      path
      message
    }
  }
}
```

In case of a failure, you might get a response like:

```ruby
{
  "data" => {
    "createPost" => {
      "post" => nil,
      "errors" => [
        { "message" => "Title can't be blank", "path" => ["attributes", "title"] },
        { "message" => "Body can't be blank", "path" => ["attributes", "body"] }
      ]
    }
  }
}
```

Then, client apps can show the error messages to end users, so they might correct the right fields in a form, for example.

## Nullable Mutation Payload Fields

To benefit from "Errors as Data" described above, mutation fields must not have `null: false`. Why?

Well, for _non-null_ fields (which have `null: false`), if they return `nil`, then GraphQL aborts the query and removes those fields from the response altogether.

In mutations, when errors happen, the other fields may return `nil`. So, if those other fields have `null: false`, but they return `nil`, the GraphQL will panic and remove the whole mutation from the response, _including_ the errors!

In order to have the rich error data, even when other fields are `nil`, those fields must have `null: true` (which is the default) so that the type system can be obeyed when errors happen.

Here's an example of a nullable field (good!):

```ruby
class Mutations::UpdatePost < Mutations::BaseMutation
  # Use the default `null: true` to support rich errors:
  field :post, Types::Post
  # ...
end
```

---

### Guides Mutations Mutation Root (`guides/mutations/mutation_root.md`)

# Mutation Root

GraphQL mutations all begin with the `mutation` keyword:

```graphql
mutation($accountNumber: ID!, $newBalance: Int!) {
# ^^^^ here
  setAccountBalance(accountNumber: $accountNumber, newBalance: $newBalance) {
    # ...
  }
}
```

Operations that begin with `mutation` get special treatment by the GraphQL runtime: root fields are guaranteed
to be executed sequentially. This way, the effect of a series of mutations is predictable.

Mutations are executed by a specific GraphQL object, `Mutation`. This object is defined like any other GraphQL object:

```ruby
class Types::Mutation < Types::BaseObject
  # ...
end
```

Then, it must be attached to your schema with the `mutation(...)` configuration:

```ruby
class Schema < GraphQL::Schema
  # ...
  mutation(Types::Mutation)
end
```

Now, whenever an incoming request uses the `mutation` keyword, it will go to `Mutation`.

See [Mutation Classes](/mutations/mutation_classes) for some helpers to define mutation fields.

---

### Guides Object Cache Caching (`guides/object_cache/caching.md`)

# Caching Results

`GraphQL::Enterprise::ObjectCache` supports several different caching configurations for objects and fields. To get started, include the extension in your base object class and base field class and use `cacheable(...)` to set up the default cache behavior:

```ruby
# app/graphql/types/base_object.rb
class Types::BaseObject < GraphQL::Schema::Object
  include GraphQL::Enterprise::ObjectCache::ObjectIntegration
  field_class Types::BaseField
  cacheable(...) # see below
  # ...
end
```

```ruby
# app/graphql/types/base_field.rb
class Types::BaseField < GraphQL::Schema::Field
  include GraphQL::Enterprise::ObjectCache::FieldIntegration
  cacheable(...) # see below
  # ...
end
```

Also, make sure your base interface module is using your field class:

```ruby
# app/graphql/types/base_interface.md
module Types::BaseInterface
 field_class Types::BaseField
end
```

Field caching can be configured per-field, too, for example:

```ruby
field :latest_update, Types::Update, null: false, cacheable: { ttl: 60 }

field :random_number, Int, null: false, cacheable: false
```

Only _queries_ are cached. `ObjectCache` skips mutations and subscriptions altogether.

## `cacheable(true|false)`

`cacheable(true)` means that the configured type or field may be stored in the cache until its cache fingerprint changes. It also defaults to `public: false`, meaning that clients will _not_ share cached responses. See [`public:`](#public) below for more about this option.

`cacheable(false)` disables caching for the configured type or field. Any query that includes this type or field will neither check for an already-cached value nor update the cache with its result.

## `public:`

`cacheable(public: false)` means that a type or field may be _cached_, but [`Schema.private_context_fingerprint_for(ctx)`](/object_cache/schema_setup#context-fingerprint) should be included in its cache key. In practice, this means that each client can have its own cached responses. Any query that contains a `cacheable(public: false)` type or field will use a private cache key.

`cacheable(public: true)` means that cached values from this type or field may be shared by _all_ clients. Use this for public-facing data which is the same for all viewers. Queries that include _only_ `public: true` types and fields will not include `Schema.private_context_fingerprint_for(ctx)` in their cache keys. That way their responses will be shared by all clients who request them.

## `ttl:`

`cacheable(ttl: seconds)` expires any cached value after the given number of seconds, regardless of cache fingerprint. `ttl:` shines in a few cases:

- Objects that can't reliably generate a fingerprint value (for example, they have no `.updated_at` timestamp). In this case, a conservative `ttl` may be the only option for cache expiration.
- Or, root-level fields that should be expired after a certain amount of time. The root-level `Query` often has _no_ backing object, so it won't have a cache fingerprint, either. Adding `cacheable: { ttl: ... }` to root level fields will provide some caching along with a guarantee about when they'll be expired.
- Or, list responses that may be difficult to invalidate properly (see below).

Under the hood, `ttl:` is implemented with Redis's `EXPIRE`.

## Caching lists and connections

Lists and connections require a little extra consideration. By default, each _item_ in a list is registered with the cache, but when new items are created, they are unknown to the cache and therefore don't invalidate the cached result. There are two main approaches to address this.

### `has_many` lists

In order to effectively bust the cache, items that belong to the list of "parent" object should __update the parent__ (eg, Rails `.touch`) whenever they're created, destroyed, or updated. For example, if there's a list of players on a team:

```graphql
{
  team { players { totalCount } }
}
```

None of the _specific_ `Player`s will be part of the cached response, but the `Team` will be. To properly invalidate the cache, the `Team`'s `updated_at` (or other cache key) should be updated whenever a `Player` is added or removed from the `Team`.

If a list may be sorted, then updates to `Player`s should also update the `Team` so that any sorted results in the cache are invalidated, too. Alternatively (or additionally), you could use a `ttl:` to expire cached results after a certain duration, just to be sure that results are eventually expired.

With Rails, you can accomplish this with:

```ruby
  # update the team whenever a player is saved or destroyed:
  belongs_to :team, touch: true
```

### Top-level lists

For `ActiveRecord::Relation`s _without_ a "parent" object, you can use `GraphQL::Enterprise::ObjectCache::CacheableRelation` to make a synthetic cache entry for the _whole_ relation. To use this class, make a subclass and implement `def items`, for example:

```ruby
class AllTeams < GraphQL::Enterprise::ObjectCache::CacheableRelation
  def items(division: nil)
    teams = Team.all
    if division
      teams = teams.where(division: division)
    end
    teams
  end
end
```

Then, in your resolver, use your new class to retrieve the items:

```ruby
class Query < GraphQL::Schema::Object
  field :teams, Team.connection_type do
    argument :division, Division, required: false
  end

  def teams(division: nil)
    AllTeams.items_for(self, division: division)
  end
end
```

If you're using [GraphQL::Schema::Resolver](rdoc-ref:GraphQL::Schema::Resolver), you'd call `.items_for` slightly differently:

```ruby
def resolve(division: nil)
  # use `context[:current_object]` to get the GraphQL::Schema::Object instance whose field is being resolved
  AllTeams.items_for(context[:current_object], division: division)
end
```

Finally, you'll need to handle `CacheableRelation`s in your object identification methods, for example:

```ruby
class MySchema < GraphQL::Schema
  # ...
  def self.id_from_object(object, type, ctx)
    if object.is_a?(GraphQL::Enterprise::ObjectCache::CacheableRelation)
      object.id
    else
      # The rest of your id_from_object logic here...
    end
  end

  def self.object_from_id(id, ctx)
    if (cacheable_rel = GraphQL::Enterprise::ObjectCache::CacheableRelation.find?(id))
      cacheable_rel
    else
      # The rest of your object_from_id logic here...
    end
  end
end
```

In this example, `AllTeams` implements several methods to support caching:

- `#id` creates a cache-friendly, stable global ID
- `#to_param` creates a cache fingerprint (using Rails's `#cache_key` under the hood)
- `.find?` retrieves the list based on its ID

This way, if a `Team` is created, the cached result will be invalidated and a fresh result will be created.

Alternatively (or additionally), you could use a `ttl:` to expire cached results after a certain duration, just to be sure that results are eventually expired.

### Connections

By default, connection-related objects (like `*Connection` and `*Edge` types) "inherit" cacheability from their node types. You can override this in your base classes as long as `GraphQL::Enterprise::ObjectCache::ObjectIntegration` is included in the inheritance chain somewhere.

## Caching Introspection

By default, introspection fields are considered _public_ for all queries. This means that they are considered cacheable and their results will be reused for any clients who request them. When [adding the ObjectCache to your schema](/object_cache/schema_setup#add-the-cache), you can provide some options to customize this behavior:

- `cache_introspection: { public: false, ... }` to use [`public: false`](#public) for all introspection fields. Use this if you hide schema members for some clients.
- `cache_introspection: false` to completely disable caching on introspection fields.
- `cache_introspection: { ttl: ..., ... }` to set a [ttl](#ttl) (in seconds) for introspection fields.

## Object Dependencies

By default, the `object` of a GraphQL Object type is used for caching the fields selected on that object. But, you can specify what object (or objects) should be used to check the cache by implementing `def self.cache_dependencies_for(object, context)` in your type definition. For example:

```ruby
class Types::Player
  def self.cache_dependencies_for(player, context)
    # we update the team's timestamp whenever player details change,
    # so ignore the `player` for caching purposes
    player.team
  end
end
```

Use this to:

- improve performance when caching lists of children that belong to a parent object
- register other objects with the ObjectCache when running a query. (`cacheable_object(obj)` or `def self.object_fingerprint_for` can also be used in this case.)

If this method returns an `Array`, each object in the array will be registered with the cache.

---

### Guides Object Cache Memcached (`guides/object_cache/memcached.md`)

# Dalli Configuration

`GraphQL::Enterprise::ObjectCache` can also run with a Memcached backend via the [Dalli](https://github.com/petergoldstein/dalli) client gem.

Set it up by passing a `Dalli::Client` instance as `dalli: ...`, for example:

```ruby
use GraphQL::Enterprise::OperationStore, dalli: Dalli::Client.new(...)
```

---

### Guides Object Cache Redis (`guides/object_cache/redis.md`)

# Redis Configuration

`GraphQL::Enterprise::ObjectCache` requires a Redis connection to store cached responses. Unlike `OperationStore` or rate limiters, this Redis instance should be configured to evict keys as needed.

## Memory Management

Memory consumption is hard to estimate since it depends on how many queries the cache receives, how many objects those queries reference, how big the response is for those queries, and how long the fingerprints are for each object and query. To manage memory, configure the Redis instance with a `maxmemory` and `maxmemory-policy` directive, for example:


```
maxmemory 1gb
maxmemory-policy allkeys-lfu
```

Additionally, consider conditionally skipping the cache to prioritize your most critical GraphQL traffic.

## Redis Cluster

`ObjectCache` also supports Redis Cluster. To use, pass `redis_cluster:`:

```ruby
use GraphQL::Enterprise::ObjectCache, redis_cluster: Redis::Cluster.new(...)
```

Under the hood, it uses query fingerprints as [hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/#hash-tags) and each cached result has its own set of object metadata.

## Connection Pool

`ObjectCache` also supports [ConnectionPool](https://github.com/mperham/connection_pool). To use it, pass `connection_pool:`:

```ruby
use GraphQL::Enterprise::ObjectCache, connection_pool: ConnectionPool.new(...) { ... }
```

## Data Structure

Under the hood, `ObjectCache` stores a mapping of queries and objects. Additionally, there are back-references from objects to queries that reference them. In general, like this:

```
"query1:result" => '{"data":{...}}'
"query1:objects" => ["obj1:v1", "obj2:v2"]

"query2:result" => '{"data":{...}}'
"query2:objects" => ["obj2:v2", "obj3:v1"]

"obj1:v1" => { "fingerprint" => "...", "id" => "...", "type_name" => "..." }
"obj2:v2" => { "fingerprint" => "...", "id" => "...", "type_name" => "..." }
"obj3:v1" => { "fingerprint" => "...", "id" => "...", "type_name" => "..." }

"obj1:v1:queries" => ["query1"]
"obj2:v2:queries" => ["query1", "query2"]
"obj3:v1:queries" => ["query2"]
```

These mappings enable proper clean-up when queries or objects are expired from the cache. Additionally, whenever `ObjectCache` finds incomplete data in storage (for example, a necessary key was evicted), then it invalidates the whole query and re-runs it.

---

### Guides Object Cache Runtime Considerations (`guides/object_cache/runtime_considerations.md`)

# Runtime Considerations

With caching configured, here are a few more things to keep in mind while queries are running.

## Skipping the cache

You can set `skip_object_cache: true` in your query `context: { ... }` to disable `ObjectCache` for a given query.

## Manually adding an object to caching

By default, `ObjectCache` gathers the objects "behind" each GraphQL object in the result, then uses their fingerprints as cache keys. To manually register another object with the cache while a query is running, call `Schema::Object.cacheable_object(...)`, passing the object and `context`. For example:

```ruby
field :team_member_count, Integer, null: true do
  argument :name, String, required: true
end

def team_member_count(name:)
  team = Team.find_by(name: name)
  if team
    # Register this object so that the cached result
    # will be invalidated when the team is updated:
    Types::Team.cacheable_object(team, context)
    team.members.count
  else
    nil
  end
end
```

(When the cache is disabled, `cacheable_object(...)` is a no-op.)

## Measuring the cache

While the cache is running, it logs some data in a Hash as `context[:object_cache]`. For example:

```ruby
result = MySchema.execute(...)
pp result.context[:object_cache]
{
  key: "...",                 # the cache key used for this query
  write: true,                # if this query caused an update to the cache
  ttl: 15,                    # the smallest `ttl:` value encountered in this query (used for this query's result)
  hit: true,                  # if this query returned a cached result
  public: false,              # true or false, whether this query used a public cache key or a private one
  messages: ["...", "..."],   # status messages about the cache's behavior
  objects: Set(...),          # application objects encountered during the query
  uncacheable: true,          # if ObjectCache found a reason that this query couldn't be cached (see `messages: ...` for reason)
  reauthorized_cached_objects: true,
                              # if `.authorized?` was checked for cached objects, see "Disabling Reauthorization"
}
```

## Manually refreshing the cache

If you need to manually clear the cache for a query, pass `context: { refresh_object_cache: true, ... }`. This will cause the `ObjectCache` to remove the already-cached result (if there was one), reassess the query for cache validity, and return a freshly-executed result.

Usually, this shouldn't be necessary; making sure objects update their [cache fingerprints](/object_cache/schema_setup.html#object-fingerprint) will cause entries to expire when they should be re-executed. See also [Schema fingerprint](/object_cache/schema_setup.html#schema-fingerprint) for expiring _all_ results in the cache.

---

### Guides Object Cache Schema Setup (`guides/object_cache/schema_setup.md`)

# 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: ...`:

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

See the [Redis guide](/object_cache/redis) or [Memcached guide](/object_cache/memcached) for details about configuring cache storage.

Additionally, it accepts some options for customizing how introspection is cached, see [Caching Introspection](/object_cache/caching#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](/object_cache/caching#public). For example:

```ruby
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" )
    end

    # 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(":")

    "user:#{viewer.id}:#{permission_fingerprint}"
  end
end
```

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:

```ruby
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)
      object.custom_cache_key
    elsif object.respond_to?(:uncacheable?) && object.uncacheable?
      nil # don't cache queries containing this object
    else
      super
    end
  end
end
```

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_cache/redis#memory-management).

## Object Identification

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

- `def self.id_from_object(object, type, context)` which returns a globally-unique String id for `object`
- `def self.object_from_id(id, context)` which returns the application object for the given globally-unique `id`
- `def self.resolve_type(abstract_type, object, context)` which returns a GraphQL object type definition to use for `object`

After your schema is setup, you can [configure caching on your types and fields](/object_cache/caching).

## 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:

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

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

## Disabling Reauthorization

By default, `ObjectCache` checks `.authorized?` on each object before returning a cached result. However, if all authorization-related considerations are present in the object's cache fingerprint, then you can disable this check in two ways:

- __per-query__, by passing `context: { reauthorize_cached_objects: false }`
- __globally__, by configuring `use GraphQL::Enterprise::ObjectCache, ... reauthorize_cached_objects: false`

---

### Guides Operation Store Access Control (`guides/operation_store/access_control.md`)

# Access Control

The `OperationStore` has a built-in mechanism for authenticating incoming `sync` requests. This way, you can be sure that all registered queries came from legitimate sources.

## Authentication

When you [add a client](/operation_store/client_workflow#add-a-client), you also associate a _secret_ with that client. You can use the default or provide your own and you can update a client secret at any time. By updating a secret, old secrets become invalid.

This secret is used to add an authorization header, generated with HMAC-SHA256. With this header, the server can assert:

- The request came from an authorized client
- The request was not corrupted in transit

For more info about HMAC, see [Wikipedia](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) or Ruby's [OpenSSL::HMAC](https://ruby-doc.org/stdlib-2.4.0/libdoc/openssl/rdoc/OpenSSL/HMAC.html) support.

The Authorization header takes the form:

```ruby
"GraphQL::Pro #{client_name} #{hmac}"
```

[graphql-ruby-client](/javascript_client/sync) adds this header to outgoing requests by using the provided `--client` and `--secret` values.

---

### Guides Operation Store Active Record Backend (`guides/operation_store/active_record_backend.md`)

# ActiveRecord Backend

GraphQL-Pro's `OperationStore` can use ActiveRecord to store persisted queries. After setting up the database, it will read and write using those tables as needed.

## Database Setup

To use ActiveRecord, `GraphQL::Pro::OperationStore` requires some database tables.

### Rails Generator

With Rails, you can generate the required migration then run it:

```bash
$ rails generate graphql:operation_store:create
$ rails db:migrate
```

(You'll have to run that migration on any staging or production servers, too.)

Now, `OperationStore` has what it needs to save queries using ActiveRecord!

### Manual Setup

You can also create the required migration by manually by generating an empty migration:

```bash
$ rails generate migration SetupOperationStore
```

Then open the migration file and add:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Then run the migration:

```
$ bundle exec rake db:migrate
```

(You'll have to run that migration on any staging or production servers, too.)

Now, `OperationStore` has what it needs to save queries using ActiveRecord!

## Database Update

GraphQL-Pro 1.15.0 introduced new features for the OperationStore. To enable them, add some columns to your database:

```ruby
add_column :graphql_client_operations, :is_archived, :boolean, default: false
add_column :graphql_client_operations, :last_used_at, :datetime
```

## Updating `last_used_at`

By default, GraphQL-Pro updates `last_used_at` values in a background thread every 5 seconds. You can customize this by passing a number of seconds to `update_last_used_at_every:` when installing the OperationStore:

```ruby
use GraphQL::Pro::OperationStore, update_last_used_at_every: 1 # seconds
```

To update that column inline each time an operation is accessed, pass `0`.

**Note:** It is recommended to set this to `0` in test environments, to avoid delayed updates in another thread that can cause intermittent test hangs and failures. For example:

```ruby
# Update immediately in Test, wait 5 seconds in other environments:
use GraphQL::Pro::OperationStore, update_last_used_at_every: Rails.env.test? ? 0 : 5
```

---

### Guides Operation Store Client Workflow (`guides/operation_store/client_workflow.md`)

# Client Workflow

To use persisted queries with your client application, you must:

- Set up `OperationStore`, as described in [Getting Started](/operation_store/getting_started)
- [Add the client](#add-a-client) to the system
- [Sync operations](#syncing) from the client to the server
- [Send `params[:operationId]`](#client-usage) from the client app

This documentation also touches on [graphql-ruby-client sync](/javascript_client/sync), a JavaScript client library for using `OperationStore`.

## Add a Client

Clients are registered via [the dashboard](/operation_store/getting_started#add-routes):



A default `secret` is provided for you, but you can also enter your own. The `secret` is used for [HMAC authentication](/operation_store/access_control).

(Are you interested in a Ruby API for this? Please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=OperationStore+Ruby+API&body=) or email `support@graphql.pro`.)

## Syncing

Once a client is registered, it can push queries to the server via [the Sync API](/operation_store/getting_started#add-routes).

The easiest way to sync is with `graphql-ruby-client sync`, a command-line tool written in JavaScript ([Sync Guide](/javascript_client/sync))

In short, it:

- Finds GraphQL queries from `.graphql` files or `relay-compiler` output in the provided `--path`
- Adds an [Authentication header](/operation_store/access_control) based on the provided `--client` and `--secret`
- Sends the operations to the provided `--url`
- Generates a JavaScript module into the provided `--outfile`

For example:



For help syncing in another language, you can take inspiration from the [JavaScript implementation](https://github.com/rmosolgo/graphql-ruby/tree/master/javascript_client), [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=Implementing+operation+sync+in+another+language&body=), or email `support@graphql.pro`.

## Client Usage

See the [Sync Guide](/javascript_client/sync) for using OperationStore with Relay Modern, Apollo 1.x, Apollo Link, or plain JavaScript.

To run stored operations from another client, send a param called `operationId` which is composed of:


```ruby
 {
   # ...
   operationId: "my-relay-app/ce79aa2784fc..."
   #            ^ client id  / ^ operation id
 }
```

The server will use those values to fetch an operation from the database.

### Next Steps

Learn more about `OperationStore`'s [authentication](/operation_store/access_control) or read some tips for [server management](/operation_store/server_management).

---

### Guides Operation Store Getting Started (`guides/operation_store/getting_started.md`)

# Getting Started

To use `GraphQL::Pro::OperationStore` with your app, follow these steps:

- [Check the dependencies](#dependencies) to make sure `OperationStore` is supported
- [Prepare the database](#prepare-the-database) for `OperationStore`'s  data
- [Add `OperationStore`](#add-operationstore) to your GraphQL schema
- [Add routes](#add-routes) for the Dashboard and sync API
- [Update your controller](#update-the-controller) to support persisted queries
- [Add a client](/operation_store/client_workflow) to start syncing queries

## Dependencies

`OperationStore` requires two gems in your application environment:

- [ActiveRecord](/operation_store/active_record_backend) or [Redis](/operation_store/redis_backend) for persistence. (Using another ORM or backend? Please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=Backend+support+request+for+OperationStore&body=) to request support!)
- `Rack`: to serve the Dashboard and Sync API. (In Rails, this is provided by `config/routes.rb`.)

These are bundled with Rails by default.

## Prepare the Database

If you're going to store data with ActiveRecord, [migrate the database](/operation_store/active_record_backend) to prepare tables for it.

## Add `OperationStore`

To hook up the storage to your schema, add the plugin:

```ruby
class MySchema < GraphQL::Schema
  # Add it _after_ other tracing-related features, for example:
  # use GraphQL::Tracing::DataDogTracing
  # ...
  use GraphQL::Pro::OperationStore
end
```

Make sure to add this feature _after_ other [Tracing](/queries/tracing)-based features so that those other features will have access to the loaded query string. Otherwise, you may get `"No query string was present"` errors.

By default, it uses `ActiveRecord`. It also accepts:

- `redis:`, for using a [Redis backend](/operation_store/redis_backend); OR
- `backend_class:`, for implementing custom persistence.

Also, you can disable updates to "last used at" with `default_touch_last_used_at: false`. (This can also be configured per-query with `context[:operation_store_touch_last_used_at] = true|false`.)

## Add Routes

To use `OperationStore`, add two routes to your app:

```ruby
# config/routes.rb

# Include GraphQL::Pro's routing extensions:
using GraphQL::Pro::Routes

Rails.application.routes.draw do
  # ...
  # Add the Dashboard
  # TODO: authorize, see the dashboard guide
  mount MySchema.dashboard, at: "/graphql/dashboard"
  # Add the Sync API (authorization built-in)
  mount MySchema.operation_store_sync, at: "/graphql/sync"
end
```

`MySchema.operation_store_sync` receives pushes from clients. See [Client Workflow](/operation_store/client_workflow) for more info on how this endpoint is used.

`MySchema.dashboard` includes a web view to the `OperationStore`, visible at `/graphql/dashboard`. See the [Dashboard guide](/pro/dashboard) for more details, including authorization.



`operation_store_sync` and `dashboard` are both Rack apps, so you can mount them in Rails, Sinatra, or any other Rack app.

__Alternatively__, you can configure the routes to load your schema lazily, during the first request:

```ruby
# Provide the fully-qualified class name of your schema:
lazy_routes = GraphQL::Pro::Routes::Lazy.new("MySchema")
mount lazy_routes.dashboard, at: "/graphql/dashboard"
mount lazy_routes.operation_store_sync, at: "/graphql/sync"
```

### With Visibility Profiles

You can apply a [visibility profile](/authorization/visibility#visibility-profiles) to incoming operations by passing the profile name to `operation_store_sync`, for example:

```ruby
mount MySchema.operation_store_sync(visibility_profile: :public_api), at: "/graphql/sync"
# or:
mount lazy_routes.operation_store_sync(visibility_profile: :public_api), at: "/graphql/sync"
```

This will apply that profile to all newly-synced operations. (It doesn't affect operations that were already synced.)

## Update the Controller

Add `operation_id:` to your GraphQL context:

```ruby
# app/controllers/graphql_controller.rb
context = {
  # Relay / Apollo 1.x:
  operation_id: params[:operationId]
  # Or, Apollo Link:
  # operation_id: params[:extensions][:operationId]
}

MySchema.execute(
  # ...
  context: context,
)
```

`OperationStore` will use `operation_id` to fetch the operation from the database.

See [Server Management](/operation_store/server_management) for details about rejecting GraphQL from `params[:query]`.

## Next Steps

Sync your operations with the [Client Workflow](/operation_store/client_workflow).

---

### Guides Operation Store Redis Backend (`guides/operation_store/redis_backend.md`)

# Redis Backend

`OperationStore` can use Redis to store persisted queries. Pass a `redis:` option when adding the plugin:

```ruby
class MySchema < GraphQL::Schema
  use GraphQL::Pro::OperationStore, redis: Redis.new
end
```

(You can initialize `Redis` with any options you need.)

__Note:__ Be sure that this Redis instance is configured as a _persistent database_, not as a cache. You don't want it to throw away old keys!

---

### Guides Operation Store Server Management (`guides/operation_store/server_management.md`)

# Server Management

After [getting started](/operation_store/getting_started), here some things to keep in mind.

## Rejecting Arbitrary Queries

With persisted queries, you can stop accepting arbitrary GraphQL input. This way, malicious users can't run large or inappropriate queries on your server.

In short, you can ignore arbitrary GraphQL by _skipping_ the first argument of `MySchema.execute`:

```ruby
# app/controllers/graphql.rb

# Don't pass a query string; ignore `params[:query]`
MySchema.execute(
  context: context,
  variables: params[:variables],
  operation_name: params[:operationName],
)
```

However, take these points into consideration:

- Are any previous clients using arbitrary GraphQL? (For example, old versions of native apps or old web pages may still be sending GraphQL.)
- Should some users still be allowed to send custom strings? (For example, do staff members use GraphiQL to develop new features or debug issues?)

If those apply to you, you can apply some logic to `query_string`:

```ruby
# Allow arbitrary GraphQL:
# - from staff users
# - in development
query_string = if current_user.staff? || Rails.env.development?
  params[:query]
else
  nil
end

MySchema.execute(
  query_string, # maybe nil, that's OK.
  context: context,
  variables: params[:variables],
  operation_name: params[:operationName],
)
```

## Archiving and Deleting Data

Clients can only _add_ to the database, but as an administrator, you can also archive or delete entries from the database. (Make sure you [authorize access to the Dashboard](/pro/dashboard).) This is a dangerous operation: by archiving or deleting something, any clients who depend on that data will crash.

Some reasons to archive or delete from the database are:

- Data was pushed in error; the data is not used
- The queries are invalid or unsafe; it's better to remove them than to keep them

If this is true, you can use "Archive" or "Delete" buttons to remove things from production.

When an operation is archived, it's no longer available to clients, but it's still in the database. It can be unarchived later, so this is lower-risk than full deletion.

## Integration with Your Application

It's on the road map to add a Ruby API to `OperationStore` so that you can integrate it with your application. For example, you might:

- Create clients that correspond to users in your system
- Show client secrets via the Dashboard so that users can save them
- Render your own administration dashboards with `OperationStore` data

If this interests you, please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=OperationStore+Ruby+API&body=) or email `support@graphql.pro`.

---

### Guides Pagination Connection Concepts (`guides/pagination/connection_concepts.md`)

# Connection Concepts

__Connections__ are a pagination solution which started with [Relay JS](https://facebook.github.io/relay), but now it's used for almost any GraphQL API.

Connections are composed of a few kinds of objects:

- `Connection` types are generics that expose pagination-related metadata and access to the items
- `Edge` types are also generics. They represent the relationship between the parent and child (eg, a `PostEdge` represents the link from `Blog` to `Post`).
- _nodes_ are actual list items. In a `PostsConnection`, each node is a `Post`.

Connections have some advantages over offset-based pagination:

- First-class support for relationship metadata
- Cursor implementations can support efficient, stable pagination

## Connections, Edges and Nodes

Connection pagination has three core objects: connections, edges, and nodes.

### Nodes

Nodes are items in a list. A `node` is usually an object in your schema. For example, a `node` in a `posts` connection is a `Post`:

```ruby
{
  posts(first: 5) {
    edges {
      node {
        # This is a `Post` object:
        title
        body
        publishedAt
      }
    }
  }
}
```

### Connections

Connections are objects that _represent_ a one-to-many relation. They contain _metadata_ about the list of items and _access to the items_.

Connections are often generated from object types. Their list items, called _nodes_, are members of that object type. Connections can also be generated from union types and interface types.

##### Connection metadata

Connections can tell you about the list in general. For example, if you [add a total count field](/type_definitions/extensions#customizing-connections), they can tell you the count:

```ruby
{
  posts {
    # This is a PostsConnection
    totalCount
  }
}
```

##### Connection items

The list items in a connection are called _nodes_. They can generally be accessed two ways:

- via edges: `posts { edges { node { ... } } }`
- via nodes: `posts { nodes { ... } }`

The differences is that `edges { node { ... } }` has more room for relationship metadata. For example, when listing the members of a team, you might include _when_ someone joined the team as edge metadata:

```ruby
team {
  members(first: 10) {
    edges {
      # when did this person join the team?
      joinedAt
      # information about the person:
      node {
        name
      }
    }
  }
}
```

Alternatively, `nodes` provides easier access to the items, but can't expose relationship metadata:

```ruby
team {
  members(first: 10) {
    nodes {
      # shows the team members' names
      name
    }
  }
}
```

There's no way to show `joinedAt` above without using `edges { ... }`.

### Edges

Edges are _like_ join tables in that they can expose relationship metadata between a parent object and its children.

For example, let's say that someone may be the member of _several_ teams. You would make a join table in the database (eg, `team_memberships`) which connect people to each of the teams they're on. This join table could also include information about _how_ that person is related to the team: when they joined, what role they have, etc.

Edges can reveal this information about the relationship, for example:

```ruby
team {
  # this is the team name
  name

  members(first: 10) {
    edges {
      # this is a team membership
      joinedAt
      role

      node {
        # this is the person on the team
        name
      }
    }
  }
}
```

So, edges really help when the _relationship_ between two objects has special data associated with it. If you use a join table, that's a clue that you might use a custom edge to model the relationship.

---

### Guides Pagination Cursors (`guides/pagination/cursors.md`)

# Cursors

Connections use _cursors_ to advance through paginated lists. A cursor is an opaque string that indicates a specific point in this.

Here, _opaque_ means that the string has no meaning except its value. cursors shouldn't be decoded, reverse-engineered, or generated ad-hoc. The only guarantee of a cursor is that, after you retrieve one, you can use it to request subsequent or preceding items in the list.

Although cursors can be tricky, they were chosen for Relay-style connections because they can be implemented in stable and high-performing ways.

## Customizing Cursors

By default, cursors are encoded in base64 to make them opaque to a human client. You can specify a custom encoder with `Schema.cursor_encoder`. The value should be an object which responds to `.encode(plain_text, nonce:)` and `.decode(encoded_text, nonce: false)`.

For example, to use URL-safe base-64 encoding:

```ruby
module URLSafeBase64Encoder
  def self.encode(txt, nonce: false)
    Base64.urlsafe_encode64(txt)
  end

  def self.decode(txt, nonce: false)
    Base64.urlsafe_decode64(txt)
  end
end

class MySchema < GraphQL::Schema
  # ...
  cursor_encoder(URLSafeBase64Encoder)
end
```

Now, all connections will use URL-safe base-64 encoding.

From a connection instance, the `cursor_encoder` methods are available via [GraphQL::Pagination::Connection](https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/pagination/connection.rb) `#encode` and `#decode`

---

### Guides Pagination Custom Connections (`guides/pagination/custom_connections.md`)

# Custom Connections

GraphQL-Ruby ships with built-in connection support for ActiveRecord, Sequel, Mongoid, and Ruby Arrays. You can read more in the [Using Connections](/pagination/using_connections) guide.

When you want to serve a connection based on your _own_ data object, you can create a custom connection. The implementation will have several components:

- The __application object__ -- the list of items that you want to paginate in GraphQL
- The __connection wrapper__ which wraps the application object and implements methods used by GraphQL
- The __connection type__, a GraphQL object type which implements the connection contract

For this example, we'll imagine that your application communicates with an external search engine, and expresses all search results with a `SearchEngine::Result` class. (There isn't _really_ any class like this; it's an arbitrary example of an application-specific collection of items.)

## Application Object

In Ruby, _everything_ is an object, and that includes _lists of objects_. For example, we think of an Array as a _list_ of objects, but Arrays are also objects in their own right.

Some list objects have very fancy implementations. Think of an `ActiveRecord::Relation`: it gathers up the parts of a SQL query, and at the right moment, it dispatches a call to your database to fetch the objects in the list. An `ActiveRecord::Relation` is also a list object.

Your application probably has other list objects that you want to paginate via GraphQL connections. For example, you might show a user some search results, or a list of files from a fileserver. Those lists are modeled with _list objects_, and those list objects can be wrapped with connection wrappers.

## Connection Wrapper

A connection wrapper is an adapter between a plain-Ruby list object (like an Array, Relation, or something application-specific, like `SearchEngine::Result`) and a GraphQL connection type. The connection wrapper implements methods which the GraphQL connection type requires, and it implements those methods based on the underlying list object.

You can extend [GraphQL::Pagination::Connection](rdoc-ref:GraphQL::Pagination::Connection) to get started on a custom connection wrapper, for example:

```ruby
# app/graphql/connections/search_results_connection.rb
class Connections::SearchResultsConnection < GraphQL::Pagination::Connection
  # implementation here ...
end
```

The methods you must implement are:

- `#nodes`, which returns a paginated slice of `@items` based on the given arguments
- `#has_next_page`, which returns `true` if there are items after the ones in `#nodes`
- `#has_previous_page`, which returns `true` if there are items before the ones in `#nodes`
- `#cursor_for(item)`, which returns a String to serve as the cursor for `item`

How to implement these methods (efficiently!) depends on your backend and how you communicate with it. For inspiration, you can see the built-in connections:

- [GraphQL::Pagination::ArrayConnection](rdoc-ref:GraphQL::Pagination::ArrayConnection)
- [GraphQL::Pagination::ActiveRecordRelationConnection](rdoc-ref:GraphQL::Pagination::ActiveRecordRelationConnection)
- [GraphQL::Pagination::SequelDatasetConnection](rdoc-ref:GraphQL::Pagination::SequelDatasetConnection)
- [GraphQL::Pagination::MongoidRelationConnection](rdoc-ref:GraphQL::Pagination::MongoidRelationConnection)

### Using a Custom Connection

To integrate your custom connection wrapper with GraphQL, you have two options:

- Map the wrapper to a list object at the _schema level_, so that those list objects are _always_ automatically wrapped by your wrapper; OR
- Use the wrapper manually in resolve methods, to override any automatic mapping

The first case is very convenient, and the second case makes it possible to customize connections for specific situations.

To __map the wrapper to a class of objects__, add it to your schema:

```ruby
class MySchema < GraphQL::Schema
  # Hook up a custom wrapper
  connections.add(SearchEngine::Result, Connections::SearchResultsConnection)
end
```

Now, any time a field returns an instance of `SearchEngine::Result`, it will be wrapped with `Connections::SearchResultsConnection`

Alternatively, you can apply a connection wrapper on a case-by-case basis by applying it during the resolver (method or [GraphQL::Schema::Resolver](rdoc-ref:GraphQL::Schema::Resolver)):

```ruby
field :search, Types::SearchResult.connection_type, null: false do
  argument :query, String
end

def search(query:)
  search = SearchEngine::Search.new(query: query, viewer: context[:current_user])
  results = search.results
  # Apply the connection wrapper and return it
  Connections::SearchResultsConnection.new(results)
end
```

GraphQL-Ruby will use the provided connection wrapper in that case. You can use this fine-grained approach to handle special cases or implement performance optimizations.

## Connection Type

Connection types are GraphQL object types which comply to the [Relay connection specification](https://relay.dev/graphql/connections.htm). GraphQL-Ruby ships with some tools to help you create those object types:

- [GraphQL::Types::Relay::BaseConnection](rdoc-ref:GraphQL::Types::Relay::BaseConnection) and [GraphQL::Types::Relay::BaseEdge](rdoc-ref:GraphQL::Types::Relay::BaseEdge) are example implementations of the spec. They don't inherit from your application's base object class though, so you might not be able to use them out of the box.
- Type classes respond to `.connection_type` which returns a generated connection type based on that class. By default, it inherits from the provided `GraphQL::Types::Relay::BaseConnection`, but you can override that by setting `connection_type_class(Types::MyBaseConnectionObject)` in your base classes.

For example, you could implement a base connection class:

```ruby
class Types::BaseConnectionObject < Types::BaseObject
  # implement based on `GraphQL::Types::Relay::BaseConnection`, etc
end
```

Then hook it up to your base classes:

```ruby
class Types::BaseObject < GraphQL::Schema::Object
  # ...
  connection_type_class(Types::BaseConnectionObject)
end

class Types::BaseUnion < GraphQL::Schema::Union
  connection_type_class(Types::BaseConnectionObject)
end

module Types::BaseInterface
  include GraphQL::Schema::Interface

  connection_type_class(Types::BaseConnectionObject)
end
```


Then, when defining fields, you could use `.connection_type` to use your connection class hierarchy:

```ruby
field :posts, Types::Post.connection_type, null: false
```

(Those fields get `connection: true` by default, because the generated connection type's name ends in `*Connection`.)

---

### Guides Pagination Stable Relation Connections (`guides/pagination/stable_relation_connections.md`)

# Stable Relation Connections

`GraphQL::Pro` includes a mechanism for serving _stable_ connections for `ActiveRecord::Relation`s based on column values. If objects are created or destroyed during pagination, the list of items won't be disrupted.

These connection implementations are database-specific so that they can build proper queries with regard to `NULL` handling. (Postgres treats nulls as _larger_ than other values while MySQL and SQLite treat them as _smaller_ than other values.)

## What's the difference?

The default [GraphQL::Pagination::ActiveRecordRelationConnection](rdoc-ref:GraphQL::Pagination::ActiveRecordRelationConnection) (which turns an `ActiveRecord::Relation` into a GraphQL-ready connection) uses _offset_ as a cursor. This naive approach is sufficient for many cases, but it's subject to a specific set of bugs.

Let's say you're looking at the second page of 10 items (`LIMIT 10 OFFSET 10`). During that time, one of the items on page 1 is deleted. When you navigate to page 3 (`LIMIT 10 OFFSET 20`), you'll actually _miss_ one item. The entire list shifted "up" one position when a previous item was deleted.

To solve this bug, we should use a _value_ to page through items (instead of _offset_). For example, if items are ordered by `id`, use the `id` for pagination:

```sql
LIMIT 10                      -- page 1
WHERE id > :last_id LIMIT 10  -- page 2
```

This way, even when items are added or removed, pagination will continue without interruption.

For more information about this issue, see ["Pagination: You're (Probably) Doing It Wrong"](https://coderwall.com/p/lkcaag/pagination-you-re-probably-doing-it-wrong).

## Installation

You can use a stable connection for _all_ `ActiveRecord::Relation`s by installing it at the schema level:

```ruby
class MyAppSchema < GraphQL::Schema
  # Hook up the stable connection that matches your database
  connections.add(ActiveRecord::Relation, GraphQL::Pro::PostgresStableRelationConnection)
  # Or...
  # connections.add(ActiveRecord::Relation, GraphQL::Pro::MySQLStableRelationConnection)
  # connections.add(ActiveRecord::Relation, GraphQL::Pro::SqliteStableRelationConnection)
end
```

Alternatively, you can apply the stable connection wrapper on a _field-by-field_ basis. For example:

```ruby
field :items, Types::ItemType.connection_type, null: false

def items
  # Build an ActiveRecord::Relation
  relation = Item.all
  # And wrap it with a connection implementation, then return the connection
  GraphQL::Pro::MySQLStableRelationConnection.new(relation)
end
```

That way, you can adopt stable cursors bit-by-bit. (See below for [backwards compatibility](#backwards-compatibility) notes.)

Similarly, if you enable stable connections for the whole schema, you can wrap _specific_ relations with `GraphQL::Pagination::ActiveRecordRelationConnection` when you want to use index-based cursors. (This is handy for relations whose ordering is too complicated for cursor generation.)

## Implementation Notes

Keep these points in mind when using value-based cursors:

- For a given `ActiveRecord::Relation`, only columns of that specific model can be used in pagination. (This is because column names are turned into `WHERE` conditions.)
- The connection may add an additional `primary_key` ordering to ensure that the cursor value is unique. This behavior is inspired by `Relation#reverse_order` which also assumes that `primary_key` is the default sort.
- The connection will add fields to the relation's `SELECT` clause, so that cursors can be reliably constructed from the database results.

## Grouped Relations

When using a grouped `ActiveRecord::Relation`, include a unique ID in your sort to ensure that each row in the result has a unique cursor. For example:

```ruby
# Bad: If two results have the same `max(price)`,
# they will be identical from a pagination perspective:
Products.select("max(price) as price").group("category_id").order("price")

# Good: `category_id` is used to disambiguate any results with the same price:
Products.select("max(price) as price").group("category_id").order("price, category_id")
```

For ungrouped relations, this issue is handled automatically by adding the model's `primary_key` to the order values.

If you provide an unordered, grouped relation, `GraphQL::Pro::RelationConnection::InvalidRelationError` will be raised because an unordered relation _cannot_ be paginated in a stable way.

## Backwards Compatibility

`GraphQL::Pro`'s stable relation connection is backwards-compatible. If it receives an offset-based cursor, it uses that cursor for the next resolution, then returns value-based cursors in the next result.

## ActiveRecord Versions

Stable relation connections support ActiveRecord `>= 4.1.0`.

---

### Guides Pagination Using Connections (`guides/pagination/using_connections.md`)

# Using Connections

GraphQL-Ruby ships with a few implementations of the [connection pattern](/pagination/connection_concepts) that you can use out of the box. They support Ruby Arrays, Mongoid, Sequel, and ActiveRecord.

Additionally, connections allow you to limit the number of items returned with [`max_page_size`](#max-page-size) and set the default number of items returned with [`default_page_size`](#default-page-size).

## Make Connection Fields

Use `.connection_type` to generate a connection type for paginating over objects of a given type:

```ruby
field :items, Types::ItemType.connection_type, null: false
```

The generated return type will be called `ItemConnection`. Since it ends in `*Connection`, the `field(...)` will automatically be configured with `connection: true`. If the connection type's name doesn't end in `Connection`, you have to add that configuration yourself:

```ruby
# here's a custom type whose name doesn't end in "Connection", so `connection: true` is required:
field :items, Types::ItemConnectionPage, null: false, connection: true
```

The field will be given some arguments by default: `first`, `last`, `after`, and `before`.

### Opting out of default connection handling

To opt out of GraphQL-Ruby's default connection handling, add `connection: false` to the field definition:

```diff
- field :items, Types::ItemType.connection_type, null: false
+ field :items, Types::ItemType.connection_type, null: false, connection: false
```

Then, add any arguments you want (`first`, `last`, `after`, `before`) and make sure that your resolver returns an object that can fulfill the fields of the configured return type.

## Return Collections

With connection fields, you can return collection objects from fields or resolvers:

```ruby
def items
  object.items # => eg, returns an ActiveRecord Relation
end
```

The collection object (Array, Mongoid relation, Sequel dataset, ActiveRecord relation) will be automatically paginated with the provided arguments. Cursors will be generated based on the offset of nodes in the collection.

## Make Custom Connections

If you want to paginate something that _isn't_ supported out-of-the-box, you can implement your own pagination wrapper and hook it up to GraphQL-Ruby. Read more in [Custom Connections](/pagination/custom_connections).

## Special Cases

Sometimes, you have _one collection_ that needs special handling, unlike other instances of its class. For cases like this, you can manually apply the connection wrapper in the resolver. For example:

```ruby
def items
  # Get the ActiveRecord relation to paginate
  relation = object.items
  # Apply a custom wrapper
  Connections::ItemsConnection.new(relation)
end
```

This way, you can handle this _particular_ `relation` with custom code.

## Max Page Size

You can apply `max_page_size` to limit the number of items returned and queried from the database, regardless of what the client requests.

- __For the whole schema__, you can add it to your schema definition:

```ruby
class MyAppSchema < GraphQL::Schema
  default_max_page_size 50
end
```

  At runtime, that value will be applied to _every_ connection, unless an override is provided as described below.

- __For a given field__, add it to the field definition with a keyword:

```ruby
field :items, Item.connection_type, null: false,
  max_page_size: 25
```

- __Dynamically__, you can add `max_page_size:` when you apply custom connection wrappers:

```ruby
def items
  relation = object.items
  Connections::ItemsConnection.new(relation, max_page_size: 10)
end
```

To _remove_ a `max_page_size` setting, you can pass `nil`. That will allow unbounded collections to be returned to clients.

## Default Page Size

You can apply `default_page_size` to limit the number of items returned and queried from the database when no `first` or `last` is provided.

- __For the whole schema__, you can add it to your schema definition:

```ruby
class MyAppSchema < GraphQL::Schema
  default_page_size 50
end
```

  At runtime, that value will be applied to _every_ connection, unless an override is provided as described below.

- __For a given field__, add it to the field definition with a keyword:

```ruby
field :items, Item.connection_type, null: false,
  default_page_size: 25
```

- __Dynamically__, you can add `default_page_size:` when you apply custom connection wrappers:

```ruby
def items
  relation = object.items
  Connections::ItemsConnection.new(relation, default_page_size: 10)
end
```

If `max_page_size` is set and `default_page_size` is higher than it, the `default_page_size` will be clamped down to match `max_page_size`. If both `default_page_size` and `max_page_size` are set to `nil`, unbounded collections will be returned.

---

### Guides Pro Dashboard (`guides/pro/dashboard.md`)

# Dashboard

[GraphQL-Pro](https://graphql.pro) includes a web dashboard for monitoring [Operation Store](/operation_store/overview) and [subscriptions](/subscriptions/pusher_implementation).



## Installation

To hook up the Dashboard, add it to `routes.rb`

```ruby
# config/routes.rb

# Include GraphQL::Pro's routing extensions:
using GraphQL::Pro::Routes

Rails.application.routes.draw do
  # ...
  # Add the GraphQL::Pro Dashboard
  # TODO: authorize, see below
  mount MySchema.dashboard, at: "/graphql/dashboard"
end
```

With this configuration, it will be available at `/graphql/dashboard`.

The dashboard is a Rack app, so you can mount it in Sinatra or any other Rack app.

#### Lazy-loading the schema

Alternatively, you can set up the dashboard to load the schema during the first request. To do that, initialize `GraphQL::Pro::Routes::Lazy` with a string that gives the fully-qualified name of your schema class, for example:

```ruby
Rails.application.routes.draw do
  # ...
  # Add the GraphQL::Pro Dashboard
  # TODO: authorize, see below
  lazy_routes = GraphQL::Pro::Routes::Lazy.new("MySchema")
  mount lazy_routes.dashboard, at: "/graphql/dashboard"
end
```

With this setup, `MySchema` will be loaded when the dashboard serves its first request. This can speed up your application's boot in development since it doesn't load the whole GraphQL schema when building the routes.

## Authorizing the Dashboard

You should only allow admin users to see `/graphql/dashboard` because it allows viewers to delete stored operations.

### Rails Routing Constraints

Use [Rails routing constraints](https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints) to restrict access to authorized users, for example:

```ruby
# Check the secure session for a staff flag:
STAFF_ONLY = ->(request) { request.session["staff"] == true }
# Only serve the GraphQL Dashboard to staff users:
constraints(STAFF_ONLY) do
  mount MySchema.dashboard, at: "/graphql/dashboard"
end
```

### Rack Basic Authentication

Insert the `Rack::Auth::Basic` middleware, before the web view. This prompts for a username and password when visiting the dashboard.

```ruby
graphql_dashboard = Rack::Builder.new do
  use(Rack::Auth::Basic) do |username, password|
    username == ENV.fetch("GRAPHQL_USERNAME") && password == ENV.fetch("GRAPHQL_PASSWORD")
  end

  run MySchema.dashboard
end
mount graphql_dashboard, at: "/graphql/dashboard"
```

---

### Guides Pro Encoders (`guides/pro/encoders.md`)

# Encrypted, Versioned Cursors and IDs

`GraphQL::Pro` includes a mechanism for serving encrypted, versioned cursors and IDs.  This provides some benefits:

- Users can't reverse-engineer node IDs or connection cursors, removing a possible attack vector.
- You can gradually transition between cursor strategies, adding encrypting while supporting any "stale" encoders which clients already have.

`GraphQL::Pro`'s encrypted encoders provide a few security features:

- Key-based encryption by `aes-128-gcm` by default
- Authentication
- Nonces for cursors (but not IDs, that would be silly)

## Defining an Encoder

Encoders can be created by subclassing `GraphQL::Pro::Encoder`: 

```ruby
class MyEncoder < GraphQL::Pro::Encoder
  key("f411f30...")
  # optional:
  tag("81ce51c307")
end
```

- `key` is the encryption key for this encoder. You can generate one with: `require "securerandom"; SecureRandom.bytes(16)`
- `tag`, if provided, is used as authentication data or for disambiguating versioned encoders

## Encrypting Cursors

Encrypt cursors by attaching an encrypted encoder to `Schema#cursor_encoder`:

```ruby
class MySchema GraphQL::Schema
  cursor_encoder(MyCursorEncoder)
end
```

Now, built-in connection implementations will use that encoder for cursors.

If you implement your own connections, you can access the encoder's encryption methods via `GraphQL::Pagination::Connection#encode` and `GraphQL::Pagination::Connection#decode`.


## Encrypting IDs

Encrypt IDs by using encoders in `Schema.id_from_object` and `Schema.object_from_id`:

```ruby
class MySchema < GraphQL::Schema
  def self.id_from_object(object, type, ctx)
    id_data = "#{object.class.name}/#{object.id}"
    MyIDEncoder.encode(id_data)
  end

  def self.object_from_id(id, ctx)
    id_data = MyIDEncoder.decode(id)
    class_name, id = id_data.split("/")
    class_name.constantize.find(id)
  end
end
```

Note that IDs are _not_ encrypted with nonces. This means that if someone can _guess_ how IDs are constructed, they can determine the encryption key (a kind of [known-plaintext attack](https://en.wikipedia.org/wiki/Known-plaintext_attack)). To reduce this risk, make your plaintext IDs unpredictable, for example, by appending a salt or obfuscating their content.

## Versioning

You can combine several encoders into a single chain of versioned encoders. Pass them to `.versioned`, newest-to-oldest:

```ruby
# Define some encoders ...
class NewSecureEncoder < GraphQL::Pro::Encoder
  # ...
end

class OldSecureEncoder < GraphQL::Pro::Encoder
  # ...
end

class LegacyInsecureEncoder < GraphQL::Pro::Encoder
  # ...
end

# Then order them by priority:
VersionedEncoder = GraphQL::Pro::Encoder.versioned(
  # Newest:
  NewSecureEncoder,
  OldSecureEncoder,
  # Oldest:
  LegacyInsecureEncoder
)
```

When receiving an ID or cursor, a versioned encoders tries each encoder in sequence. When creating a new ID or cursor, the encoder always uses the first encoder. This way, clients will receiving _new_ encoders, but the server will still accept _old_ encoders (until the old one is removed from the list).

`VersionedEncoder#decode_versioned` returns two values: the decoded data _and_ the encoder which successfully decoded it. You can use this to determine how to process decoded data. For example, you can switch on the encoder:

```ruby
data, encoder = VersionedEncoder.decode_versioned(id)
case encoder
when UUIDEncoder
  find_by_uuid(data)
when SQLPrimaryKeyEncoder
  find_by_pk(data)
when nil
  # `id` could not be decoded
  nil
end
```

## Encoding

By default, encrypted bytes is stringified as base-64. You can specific a custom encoder with the `Encoder#encoder` definition. For example, you could define an encode which uses URL-safe base-64 functions:

```ruby
module URLSafeEncoder
  def self.encode(str)
    Base64.urlsafe_encode64(str)
  end
  def self.decode(str)
    Base64.urlsafe_decode64(str)
  end
end
```

Then attach it to your encoder:

```ruby
class MyURLSafeEncoder < GraphQL::Pro::Encoder
  encoder URLSafeEncoder
end
```

Now, these node IDs and cursors will be URL-safe!

---

### Guides Pro Installation (`guides/pro/installation.md`)

# Installation

`GraphQL::Pro` is distributed as a Ruby gem. When you buy `GraphQL::Pro`, you'll receive credentials, which you can register with bundler:

```sh
bundle config gems.graphql.pro #{YOUR_CREDENTIALS}
```

Then, you can add `graphql-pro` to your Gemfile, which a custom `source`:

```ruby
source "https://gems.graphql.pro" do
  gem "graphql-pro"
end
```

Then, install the gem with Bundler:

```sh
bundle install
```

Then, check out some of `GraphQL::Pro`'s features!

## Updates

To update `GraphQL::Pro`, use Bundler:

```sh
bundle update graphql-pro
```

Be sure to check the [changelog](https://github.com/rmosolgo/graphql-ruby/blob/master/CHANGELOG-pro.md) between versions!

## Dependencies

`graphql-pro 1.0.0` requires `graphql ~>1.4`. The latest version requires `graphql =>1.7.6`.

## Verifying Integrity

You can verify the integrity of `graphql-pro` by getting its checksum and comparing it to the [published checksums](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/pro/checksums).

Include the `graphql:pro:validate` task in your `Rakefile`:

```ruby
# Rakefile
require "graphql/rake_task/validate"
```

Then invoke it with a version:

```
$ bundle exec rake graphql:pro:validate[1.0.0]
Validating graphql-pro v1.0.0
  - Checking for graphql-pro credentials...
    ✓ found
  - Fetching the gem...
    ✓ fetched
  - Validating digest...
    ✓ validated from GitHub
    ✓ validated from graphql-ruby.org
✔ graphql-pro 1.0.0 validated successfully!
```

In case of a failure, please [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=GraphQL+Pro+installation+failure&body=):

```
Validating graphql-pro v1.4.800
  - Checking for graphql-pro credentials...
    ✓ found
  - Fetching the gem...
    ✓ fetched
  - Validating digest...
    ✘ SHA mismatch:
      Downloaded:       c9cab2619aa6540605ce7922784fc84dbba3623383fdce6b17fde01d8da0aff49d666810c97f66310013c030e3ab7712094ee2d8f1ea9ce79aaf65c1684d992a
      GitHub:           404: Not Found
      graphql-ruby.org: 404: Not Found

      This download of graphql-pro is invalid, please open an issue:
      https://github.com/rmosolgo/graphql-ruby/issues/new?title=graphql-pro%20digest%20mismatch%20(1.4.800)
```

---

### Guides Pro Privacy (`guides/pro/privacy.md`)

# Privacy

The following statement describes what data GraphQL::Pro collects during normal operation and how that data is used.

## What Data We Collect And How It's Used

GraphQL::Pro collects the following kinds of data:

Kind of Data | Use
------ | -----
Cookies | User authentication
HTTP traffic information, such as IP address and bundler credential | Authentication, system operation
User-provided profile information, such as contact email address and company name | Business communications, newsletter delivery (if signed up)
Billing information | Recurring payment for GraphQL::Pro license

The `graphql-pro` Ruby gem collects no data and never "phones home" for any purpose.

GraphQL::Pro is not directed at children under the age of 13. If you are under age 13, please do not use GraphQL::Pro.

## Third-Party Services

Use of GraphQL::Pro includes the following third-party services:

Name of Service | Use
------|------
GitHub Pages | Website Hosting
Digital Ocean | Cloud Application Hosting
Heroku | Cloud Application Hosting
Bugsnag | Application Monitoring
New Relic APM | Application Monitoring
Papertrail | Application Monitoring
Stripe | Payment Processing
Buttondown | Newsletter Management
Google Apps | Company Infrastructure

## Data Security

GraphQL::Pro does not collect ["Sensitive Personal Information"](https://gdpr-info.eu/art-9-gdpr/). GraphQL::Pro's systems are secured with strong, unique passwords and two-factor authentication is enabled wherever possible.

You are responsible for using a strong, unique password to log into https://billing.graphql.pro.

## More Information

This document is managed [on GitHub](https://github.com/rmosolgo/graphql-ruby/blob/master/guides/pro/privacy.md). You can use a GitHub account to watch for changes or subscribe to a [public RSS feed](https://github.com/rmosolgo/graphql-ruby/commits/master.atom).

For questions, comments, or requests regarding this policy, please contact [info@graphql.pro](mailto:info@graphql.pro)

---

### Guides Queries Ast Analysis (`guides/queries/ast_analysis.md`)

# Ahead-of-Time AST Analysis

You can do ahead-of-time analysis for your queries.

The primitive for analysis is [GraphQL::Analysis::Analyzer](rdoc-ref:GraphQL::Analysis::Analyzer). Analyzers must inherit from this base class and implement the desired methods for analysis.

## Using Analyzers

Query analyzers are added to the schema with `query_analyzer`, for example:

```ruby
class MySchema < GraphQL::Schema
  query_analyzer MyQueryAnalyzer
end
```

Pass the **class** (and not an _instance_) of your analyzer. The analysis engine will take care of instantiating your analyzers with the query.

## Analyzer API

Analyzers respond to methods similar to AST visitors. They're named like `on_enter_#{ast_node}` and `on_leave_#{ast_node}`. Methods are called with three arguments:

- `node`: The current AST node (being entered or left)
- `parent`: The AST node which precedes this one in the tree
- `visitor`: A [GraphQL::Analysis::Visitor](rdoc-ref:GraphQL::Analysis::Visitor) which is managing this analysis run

For example:

```ruby
class BasicCounterAnalyzer < GraphQL::Analysis::Analyzer
  def initialize(query_or_multiplex)
    super
    @fields = Set.new
    @arguments = Set.new
  end

  # Visitors are all defined on the Analyzer base class
  # We override them for custom analyzers.
  def on_leave_field(node, _parent, _visitor)
    @fields.add(node.name)
  end

  def result
    # Do something with the gathered result.
    Analytics.log(@fields)
  end
end
```

In this example, we counted every field, no matter if it was on fragment definitions
or if it was skipped by directives. If we want to detect those contexts, we can use helper
methods:

```ruby
class BasicFieldAnalyzer < GraphQL::Analysis::Analyzer
  def initialize(query_or_multiplex)
    super
    @fields = Set.new
  end

  # Visitors are all defined on the Analyzer base class
  # We override them for custom analyzers.
  def on_leave_field(node, _parent, visitor)
    if visitor.skipping? || visitor.visiting_fragment_definition?
      # We don't want to count skipped fields or fields
      # inside fragment definitions
    else
      @fields.add(node.name)
    end
  end

  def result
    Analytics.log(@fields)
  end
end
```

See [GraphQL::Analysis::Visitor](rdoc-ref:GraphQL::Analysis::Visitor) for more information about the `visitor` object.

### Field Arguments

Usually, analyzers will use `on_enter_field` and `on_leave_field` to process queries. To get a field's arguments during analysis, use `visitor.query.arguments_for(node, visitor.field_definition)` ([GraphQL::Query#arguments_for](rdoc-ref:GraphQL::Query#arguments_for)). That method returns coerced argument values and normalizes argument literals and variable values.

### Errors

It is still possible to return errors from an analyzer. To reject a query and halt its execution, you may return [GraphQL::AnalysisError](rdoc-ref:GraphQL::AnalysisError) in the `result` method:

```ruby
class NoFieldsCalledHello < GraphQL::Analysis::Analyzer
  def on_leave_field(node, _parent, visitor)
    if node.name == "hello"
      @field_called_hello = true
    end
  end

  def result
    GraphQL::AnalysisError.new("A field called `hello` was found.") if @field_called_hello
  end
end
```

### Conditional Analysis

Some analyzers might only make sense in certain context, or some might be too expensive to run for every query. To handle these scenarios, your analyzers may answer to an `analyze?` method:

```ruby
class BasicFieldAnalyzer < GraphQL::Analysis::Analyzer
  # Use the analyze? method to enable or disable a certain analyzer
  # at query time.
  def analyze?
    !!subject.context[:should_analyze]
  end

  def on_leave_field(node, _parent, visitor)
    # ...
  end

  def result
    # ...
  end
end
```

## Analyzing Multiplexes

Analyzers are initialized with the _unit of analysis_, available as `subject`.

When analyzers are hooked up to multiplexes, `query` is `nil`, but `multiplex` returns the subject of analysis. You can use `visitor.query` inside visit methods to reference the query that owns the current AST node.

Note that some built-in analyzers (eg `AST::MaxQueryDepth`) support multiplexes even though `Query` is in their name.

---

### Guides Queries Backtrace Annotations (`guides/queries/backtrace_annotations.md`)

# Backtrace Annotations

`context` objects have a `backtrace` which shows its GraphQL context. You can print the backtrace during query execution:

```ruby
puts context.backtrace
# Loc  | Field                         | Object       | Arguments             | Result
# 3:13 | User.login                    | #<User id=1> | {"message"=>"Boom"}   | #<RuntimeError: This is broken: Boom>
# 2:11 | Query.user                    | nil          | {"login"=>"rmosolgo"} | {}
# 1:9  | query                         | nil          | {"msg"=>"Boom"}       |
```

The backtrace contains some execution data:

- `Loc` is the `line:column` of the field in the query string
- `Field` is the `TypeName.fieldName` of the fields in the backtrace
- `Object` is the `obj` for query resolution (used for resolving the given field), equivalent to `obj.inspect`
- `Arguments` are the GraphQL arguments for field resolution (including any default values and variable values)
- `Result` is the GraphQL-ready result which is being constructed (it may be incomplete if the query is still in-progress)

## Wrapping Errors

You can wrap unhandled errors with a GraphQL error with `GraphQL::Backtrace`.

To enable this feature for a query, add `backtrace: true` to your `context`, for example:

```ruby
# Wrap this query with backtrace annotation
MySchema.execute(query_string, context: { backtrace: true })
```

Or, to _always_ wrap backtraces, add it to your schema definition with `use`, for example:

```ruby
class MySchema < GraphQL::Schema
  # Always wrap backtraces with GraphQL annotation
  use GraphQL::Backtrace
end
```

Now, any unhandled errors will be wrapped by `GraphQL::Backtrace::TracedError`, which prints out the GraphQL backtrace, too. For example:

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Guides Queries Complexity And Depth (`guides/queries/complexity_and_depth.md`)

# Complexity & Depth

GraphQL-Ruby ships with some validations based on [query analysis](/queries/ast_analysis). You can customize them as-needed, too.

## Prevent deeply-nested queries

You can also reject queries based on the depth of their nesting. You can define `max_depth` at schema-level or query-level:

```ruby
# Schema-level:
class MySchema < GraphQL::Schema
  # ...
  max_depth 15
end

# Query-level, which overrides the schema-level setting:
MySchema.execute(query_string, max_depth: 20)
```

By default, **introspection fields are counted**. The default introspection query requires at least `max_depth 13`. You can also configure your schema not to count introspection fields with `max_depth ..., count_introspection_fields: false`.

You can use `nil` to disable the validation:

```ruby
# This query won't be validated:
MySchema.execute(query_string, max_depth: nil)
```

To get a feeling for depth of queries in your system, you can extend [GraphQL::Analysis::QueryDepth](rdoc-ref:GraphQL::Analysis::QueryDepth). Hook it up to log out values from each query:

```ruby
class LogQueryDepth < GraphQL::Analysis::QueryDepth
  def result
    query_depth = super
    message = "[GraphQL Query Depth] #{query_depth} || staff?  #{query.context[:current_user].staff?}"
    Rails.logger.info(message)
  end
end

class MySchema < GraphQL::Schema
  query_analyzer(LogQueryDepth)
end
```

## Prevent complex queries

Fields have a "complexity" value which can be configured in their definition. It can be a constant (numeric) value, or a proc. If no `complexity` is defined for a field, it will default to a value of `1`. It can be defined as a keyword _or_ inside the configuration block. For example:

```ruby
# Constant complexity:
field :top_score, Integer, null: false, complexity: 10

# Dynamic complexity:
field :top_scorers, [PlayerType], null: false do
  argument :limit, Integer, limit: false, default_value: 5
  complexity ->(ctx, args, child_complexity) {
    if ctx[:current_user].staff?
      # no limit for staff users
      0
    else
      # `child_complexity` is the value for selections
      # which were made on the items of this list.
      #
      # We don't know how many items will be fetched because
      # we haven't run the query yet, but we can estimate by
      # using the `limit` argument which we defined above.
      args[:limit] * child_complexity
    end
  }
end
```

Then, define your `max_complexity` at the schema-level:

```ruby
class MySchema < GraphQL::Schema
  # ...
  max_complexity 100
end
```

Or, at the query-level, which overrides the schema-level setting:

```ruby
MySchema.execute(query_string, max_complexity: 100)
```

Using `nil` will disable the validation:

```ruby
# 😧 Anything goes!
MySchema.execute(query_string, max_complexity: nil)
```

To get a feeling for complexity of queries in your system, you can extend [GraphQL::Analysis::QueryComplexity](rdoc-ref:GraphQL::Analysis::QueryComplexity). Hook it up to log out values from each query:

```ruby
class LogQueryComplexityAnalyzer < GraphQL::Analysis::QueryComplexity
  # Override this method to _do something_ with the calculated complexity value
  def result
    complexity = super
    message = "[GraphQL Query Complexity] #{complexity} | staff? #{query.context[:current_user].staff?}"
    Rails.logger.info(message)
  end
end

class MySchema < GraphQL::Schema
  query_analyzer(LogQueryComplexityAnalyzer)
end
```

By default, **introspection fields are counted**. You can also configure your schema not to count introspection fields with `max_complexity ..., count_introspection_fields: false`.

#### Connection fields

By default, GraphQL-Ruby calculates a complexity value for connection fields by:

- adding `1` for `pageInfo` and each of its subselections
- adding `1` for `count`, `totalCount`, or `total`
- adding `1` for the connection field itself
- multiplying the complexity of other fields by the largest possible page size, which is the greater of `first:` or `last:`, or if neither of those are given it will go through each of `default_page_size`, the schema's `default_page_size`, `max_page_size`, and then the schema's `default_max_page_size`.

    (If no default page size or max page size can be determined, then the analysis crashes with an internal error -- set `default_page_size` or `default_max_page_size` in your schema to prevent this.)

For example, this query has complexity `26`:

```graphql
query {
  author {              # +1
    name                # +1
    books(first: 10) {  # +1
      nodes {           # +10 (+1, multiplied by `first:` above)
        title           # +10 (ditto)
      }
      pageInfo {        # +1
        endCursor       # +1
      }
      totalCount        # +1
    }
  }
}
```

To customize this behavior, implement `def calculate_complexity(query:, nodes:, child_complexity:)` in your base field class, handling the case where `self.connection?` is `true`:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  def calculate_complexity(query:, nodes:, child_complexity:)
    if connection?
      # Custom connection calculation goes here
    else
      super
    end
  end
end
```

## How complexity scoring works

GraphQL Ruby's complexity scoring algorithm is biased towards selection fairness. While highly accurate, its results are not always intuitive. Here's an example query performed on the [Shopify Admin API](https://shopify.dev/docs/api/admin-graphql):

```graphql
query {
  node(id: "123") { # interface Node
    id
    ...on HasMetafields { # interface HasMetafields
      metafield(key: "a") {
        value
      }
      metafields(first: 10) {
        nodes {
          value
        }
      }
    }
    ...on Product { # implements HasMetafields
      title
      metafield(key: "a") {
        definition {
          description
        }
      }
    }
    ...on PriceList {
      name
      catalog {
        id
      }
    }
  }
}
```

First, GraphQL Ruby allows field definitions to specify a `complexity` attribute that provides a complexity score (or a proc that computes a score) for each field. Let's say that this schema defines a system where:

- Leaf fields cost `0`
- Composite fields cost `1`
- Connection fields cost `children * input size`

Given these parameters, we get an itemized scoring distribution of:

```graphql
query {
  node(id: "123") { # 1, composite
    id # 0, leaf
    ...on HasMetafields {
      metafield(key: "a") { # 1, composite
        value # 0, leaf
      }
      metafields(first: 10) { # 1 * 10, connection
        nodes { # 1, composite
          value # 0, leaf
        }
      }
    }
    ...on Product {
      title # 0, leaf
      metafield(key: "a") { # 1, composite
        definition { # 1, composite
          description # 0, leaf
        }
      }
    }
    ...on PriceList {
      name # 0, leaf
      catalog { # 1, composite
        id # 0, leaf
      }
    }
  }
}
```

However, we cannot naively tally these itemized scores without over-costing the query. Consider:

- The `node` scope makes many _possible_ selections on an abstract type, so we need the maximum among concrete possibilities for a fair representation.
- A `node.metafield` selection path is duplicated across the `HasMetafields` and `Product` selection scopes. This path will only resolve once, so should also only cost once.

To reconcile these possibilities, the [complexity algorithm](https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/analysis/query_complexity.rb) breaks the selection down into a tree of types mapped to possible selections, across which lexical selections can be coalesced and deduplicated (pseudocode):

```ruby
{
  Schema::Query => {
    "node" => {
      Schema::Node => {
        "id" => nil,
      },
      Schema::HasMetafields => {
        "metafield" => {
          Schema::Metafield => {
            "value" => nil,
          },
        },
        "metafields" => {
          Schema::Metafield => {
            "nodes" => { ... },
          },
        },
      },
      Schema::Product => {
        "title" => nil,
        "metafield" => {
          Schema::Metafield => {
            "definition" => { ... },
          },
        },
      },
      Schema::PriceList => {
        "name" => nil,
        "catalog" => {
          Schema::Catalog => {
            "id" => nil,
          },
        },
      },
    },
  },
}
```

This aggregation provides a new perspective on the scoring where _possible typed selections_ have costs rather than individual fields. In this normalized view, `Product` acquires the `HasMetafields` interface costs, and ignores a duplicated path. Ultimately the maximum of possible typed costs is used, making this query cost `12`:

```graphql
query {
  node(id: "123") { # max(11, 12, 1) = 12
    id
    ...on HasMetafields { # 1 + 10 = 11
      metafield(key: "a") { # 1
        value
      }
      metafields(first: 10) { # 10
        nodes {
          value
        }
      }
    }
    ...on Product { # 1 + 11 from HasMetafields = 12
      title
      metafield(key: "a") { # duplicated in HasMetafields
        definition { # 1
          description
        }
      }
    }
    ...on PriceList { # 1 = 1
      name
      catalog { # 1
        id
      }
    }
  }
}
```

---

### Guides Queries Executing Queries (`guides/queries/executing_queries.md`)

# Executing Queries

You can execute queries with your [GraphQL::Schema](rdoc-ref:GraphQL::Schema) and get a Ruby Hash as a result. For example, to execute a query from a string:

```ruby
query_string = "{ ... }"
MySchema.execute(query_string)
# {
#   "data" => { ... }
# }
```

Or, you can execute multiple queries at once:

```ruby
MySchema.multiplex([
  {query: query_string_1},
  {query: query_string_2},
  {query: query_string_3},
])
# [
#   { "data" => { ... } },
#   { "data" => { ... } },
#   { "data" => { ... } },
# ]
```

The complete option contract for `variables:`, `context:`, `root_value:`,
`operation_name:`, `document:`, validation, and query limits is maintained in the
[GraphQL::Query](rdoc-ref:GraphQL::Query) and
[GraphQL::Schema](rdoc-ref:GraphQL::Schema) API references. The examples below
focus on the application-level patterns built on those options.

The API-specific portions of this page were migrated to the
[GraphQL::Query](rdoc-ref:GraphQL::Query) and
[GraphQL::Schema](rdoc-ref:GraphQL::Schema) source comments. This page keeps
the variables, context, scoped-context, and root-value walkthroughs.

## Variables

GraphQL provides [query variables](https://graphql.org/learn/queries/#variables) as a way to parameterize query strings. If your query string contains variables, you can provide values in a hash of `{ String => value }` pairs. The keys should _not_ contain `"$"`.

For example, to provide variables to a query:

```ruby
query_string = "
  query getPost($postId: ID!) {
    post(id: $postId) {
      title
    }
  }"

variables = { "postId" => "1" }

MySchema.execute(query_string, variables: variables)
```

If the variable is a [GraphQL::Schema::InputObject](rdoc-ref:GraphQL::Schema::InputObject), you can provide a nested hash, for example:

```ruby
query_string = "
mutation createPost($postParams: PostInput!, $createdById: ID!){
  createPost(params: $postParams, createdById: $createdById) {
    id
    title
    createdBy { name }
  }
}
"

variables = {
  "postParams" => {
    "title" => "...",
    "body" => "..."
  },
  "createdById" => "5",
}

MySchema.execute(query_string, variables: variables)
```

## Context

You can provide application-specific values to GraphQL as `context:`. This is available in many places:

- `resolve` functions
- `Schema#resolve_type` hook
- ID generation & fetching

Common uses for `context:` include the current user or auth token. To provide a `context:` value, pass a hash to `Schema#execute`:

```ruby
context = {
  current_user: session[:current_user],
  current_organization: session[:current_organization],
}

MySchema.execute(query_string, context: context)
```

Then, you can access those values during execution:

```ruby
field :post, Post do
  argument :id, ID
end

def post(id:)
  context[:current_user] # => #<User id=123 ... >
  # ...
end
```

Note that `context` is _not_ the hash that you passed it. It's an instance of [GraphQL::Query::Context](rdoc-ref:GraphQL::Query::Context), but it delegates `#[]`, `#[]=`, and a few other methods to the hash you provide.

### Scoped Context

`context` is shared by the whole query. Anything you add to `context` will be accessible by any other field in the query (although GraphQL-Ruby's order of execution can vary).

However, "scoped context" can be used to assign values into `context` that are only available in the current field and the _children_ of the current field. For example, in this query:

```graphql
{
  posts {
    comments {
      author {
        isOriginalPoster
      }
    }
  }
}
```

You could use "scoped context" to implement `isOriginalPoster`, based on the parent `comments` field.

> **Warning:**
>
> Using scoped context may result in a violation of [the GraphQL specification](https://spec.graphql.org/draft/#sel-EABDLDFAACHAo3V) and
> break normalized client stores, which assume that a given object always
> has the same values for its fields.
>
> See ["Referencing ancestors breaks normalized stores"](https://benjie.dev/graphql/ancestors#breaks-normalized-stores)
> for details about this pitfall and alternative approaches which avoid it.
>


In `def comments`, add `:current_post` to scoped context using `context.scoped_set!`:

```ruby
class Types::Post < Types::BaseObject
  # ...
  def comments
    context.scoped_set!(:current_post, object)
    object.comments
  end
end
```

Then, inside `User` (assuming `author` resolves to `Types::User`), you can check `context[:current_post]`:

```ruby
class Types::User < Types::BaseObject
  # ...
  def is_original_poster
    current_post = context[:current_post]
    current_post && current_post.author == object
  end
end
```

`context[:current_post]` will be present if an "upstream" field assigned it with `scoped_set!`.

`context.scoped_merge!({ ... })` is also available for setting multiple keys at once.

**Note**: With batched data loading (eg, GraphQL-Batch), scoped context might not work because of GraphQL-Ruby's control flow jumps from one field to the next. In that case, use `scoped_ctx = context.scoped` to grab a scoped context reference _before_ calling a loader, then used `scoped_ctx.set!` or `scoped_ctx.merge!` to modify scoped context inside the promise body. For example:

```ruby
# For use with GraphQL-Batch promises:
scoped_ctx = context.scoped
SomethingLoader.load(:something).then do |thing|
  scoped_ctx.set!(:thing_name, thing.name)
end
```

## Root Value

You can provide a root `object` value with `root_value:`. For example, to base the query off of the current organization:

```ruby
current_org = session[:current_organization]
MySchema.execute(query_string, root_value: current_org)
```

That value will be provided to root-level fields, such as mutation fields. For example:

```ruby
class Types::MutationType < GraphQL::Schema::Object
  field :create_post, Post

  def create_post(**args)
    object # => #<Organization id=456 ...>
    # ...
  end
end
```

[GraphQL::Schema::Mutation](rdoc-ref:GraphQL::Schema::Mutation) fields will also receive `root_value:` as `obj` (assuming they're attached directly to your `MutationType`).

---

### Guides Queries Logging (`guides/queries/logging.md`)

# Logging

At runtime, GraphQL-Ruby will output debug information using [GraphQL::Query#logger](rdoc-ref:GraphQL::Query#logger). By default, this uses `Rails.logger`. To see output, make sure `config.log_level = :debug` is set. (This information isn't meant for production logs.)

You can configure a custom logger with [GraphQL::Schema.default_logger](rdoc-ref:GraphQL::Schema.default_logger), for example:

```ruby
class MySchema < GraphQL::Schema
  # This logger will be used by queries during execution:
  default_logger MyCustomLogger.new
end
```

You can also pass `context[:logger]` to provide a logger during execution.

---

### Guides Queries Lookahead (`guides/queries/lookahead.md`)

# Lookahead

GraphQL-Ruby 1.9+ includes [GraphQL::Execution::Lookahead](rdoc-ref:GraphQL::Execution::Lookahead) for checking whether child fields are selected. You can use this to optimize database access, for example, selecting only the _needed_ fields from the database.

## Getting a Lookahead

Add `extras: [:lookahead]` to your field configuration to receive an injected lookahead:

```ruby
field :files, [Types::File], null: false, extras: [:lookahead]
```

Then, update your resolver method to accept a `lookahead:` argument:

```ruby
def files(lookahead:)
  # ...
end
```

That argument will be injected by the GraphQL runtime.

## Using a lookahead

Inside your field resolver, you can use the lookahead to check for child fields. For example, you can check for a __specific selection__:

```ruby
def files(lookahead:)
  if lookahead.selects?(:full_path)
    # This is a query like `files { fullPath ... }`
  else
    # This query doesn't have `fullPath`
  end
end
```

Or, you can list __all the selected fields__:

```ruby
def files(lookahead:)
  all_selections = lookahead.selections.map(&:name)
  if all_selections == [:name]
    # Only `files { name }` was selected, use a fast cached value:
    object.file_names.map { |n| { name: n }}
  else
    # Lots of fields were selected, fall back to a more resource-intensive approach
    FileSystemHelper.load_files_for(object)
  end
end
```

Lookaheads are _chainable_, so you can use them to check __nested selections__ too:

```ruby
def files(lookahead:)
  if lookahead.selection(:history).selects?(:author)
    # For example, `files { history { author { ... } } }`
    # We're checking for commit authors, so load those objects appropriately ...
  else
    # Not selecting commit authors ...
  end
end
```

Nested lookaheads return empty objects when there's no selection (not `nil`), so the code above will never have a "no method error on `nil`".

## Lookaheads with connections

If you want to see what selections were made on the items in a connection, you can use nested lookaheads. However, don't forget to check for `edges { node }` _and_ `nodes { ... }`, if you support that shortcut field. For example:

```ruby
field :products, Types::Product.connection_type, null: false, extras: [:lookahead]

def products(lookahead:)
  selects_quantity_available = lookahead.selection(:nodes).selects?(:quantity_available) ||
                               # ^^ check for `products { nodes { quantityAvailable } }`
    lookahead.selection(:edges).selection(:node).selects?(:quantity_available)
    # ^^ check for `products { edges { node { quantityAvailable } } }`

  if selects_quantity_available
    # ...
  else
    # ...
  end
end
```

That way, you can check for specific selections on the nodes in a connection.

## Lookaheads with aliases

If you want to find selection by its [alias](https://spec.graphql.org/June2018/#sec-Field-Alias), you can use `#alias_selection(...)` or check if it exists with `#selects_alias?`. In this case, the lookahead will check if there is a field with the provided alias.


For example, this query can find a bird species by its name:

```graphql
query {
  gull: findBirdSpecies(byName: "Laughing Gull") {
    name
  }

  tanager: findBirdSpecies(byName: "Scarlet Tanager") {
    name
  }
}
```

You can get the lookahead for each selection in a following way:

```ruby
def find_bird_species(by_name:, lookahead:)
  if lookahead.selects_alias?("gull")
    lookahead.alias_selection("gull")
  end
end
```

---

### Guides Queries Multiplex (`guides/queries/multiplex.md`)

# Multiplex

Some clients may send _several_ queries to the server at once (for example, [Apollo Client's query batching](https://www.apollographql.com/docs/react/api/link/apollo-link-batch-http/)). You can execute them concurrently with [Schema.multiplex](rdoc-ref:GraphQL::Schema.multiplex).

Multiplex runs have their own context, analyzers and instrumentation.

__NOTE:__ As an implementation detail, _all_ queries run inside multiplexes. That is, a stand-alone query is executed as a "multiplex of one", so instrumentation and multiplex analyzers and tracers _will_ apply to standalone queries run with `MySchema.execute(...)`.

## Concurrent Execution

To run queries concurrently, build an array of query options, using `query:` for the query string. For example:

```ruby
# Prepare the context for each query:
context = {
  current_user: current_user,
}

# Prepare the query options:
queries = [
  {
   query: "query Query1 { someField }",
   variables: {},
   operation_name: 'Query1',
   context: context,
 },
 {
   query: "query Query2 ($num: Int){ plusOne(num: $num) }",
   variables: { num: 3 },
   operation_name: 'Query2',
   context: context,
 }
]
```

Then, pass them to `Schema.multiplex`:

```ruby
results = MySchema.multiplex(queries)
```

`results` will contain the result for each query in `queries`. __NOTE:__ The results will always be in the same order that their respective requests were sent in.

## Apollo Query Batching

Apollo sends batches of queries as an array of queries. Rails' ActionDispatch will parse the request and put the result into the `_json` field of the `params` variable. You also need to ensure that your schema can handle both batched and non-batched queries, below is an example of the default GraphqlController rewritten to handle Apollo batches:

```ruby
def execute
  context = {}

  # Apollo sends the queries in an array when batching is enabled. The data ends up in the _json field of the params variable.
  # see the Apollo Documentation about query batching: https://www.apollographql.com/docs/react/api/link/apollo-link-batch-http/
  result = if params[:_json]
    queries = params[:_json].map do |param|
      {
        query: param[:query],
        operation_name: param[:operationName],
        variables: ensure_hash(param[:variables]),
        context: context
      }
    end
    MySchema.multiplex(queries)
  else
    MySchema.execute(
      params[:query],
      operation_name: params[:operationName],
      variables: ensure_hash(params[:variables]),
      context: context
    )
  end

  render json: result, root: false
end
```

## Validation and Error Handling

Each query is validated and [analyzed](/queries/ast_analysis) independently. The `results` array may include a mix of successful results and failed results.

## Multiplex-Level Context

You can add values to `Execution::Multiplex#context` by providing a `context:` hash:

```ruby
MySchema.multiplex(queries, context: { current_user: current_user })
```

This will be available to instrumentation as `multiplex.context[:current_user]` (see below).

## Multiplex-Level Analysis

You can analyze _all_ queries in a multiplex by adding a multiplex analyzer. For example:

```ruby
class MySchema < GraphQL::Schema
  # ...
  multiplex_analyzer(MyAnalyzer)
end
```

The API is the same as [query analyzers](/queries/ast_analysis#analyzing-multiplexes).

Multiplex analyzers may return [GraphQL::AnalysisError](rdoc-ref:GraphQL::AnalysisError) to halt execution of the whole multiplex.

## Multiplex Tracing

You can add hooks for each multiplex run with [trace modules](/queries/tracing).

The trace module may implement `def execute_multiplex(multiplex:)` which calls `super` to allow the multiplex to execute. See `Execution::Multiplex` for available methods.

For example:

```ruby
# Count how many queries are in the multiplex run:
module MultiplexCounter
  def execute_multiplex(multiplex:)
    Rails.logger.info("Multiplex size: #{multiplex.queries.length}")
    super
  end
end

# ...

class MySchema < GraphQL::Schema
  # ...
  trace_with(MultiplexCounter )
end
```

Now, `MultiplexCounter#execute_multiplex` will be called for each execution, logging the size of each multiplex.

---

### Guides Queries Phases Of Execution (`guides/queries/phases_of_execution.md`)

# Phases of Execution

When GraphQL receives a query string, it goes through these steps:

- Tokenize: [GraphQL::Language::Lexer](rdoc-ref:GraphQL::Language::Lexer) splits the string into a stream of tokens
- Parse: [GraphQL::Language::Parser](rdoc-ref:GraphQL::Language::Parser) builds an abstract syntax tree (AST) out of the stream of tokens
- Validate: [GraphQL::StaticValidation::Validator](rdoc-ref:GraphQL::StaticValidation::Validator) validates the incoming AST as a valid query for the schema
- Analyze: If there are any query analyzers, they are run with [GraphQL::Analysis.analyze_query](rdoc-ref:GraphQL::Analysis.analyze_query)
- Execute: The query is traversed, `resolve` functions are called and the response is built
- Respond: The response is returned as a [GraphQL::Query::Result](rdoc-ref:GraphQL::Query::Result)

---

### Guides Queries Response Extensions (`guides/queries/response_extensions.md`)

# Response Extensions

During query execution, you can add to the response's `"extensions" => { ... }` Hash. By default, no `"extensions"` key is present in the result, but if you call the method below, it will be present with the given values.

To add to `"extensions"`, call `context.response_extensions[key] = value` during execution. For example:

```ruby
field :to_dos, [ToDo]

def to_dos
  warnings = context.response_extensions["warnings"] ||= []
  warnings << "To-Dos will be disabled on Jan. 31, 2022."
  context[:current_user].deprecated_to_dos
end
```


That would add to the final query response:

```ruby
{
  "data" => { ... },
  "extensions" => {
    "warnings" => ["To-Dos will be disabled on Jan. 31, 2022"],
  },
}
```

Values written to `context.response_extensions` are added to the GraphQL response verbatim.

---

### Guides Queries Timeout (`guides/queries/timeout.md`)

# Timeout

You can apply a timeout to query execution with the `GraphQL::Schema::Timeout` plugin. For example:

```ruby
class MySchema < GraphQL::Schema
  use GraphQL::Schema::Timeout, max_seconds: 2
end
```

After `max_seconds`, no new fields will be resolved. Instead, errors will be added to the `errors` key for fields that weren't resolved.

__Note__ that this _does not interrupt_ field execution (doing so is [buggy](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/)). If you're making external calls (eg, HTTP requests or database queries), make sure to use a library-specific timeout for that operation (eg, [Redis timeout](https://github.com/redis/redis-rb#timeouts), [Net::HTTP](https://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html)'s `ssl_timeout`, `open_timeout`, and `read_timeout`).

## Custom Error Handling

To log the error, provide a subclass of `GraphQL::Schema::Timeout` with an overridden `handle_timeout` method:

```ruby
class MyTimeout < GraphQL::Schema::Timeout
  def handle_timeout(error, query)
    Rails.logger.warn("GraphQL Timeout: #{error.message}: #{query.query_string}")
  end
end

class MySchema < GraphQL::Schema
  use MyTimeout, max_seconds: 2
end
```

## Customizing the Timeout Window

To dynamically pick a timeout duration (or bypass it), override [GraphQL::Schema::Timeout#max_seconds](rdoc-ref:GraphQL::Schema::Timeout#max_seconds) in your subclass. To bypass the timeout altogether, `max_seconds` can return `false`.

For example:

```ruby
class MyTimeout < GraphQL::Schema::Timeout
  # Allow 10s for an incoming mutation, but don't apply any timeout for an admin user.
  def max_seconds(query)
    if query.context[:current_user]&.admin?
      false
    elsif query.mutation?
      10
    else
      super
    end
  end
end

# ...

class MySchema < GraphQL::Schema
  use MyTimeout, max_seconds: 5
end
```

## Validation and Analysis

Queries can originate from a user, and may be crafted in a manner to take a long time to validate against the schema.

It is possible to limit how many seconds the static validation rules and analysers are allowed to run before returning a validation timeout error. By default, validation and query analysis have a 3-second timeout. You can customize this timeout or disable it completely:

For example:

```ruby
# Customize timeout (in seconds)
class MySchema < GraphQL::Schema
  # Applies to static validation and query analysis
  validate_timeout 10
end

# OR disable timeout completely
class MySchema < GraphQL::Schema
  validate_timeout nil
end
```

**Note:** This configuration uses Ruby's built-in `Timeout` API, which can interrupt IO calls mid-flight, resulting in [very weird bugs](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/). None of GraphQL-Ruby's validators make IO calls but if you want to use this configuration and you have custom static validators that make IO calls, open an issue to discuss implementing this in an IO-safe way.

---

### Guides Queries Tracing (`guides/queries/tracing.md`)

# Tracing

[GraphQL::Tracing::Trace](rdoc-ref:GraphQL::Tracing::Trace) provides hooks to observe and modify events during runtime. Tracing hooks are methods, defined in modules and mixed in with [Schema.trace_with](rdoc-ref:GraphQL::Schema.trace_with).

```ruby
module CustomTrace
  def parse(query_string:)
    # measure, log, etc
    super
  end

  # ...
end
```

To include a trace module when running queries, add it to the schema with `trace_with`:

```ruby
# Run `MyCustomTrace` for all queries
class MySchema < GraphQL::Schema
  trace_with(MyCustomTrace)
end
```

For a full list of methods and their arguments, see [GraphQL::Tracing::Trace](rdoc-ref:GraphQL::Tracing::Trace).

By default, GraphQL-Ruby makes a new trace instance when it runs a query. You can pass an existing instance as `context: { trace: ... }`. Also, `GraphQL.parse( ..., trace: ...)` accepts a trace instance.

## Detailed Traces

You can capture detailed traces of query execution with [Tracing::DetailedTrace](rdoc-ref:GraphQL::Tracing::DetailedTrace). They can be viewed in Google's [Perfetto Trace Viewer](https://ui.perfetto.dev). They include a per-Fiber breakdown with links between fields and Dataloader sources.



Learn how to set it up in the [Tracing::DetailedTrace](rdoc-ref:GraphQL::Tracing::DetailedTrace) docs.

## External Monitoring Platforms

There integrations for GraphQL-Ruby with several other monitoring systems:

- `ActiveSupport::Notifications`: See [Tracing::ActiveSupportNotificationsTrace](rdoc-ref:GraphQL::Tracing::ActiveSupportNotificationsTrace).
- [AppOptics](https://appoptics.com/) instrumentation is automatic in `appoptics_apm` v4.11.0+.
- [AppSignal](https://appsignal.com/): See [Tracing::AppsignalTrace](rdoc-ref:GraphQL::Tracing::AppsignalTrace).
- [Datadog](https://www.datadoghq.com): See [Tracing::DataDogTrace](rdoc-ref:GraphQL::Tracing::DataDogTrace).
- [NewRelic](https://newrelic.com/): See [Tracing::NewRelicTrace](rdoc-ref:GraphQL::Tracing::NewRelicTrace).
- [Prometheus](https://prometheus.io): See [Tracing::PrometheusTrace](rdoc-ref:GraphQL::Tracing::PrometheusTrace).
- [Scout APM](https://www.scoutapm.com/): See [Tracing::ScoutTrace](rdoc-ref:GraphQL::Tracing::ScoutTrace).
- [Sentry](https://sentry.io): See [Tracing::SentryTrace](rdoc-ref:GraphQL::Tracing::SentryTrace).
- [Skylight](https://www.skylight.io):  either enable the [GraphQL probe](https://www.skylight.io/support/getting-more-from-skylight#graphql) or use [Tracing::ActiveSupportNotificationsTrace](rdoc-ref:GraphQL::Tracing::ActiveSupportNotificationsTrace).
- Statsd: See [Tracing::StatsdTrace](rdoc-ref:GraphQL::Tracing::StatsdTrace).

---

### Guides Relay Range Add (`guides/relay/range_add.md`)

# RangeAdd helper for mutations

Relay specifies `RANGE_ADD` operations for adding items to connections. GraphQL-Ruby ships with [GraphQL::Relay::RangeAdd](rdoc-ref:GraphQL::Relay::RangeAdd) to help implement this. Check the API docs for a usage example.

---

### Guides Schema Definition (`guides/schema/definition.md`)

# Definition

A GraphQL system is called a _schema_. The schema contains all the types and fields in the system. The schema executes queries and publishes an [introspection system](/schema/introspection).

Your GraphQL schema is a class that extends [GraphQL::Schema](rdoc-ref:GraphQL::Schema), for example:

```ruby
class MyAppSchema < GraphQL::Schema
  max_complexity 400
  query Types::Query
  use GraphQL::Dataloader

  # Define hooks as class methods:
  def self.resolve_type(type, obj, ctx)
    # ...
  end

  def self.object_from_id(node_id, ctx)
    # ...
  end

  def self.id_from_object(object, type, ctx)
    # ...
  end
end
```

There are lots of schema configuration methods. The complete reference is
maintained with the implementation in [GraphQL::Schema](rdoc-ref:GraphQL::Schema),
including root types, object identification, error hooks, limits, introspection,
authorization, tracing, analyzers, and plugins.

The API-specific portions of this page were migrated to the
[GraphQL::Schema](rdoc-ref:GraphQL::Schema) source comments. This page keeps the
setup, lazy-loading, and production walkthroughs.

For defining GraphQL types, see the guides for those types: [object types](/type_definitions/objects), [interface types](/type_definitions/interfaces), [union types](/type_definitions/unions),  [input object types](/type_definitions/input_objects), [enum types](/type_definitions/enums), and [scalar types](/type_definitions/scalars).

## Types in the Schema

See the [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema) for
root types, orphan types, and extra types. For defining GraphQL types, see the
guides for [object types](/type_definitions/objects), [interface types](/type_definitions/interfaces),
[union types](/type_definitions/unions), [input objects](/type_definitions/input_objects),
[enums](/type_definitions/enums), and [scalars](/type_definitions/scalars).

### Lazy-loading types

In development, GraphQL-Ruby can defer loading your type definitions until they're needed. This requires some configuration to opt in:

- Add `use GraphQL::Schema::Visibility` to your schema. ([GraphQL::Schema::Visibility](rdoc-ref:GraphQL::Schema::Visibility) supports lazy loading and will be the default in a future GraphQL-Ruby version. See [Migration Notes](/authorization/visibility#migration-notes) if you have an existing visibility implementation.)
- Move your entry-point type definitions into a block, for example:

  ```diff
  - query Types::Query
  + query { Types::Query }
  ```

- Optionally, move field types into blocks, too:

  ```diff
  - field :posts, [Types::Post] # Loads `types/post.rb` immediately
  + field :posts do
  +   type([Types::Post]) # Loads `types/post.rb` when this field is used in a query
  + end
  ```

To enforce these patterns, you can enable two Rubocop rules that ship with GraphQL-Ruby:

- `GraphQL/RootTypesInBlock` will make sure that `query`, `mutation`, and `subscription` are all defined in a block.
- `GraphQL/FieldTypeInBlock` will make sure that non-built-in field return types are defined in blocks.

## Object Identification

The [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema)
documents `object_from_id`, `id_from_object`, and `resolve_type`. For Relay-style
IDs, see [Object Identification](/schema/object_identification); for `loads:`, see
[auto-loading arguments](/mutations/mutation_classes#auto-loading-arguments).

## Error Handling

The [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema)
describes the error hooks. See the [Type errors guide](/errors/type_errors) and
[error handling guide](/errors/error_handling) for application-level examples.

## Default Limits

See the [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema)
for depth, complexity, page-size, timeout, error-count, and query-token limits.
The [Complexity and Depth](/queries/complexity_and_depth) and [Timeout](/queries/timeout)
guides show deployment-oriented examples.

## Introspection

The [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema)
documents `extra_types` and custom introspection namespaces. See the
[introspection guide](/schema/introspection) for a custom introspection system.

## Authorization

The [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema)
documents the unauthorized object and field hooks. See the
[authorization guide](/authorization/authorization) for the complete authorization flow.

## Execution Configuration

The [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema)
documents tracing, analyzers, logging, custom execution classes, lazy values, and
plugins. See [Tracing](/queries/tracing), [Analysis](/queries/ast_analysis),
[Logging](/queries/logging), and [lazy execution](/schema/lazy_execution) for
cross-cutting examples.

## Plugins

Use [Schema.use](rdoc-ref:GraphQL::Schema.use) to install plugins such as
[GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) and
[GraphQL::Schema::Visibility](rdoc-ref:GraphQL::Schema::Visibility). The plugin
contract is documented in the [GraphQL::Schema API reference](rdoc-ref:GraphQL::Schema).

## Production Considerations

- __Parser caching__: if your application parses GraphQL _files_ (queries or schema definition), it may benefit from enabling [GraphQL::Language::Cache](rdoc-ref:GraphQL::Language::Cache).
- __Eager loading the library__: by default, GraphQL-Ruby autoloads its constants as-needed. In production, they should be eager loaded instead, using `GraphQL.eager_load!`.

  - Rails: enabled automatically. (ActiveSupport calls `.eager_load!`.)
  - Sinatra: add `configure(:production) { GraphQL.eager_load! }` to your application file.
  - Hanami: add `environment(:production) { GraphQL.eager_load! }` to your application file.
  - Other frameworks: call `GraphQL.eager_load!` when your application is booting in production mode.

  See [GraphQL::Autoload#eager_load!](rdoc-ref:GraphQL::Autoload#eager_load!) for more details.

---

### Guides Schema Dynamic Types (`guides/schema/dynamic_types.md`)

# Dynamic types and fields

You can use different versions of your GraphQL schema for each operation. To do this, add `use GraphQL::Schema::Visibility` and implement `visible?(context)` on the parts of your schema that will be conditionally accessible. Additionally, many schema elements have definition methods which are called at runtime by GraphQL-Ruby. You can re-implement those to return any valid schema objects.


GraphQL-Ruby caches schema elements for the duration of the operation, but if you're making external service calls to implement the methods below, consider adding a cache layer to improve the client experience and reduce load on your backend.

At runtime, ensure that only one object is visible per name (type name, field name, etc.). (If `.visible?(context)` returns `false`, then that part of the schema will be hidden for the current operation.)

When using dynamic schema members, be sure to include the relevant `context: ...` when [generating schema definition files](#schema-dumps).

## Different fields

You can customize which field definitions are used for each operation.

### Using `#visible?(context)`

To serve different fields to different clients, implement `def visible?(context)` in your [base field class](/type_definitions/extensions#customizing-fields):

```ruby
class Types::BaseField < GraphQL::Schema::Field
  def initialize(*args, for_staff: false, **kwargs, &block)
    super(*args, **kwargs, &block)
    @for_staff = for_staff
  end

  def visible?(context)
    super && case @for_staff
    when true
      !!context[:current_user]&.staff?
    when false
      !context[:current_user]&.staff?
    else
      true
    end
  end
end
```

Then, you can configure fields with `for_staff: true|false`:

```ruby
field :comments, Types::Comment.connection_type, null: false,
  description: "Comments on this blog post",
  resolver_method: :moderated_comments,
  for_staff: false

field :comments, Types::Comment.connection_type, null: false,
  description: "Comments on this blog post, including unmoderated comments",
  resolver_method: :all_comments,
  for_staff: true
```

With that configuration, `post { comments { ... } }` will use `def moderated_comments` when `context[:current_user]` is `nil` or is not `.staff?`, but when `context[:current_user].staff?` is `true`, it will use `def all_comments` instead.

### Using `.fields(context)` and `.get_field(name, context)`

To customize the set of fields used at runtime, you can implement `def self.fields(context)` in your type classes. It should return a Hash of `{ String => GraphQL::Schema::Field }`.

Along with this, you should implement `.get_field(name, context)` to return a field for `name`, if it should exist. For example:

```ruby
class Types::User < Types::BaseObject
  def self.fields(context)
    all_fields = super
    if !context[:current_user]&.staff?
      all_fields.delete("isSpammy") # this is staff-only
    end
    all_fields
  end

  def self.get_field(name, context)
    field = super
    if field.graphql_name == "isSpammy" && !context[:current_user]&.staff?
      nil # don't show this field to non-staff
    else
      field
    end
  end
end
```

### Hidden Return Types

Besides field visibility described above, if an field's return type is hidden (that is, it implements `self.visible?(context)` to return `false`), then the field will be hidden too.

## Different arguments

As with fields, you can use different sets of argument definitions for different GraphQL operations.

### Using `#visible?(context)`

To serve different arguments to different clients, implement `def visible?(context)` in your [base argument class](/type_definitions/extensions#customizing-arguments):

```ruby
class Types::BaseArgument < GraphQL::Schema::Argument
  def initialize(*args, for_staff: false, **kwargs, &block)
    super(*args, **kwargs, &block)
    @for_staff = for_staff
  end

  def visible?(context)
    super && case @for_staff
    when true
      !!context[:current_user]&.staff?
    when false
      !context[:current_user]&.staff?
    else
      true
    end
  end
end
```

Then, you can configure arguments with `for_staff: true|false`:

```ruby
field :user, Types::User, null: true, description: "Look up a user" do
  # Require a UUID-style ID from non-staff clients:
  argument :id, ID, required: true, for_staff: false
  # Support database primary key lookups for staff clients:
  argument :id, ID, required: false, for_staff: true
  argument :database_id, Int, required: false, for_staff: true
end

def user(id: nil, database_id: nil)
  # ...
end
```

That way, any staff client will have the option of `id` or `databaseId` while non-staff clients must use `id`.

### Using `def arguments(context)` and `def get_argument(name, context)`

Also, you can implement `def arguments(context)` on your base field class to return a Hash of `{ String => GraphQL::Schema::Argument }` and `def get_argument(name, context)` to return a [GraphQL::Schema::Argument](rdoc-ref:GraphQL::Schema::Argument) or `nil`. . If you take this approach, you might want some custom field classes for any types or resolvers that use these methods. That way, you don't have to reimplement the method for _all_ the fields in the schema.

### Hidden Input Types

Besides argument visibility described above, if an argument's input type is hidden (that is, it implements `self.visible?(context)` to return `false`), then the argument will be hidden too.

## Different enum values

### Using `#visible?(context)`

You can implement `def visible?(context)` in your [base enum value class](/type_definitions/extensions#customizing-enum-values) to hide some enum values from some clients. For example:

```ruby
class BaseEnumValue < GraphQL::Schema::EnumValue
  def initialize(*args, for_staff: false, **kwargs, &block)
    super(*args, **kwargs, &block)
    @for_staff = for_staff
  end

  def visible?(context)
    super && case @for_staff
    when true
      !!context[:current_user]&.staff?
    when false
      !context[:current_user]&.staff?
    else
      true
    end
  end
end
```

With this base class, you can configure some enum values to be _just_ for staff or non-staff viewers:

```ruby
class AccountStatus < Types::BaseEnum
  value "ACTIVE"
  value "INACTIVE"
  # Use this for sensitive account statuses when the viewer is public:
  value "OTHER", for_staff: false
  # Staff-only sensitive account statuses:
  value "BANNED", for_staff: true
  value "PAYMENT_FAILED", for_staff: true
  value "PENDING_VERIFICATION", for_staff: true
end
```

### Using `.enum_values(context)`

Alternatively, you can implement `def self.enum_values(context)` in your enum types to return an Array of [GraphQL::Schema::EnumValue](rdoc-ref:GraphQL::Schema::EnumValue)s. For example, to return a dynamic set of enum values:

```ruby
class ProjectStatus < Types::BaseEnum
  def self.enum_values(context = {})
    # Fetch the values from the database
    status_names = context[:tenant].project_statuses.pluck("name")

    # Then build an Array of Enum values
    status_names.map do |name|
      # Be sure to include `owner: self`, the back-reference from the EnumValue to its parent Enum
      GraphQL::Schema::EnumValue.new(name, owner: self)
    end
  end
end
```

## Different types

You can also use different types for each query. A few behaviors depend on the methods defined above:

- If a type is not used as a return type, an argument type, or as a member of a union or implementer of an interface, it will be hidden
- If an interface or union has members, it will be hidden
- If a field's return type is hidden, the field will be hidden
- If an argument's input type is hidden, the argument will be hidden

As you can imagine, these different hiding behaviors influence one another and they can cause some real head-scratchers when used simultaneously.

### Using `.visible?(context)`

Type classes can implement `def self.visible?(context)` to hide themselves at runtime:

```ruby
class Types::BanReason < Types::BaseEnum
  # Hide any arguments or fields that use this enum
  # unless the current user is staff
  def self.visible?(context)
    super && !!context[:current_user]&.staff?
  end

  # ...
end
```

### Different definitions for the same type

You can provide different implementations of the same type by:

- Implementing `def self.visible?(context)` to return `true` and `false` in complementary contexts. (They should never both be `.visible? => true`).
- Hooking the types up to the schema with different field or argument definitions, as described above

For example, to migrate your `Money` scalar to a `Money` object type:

```ruby
# Previously, we used a simple string to describe money:
class Types::LegacyMoney < Types::BaseScalar
  # This graphql name will conflict with `Types::Money`,
  # so we have to be careful not to use them at the same time.
  # (GraphQL-Ruby will raise an error if it finds two definitions with the same name at runtime.)
  graphql_name "Money"
  describe "A string describing an amount of money."

  # Use this type definition if the current request
  # explicitly opted in to the legacy money representation:
  def self.visible?(context)
    !!context[:requests_legacy_money]
  end
end

# But we want to improve the client experience with a dedicated object type:
class Types::Money < Types::BaseObject
  field :amount, Integer, null: false
  field :currency, Types::Currency, null: false

  # Use this new definition if the client
  # didn't explicitly ask for the legacy definition:
  def self.visible?(context)
    !context[:requests_legacy_money]
  end
end
```

Then, hook the definitions up to the schema using field definitions:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  def initialize(*args, legacy_money: false, **kwargs, &block)
    super(*args, **kwargs, &block)
    @legacy_money = legacy_money
  end

  def visible?(context)
    super && (@legacy_money ? !!context[:requests_legacy_money] : !context[:requests_legacy_money])
  end
end

class Types::Invoice < Types::BaseObject
  # Add one definition for each possible return type
  # (one definition will be hidden at runtime)
  field :amount, Types::LegacyMoney, null: false, legacy_money: true
  field :amount, Types::Money, null: false, legacy_money: false
end
```

Input types (like input objects, scalars, and enums) work the same way with argument definitions.

## Schema Dumps

To dump a certain _version_ of the schema, provide the applicable `context: ...` to [Schema.to_definition](rdoc-ref:GraphQL::Schema.to_definition). For example:

```ruby
# Legacy money schema:
MySchema.to_definition(context: { requests_legacy_money: true })
```

or

```ruby
# Staff-only schema:
MySchema.to_definition(context: { current_user: OpenStruct.new(staff?: true) })
```

That way, the given `context` will be passed to `visible?(context)` calls and other relevant methods.

---

### Guides Schema Generators (`guides/schema/generators.md`)

# Generators

If you're using GraphQL with Ruby on Rails, you can use generators to:

- [setup GraphQL](#graphqlinstall), including [GraphiQL](https://github.com/graphql/graphiql), [GraphQL::Batch](https://github.com/Shopify/graphql-batch), and [Relay](https://facebook.github.io/relay/)
- [scaffold types](#scaffolding-types)
- [scaffold Relay mutations](#scaffolding-mutations)
- [scaffold ActiveRecord create/update/delete mutations](#scaffolding-activerecord-mutations)
- [scaffold GraphQL::Batch loaders](#scaffolding-loaders)

## graphql:install

You can add GraphQL to a Rails app with `graphql:install`:

```
rails generate graphql:install
```

This will:

- Set up a folder structure in `app/graphql/`
- Add schema definition
- Add base type classes
- Add a `Query` type definition
- Add a `Mutation` type definition with a base mutation class
- Add a route and controller for executing queries
- Install [`graphiql-rails`](https://github.com/rmosolgo/graphiql-rails)
- Enable [`ActiveRecord::QueryLogs`](https://api.rubyonrails.org/classes/ActiveRecord/QueryLogs.html) and add GraphQL-related metadata (using [GraphQL::Current](rdoc-ref:GraphQL::Current))

After installing you can see your new schema by:

- `bundle install`
- `rails server`
- Open `localhost:3000/graphiql`

### Options

- `--directory=DIRECTORY` will specify the directory where generated files should be saved (default is `app/graphql`)
- `--schema=MySchemaName` will be used for naming the schema (default is `#{app_name}Schema`)
- `--skip-graphiql` will exclude `graphiql-rails` from the setup
- `--skip-mutation-root-type` will not create of the mutation root type
- `--skip-query-logs` will skip the QueryLogs setup
- `--relay` will add [Relay](https://facebook.github.io/relay/)-specific code to your schema
- `--batch` will add [GraphQL::Batch](https://github.com/Shopify/graphql-batch) to your gemfile and include the setup in your schema
- `--playground` will include `graphql_playground-rails` in the setup (mounted at `/playground`)
- `--api` will create smaller stack for API only apps

## Scaffolding Types

Several generators will add GraphQL types to your project. Run them with `-h` to see the options:

- `rails g graphql:object`
- `rails g graphql:input`
- `rails g graphql:interface`
- `rails g graphql:union`
- `rails g graphql:enum`
- `rails g graphql:scalar`

### ActiveRecord columns auto-extraction

The `graphql:object` and `graphql:input` generators can detect the existence of an ActiveRecord class with the same name, and scaffold all database columns as fields/arguments using appropriate GraphQL types and nullability detection

### Options

- `--namespaced-types` will generate each one of the `object`/`input`/`interface`/... types under separate `Types::Objects::*`/`Types::Inputs::*`/`Types::Interfaces::*`/... namespaces and folders

## Scaffolding Mutations

You can prepare a Relay Classic mutation with

```
rails g graphql:mutation #{mutation_name}
```

## Scaffolding ActiveRecord Mutations

You can generate a Relay Classic create, update or delete mutation for a given model with

```
rails g graphql:mutation_create #{model_class_name}
rails g graphql:mutation_update #{model_class_name}
rails g graphql:mutation_delete #{model_class_name}
```

`model_class_name` accepts both `namespace/class_type` and `Namespace::ClassType` formats.
This mutation also accepts the `--namespaced-types` flag, to keep it consistent with the scaffolded Object and Input classes from the type generators

## Scaffolding Loaders

You can prepare a GraphQL::Batch loader with

```
rails g graphql:loader
```

---

### Guides Schema Introspection (`guides/schema/introspection.md`)

# Introspection

A GraphQL schema has a [built-in introspection system](https://graphql.org/learn/introspection/) that publishes the schema's structure. In fact, the introspection system can be queried using GraphQL, for example:

```graphql
{
  __schema {
    queryType {
      name
    }
  }
}
# Returns:
# {
#   "data": {
#     "__schema": {
#       "queryType": {
#         "name": "Query"
#       }
#     }
#   }
# }
```

This system is used for GraphQL tooling like the [GraphiQL editor](https://github.com/graphql/graphiql).

Here are the default parts of the introspection system:

- `__schema` is a root-level field that contains data about the schema: its entry points, types, and directives.
- `__type(name: String!)` is a root-level field that returns data about a type with the given `name`, if there is a type with that name.
- `__typename` works a bit differently: it can be added to _any_ selection, and it will return the type of object being queried.

Here are some `__typename` examples:

```graphql
{
  user(id: "1") {
    handle
    __typename
  }
}
# Returns:
# {
#   "data": {
#     "user": {
#       "handle": "rmosolgo",
#       "__typename": "User"
#     }
#   }
# }
```

For unions and interfaces, `__typename` returns the _object_ type for the current object, for example:

```graphql
{
  search(term: "puppies") {
    title
    __typename
  }
}
# Returns:
# {
#   "data": {
#     "search": [
#       {
#         "title": "Sound of Dogs Barking",
#         "__typename": "AudioClip",
#       },
#       {
#         "title": "Cute puppies playing with a stick",
#         "__typename": "VideoClip",
#       },
#       {
#         "title": "The names of my favorite pets",
#         "__typename": "TextSnippet"
#       },
#     ]
#   }
# }
```

## Customizing Introspection

You can use custom introspection types.

```ruby
# create a module namespace for your custom types:
module Introspection
  # described below ...
end

class MySchema < GraphQL::Schema
  # ...
  # then pass the module as `introspection`
  introspection Introspection
end
```

Keep in mind that off-the-shelf tooling may not support your custom introspection fields. You may have to modify existing tooling or create your own tools to make use of your extensions.

### Introspection Namespace

The introspection namespace may contain a few different customizations:

- Class-based [object definitions](/type_definitions/objects) which replace the built-in introspection types (such as `__Schema` and `__Type`)
- `EntryPoints`, A class-based [object definition](/type_definitions/objects) containing introspection entry points (like `__schema` and `__type(name:)`).
- `DynamicFields`, A class-based [object definition](/type_definitions/objects) containing dynamic, globally-available fields (like `__typename`.)

### Custom Introspection Types

The `module` passed as `introspection` may contain classes with the following names, which replace the built-in introspection types:

Custom class name | GraphQL type | Built-in class name
--|--|--
`SchemaType` | `__Schema` | [GraphQL::Introspection::SchemaType](rdoc-ref:GraphQL::Introspection::SchemaType)
`TypeType` | `__Type` | [GraphQL::Introspection::TypeType](rdoc-ref:GraphQL::Introspection::TypeType)
`DirectiveType` | `__Directive` | [GraphQL::Introspection::DirectiveType](rdoc-ref:GraphQL::Introspection::DirectiveType)
`DirectiveLocationType` | `__DirectiveLocation` | [GraphQL::Introspection::DirectiveLocationEnum](rdoc-ref:GraphQL::Introspection::DirectiveLocationEnum)
`EnumValueType` | `__EnumValue` | [GraphQL::Introspection::EnumValueType](rdoc-ref:GraphQL::Introspection::EnumValueType)
`FieldType` | `__Field` | [GraphQL::Introspection::FieldType](rdoc-ref:GraphQL::Introspection::FieldType)
`InputValueType` | `__InputValue` | [GraphQL::Introspection::InputValueType](rdoc-ref:GraphQL::Introspection::InputValueType)
`TypeKindType` | `__TypeKind` | [GraphQL::Introspection::TypeKindEnum](rdoc-ref:GraphQL::Introspection::TypeKindEnum)

The class-based definitions' names _must_ match the names of the types they replace.

#### Extending a Built-in Type

The built-in classes listed above may be extended:

```ruby
module Introspection
  class SchemaType < GraphQL::Introspection::SchemaType
    # ...
  end
end
```

Inside the class definition, you may:

- add new fields by calling `field(...)` and providing implementations
- redefine field structure by calling `field(...)`
- provide new field implementations by defining methods
- provide new descriptions by calling `description(...)`

### Introspection Entry Points

The GraphQL spec describes two entry points to the introspection system:

- `__schema` returns data about the schema (as type `__Schema`)
- `__type(name:)` returns data about a type, if one is found by name (as type `__Type`)

You can re-implement these fields or create new ones by creating a custom `EntryPoints` class in your introspection namespace:

```ruby
module Introspection
  class EntryPoints < GraphQL::Introspection::EntryPoints
    # ...
  end
end
```

This class is an object type definition, so you can override existing fields or add new ones here. They'll be available on the root `query` object, but ignored in introspection (just like `__schema` and `__type`).

### Dynamic Fields

The GraphQL spec describes a field which may be added to _any_ selection: `__typename`. It returns the name of the current GraphQL type.

You can add fields like this (or override `__typename`) by creating a custom `DynamicFields` definition:

```ruby
module Introspection
  class DynamicFields < GraphQL::Introspection::DynamicFields
    # ...
  end
end
```

Any fields defined there will be available in any selection, but ignored in introspection (just like `__typename`).

## Disabling Introspection

In case you want to turn off introspection entry points `__schema` and `__type` (for instance in the production environment) you can use a `#disable_introspection_entry_points` shorthand method:

```ruby
class MySchema < GraphQL::Schema
  disable_introspection_entry_points if Rails.env.production?
end
```

Where `disable_introspection_entry_points` will disable both the `__schema` and `__type` introspection entry points, you can also individually disable the introspection entry points using the `disable_schema_introspection_entry_point` and `disable_type_introspection_entry_point` shorthand methods:

```ruby
class MySchema < GraphQL::Schema
  disable_schema_introspection_entry_point
  disable_type_introspection_entry_point
end
```

---

### Guides Schema Lazy Execution (`guides/schema/lazy_execution.md`)

# Lazy Execution

With lazy execution, you can optimize access to external services (such as databases) by making batched calls. Building a lazy loader has three steps:

- Define a lazy-loading class with _one_ method for loading & returning a value
- Connect it to your schema with [GraphQL::Schema.lazy_resolve](rdoc-ref:GraphQL::Schema.lazy_resolve)
- In `resolve` methods, return instances of the lazy-loading class

## Example: Batched Find

Here's a way to find many objects by ID using one database call, preventing N+1 queries.

1. Lazy-loading class which finds models by ID.

```ruby
class LazyFindPerson
  def initialize(query_ctx, person_id)
    @person_id = person_id
    # Initialize the loading state for this query,
    # or get the previously-initiated state
    @lazy_state = query_ctx[:lazy_find_person] ||= {
      pending_ids: Set.new,
      loaded_ids: {},
    }
    # Register this ID to be loaded later:
    @lazy_state[:pending_ids] << person_id
  end

  # Return the loaded record, hitting the database if needed
  def person
    # Check if the record was already loaded:
    loaded_record = @lazy_state[:loaded_ids][@person_id]
    if loaded_record
      # The pending IDs were already loaded,
      # so return the result of that previous load
      loaded_record
    else
      # The record hasn't been loaded yet, so
      # hit the database with all pending IDs
      pending_ids = @lazy_state[:pending_ids].to_a
      people = Person.where(id: pending_ids)
      people.each { |person| @lazy_state[:loaded_ids][person.id] = person }
      @lazy_state[:pending_ids].clear
      # Now, get the matching person from the loaded result:
      @lazy_state[:loaded_ids][@person_id]
    end
  end
```

2. Connect the lazy resolve method

```ruby
class MySchema < GraphQL::Schema
  # ...
  lazy_resolve(LazyFindPerson, :person)
end
```

3. Return lazy objects from `resolve`

```ruby
field :author, PersonType

def author
  LazyFindPerson.new(context, object.author_id)
end
```

Now, calls to `author` will use batched database access. For example, this query:

```graphql
{
  p1: post(id: 1) { author { name } }
  p2: post(id: 2) { author { name } }
  p3: post(id: 3) { author { name } }
}
```

Will only make one query to load the `author` values.

## Gems for batching

The example above is simple and has some shortcomings. Consider the following gems for a robust solution to batched resolution:

* [GraphQL::Dataloader](rdoc-ref:GraphQL::Dataloader) is a built-in, Fiber-based approach to batching. See the [Dataloader guide](/dataloader/overview) for more information.
* [`graphql-batch`](https://github.com/shopify/graphql-batch) provides a powerful, flexible toolkit for lazy resolution with GraphQL.
* [`dataloader`](https://github.com/sheerun/dataloader) is more general promise-based utility for batching queries within the same thread.
* [`batch-loader`](https://github.com/exAspArk/batch-loader) works with any Ruby code including GraphQL, no extra dependencies or primitives.

---

### Guides Schema Object Identification (`guides/schema/object_identification.md`)

# Object Identification

GraphQL-Ruby ships with some helpers to implement [Relay-style object identification](https://relay.dev/graphql/objectidentification.htm).

## Schema methods

See [the Schema definition guide](/schema/definition#object-identification) for required top-level hooks.

## Node interface

One requirement for Relay's object management is implementing the `"Node"` interface.

To implement the node interface, add [GraphQL::Types::Relay::Node](rdoc-ref:GraphQL::Types::Relay::Node) to your definition:

```ruby
class Types::PostType < GraphQL::Schema::Object
  # Implement the "Node" interface for Relay
  implements GraphQL::Types::Relay::Node
  # ...
end
```

To tell GraphQL how to resolve members of the `Node` interface, you must also define `Schema.resolve_type`:

```ruby
class MySchema < GraphQL::Schema
  # You'll also need to define `resolve_type` for
  # telling the schema what type Relay `Node` objects are
  def self.resolve_type(type, obj, ctx)
    case obj
    when Post
      Types::PostType
    when Comment
      Types::CommentType
    else
      raise("Unexpected object: #{obj}")
    end
  end
end
```

## UUID fields

Nodes must have a field named `"id"` which returns a globally unique ID.

To add a UUID field named `"id"`, implement the [GraphQL::Types::Relay::Node](rdoc-ref:GraphQL::Types::Relay::Node) interface::

```ruby
class Types::PostType < GraphQL::Schema::Object
  implements GraphQL::Types::Relay::Node
end
```

This field will call the previously-defined `id_from_object` class method.

## `node` field (find-by-UUID)

You should also provide a root-level `node` field so that Relay can refetch objects from your schema. You can attach it like this:

```ruby
class Types::QueryType < GraphQL::Schema::Object
  # Used by Relay to lookup objects by UUID:
  # Add `node(id: ID!)
  include GraphQL::Types::Relay::HasNodeField
  # ...
end
```

## `nodes` field

You can also provide a root-level `nodes` field so that Relay can refetch objects by IDs:

```ruby
class Types::QueryType < GraphQL::Schema::Object
  # Fetches a list of objects given a list of IDs
  # Add `nodes(ids: [ID!]!)`
  include GraphQL::Types::Relay::HasNodesField
  # ...
end
```

---

### Guides Schema Root Types (`guides/schema/root_types.md`)

# Root Types

GraphQL queries begin from [root types](https://graphql.org/learn/schema/#the-query-mutation-and-subscription-types): `query`, `mutation`, and `subscription`.

Attach these to your schema using methods with the same name:

```ruby
class MySchema < GraphQL::Schema
  # required
  query Types::QueryType
  # optional
  mutation Types::MutationType
  subscription Types::SubscriptionType
end
```

The types are `GraphQL::Schema::Object` classes, for example:

```ruby
# app/graphql/types/query_type.rb
class Types::QueryType < GraphQL::Schema::Object
  field :posts, [PostType], 'Returns all blog posts', null: false
end

# Similarly:
class Types::MutationType < GraphQL::Schema::Object
  field :create_post, mutation: Mutations::AddPost
end
# and
class Types::SubscriptionType < GraphQL::Schema::Object
  field :comment_added, subscription: Subscriptions::CommentAdded
end
```

Each type is the entry point for the corresponding GraphQL query:

```ruby
query Posts {
  # `Query.posts`
  posts { ... }
}

mutation AddPost($postAttrs: PostInput!){
  # `Mutation.createPost`
  createPost(attrs: $postAttrs)
}

subscription CommentAdded {
  # `Subscription.commentAdded`
  commentAdded(postId: 1)
}
```

---

### Guides Schema Sdl (`guides/schema/sdl.md`)

# Parsing GraphQL Schema Definition Language into a Ruby Schema

GraphQL-Ruby includes a way to build a runable schema from the GraphQL Schema Definition Language (SDL). [GraphQL::Schema.from_definition](rdoc-ref:GraphQL::Schema.from_definition) returns a schema class based on a filename or string containing GraphQL SDL. For example:

```ruby
# From a file:
MySchema = GraphQL::Schema.from_definition("path/to/schema.graphql")
# Or, from a string:
MySchema = GraphQL::Schema.from_definition(<<~GRAPHQL)
  type Query {
    # ...
  }
  # ...
GRAPHQL
```

Definitions from the SDL are converted into Ruby classes, similar to those defined in plain Ruby code.

## Execution

You can provide execution behaviors to a generated schema as `default_resolve:`, which accepts two kinds of values:

- __Implementation Object__: an object that implements several methods used at runtime
- __Implementation Hash__: keys and nested hashes provide procs for execution

### Implementation Object

By providing an object that implements several runtime methods, you can define the execution behaviors of a schema loaded from SDL:

```ruby
class SchemaImplementation
  # see below for methods
end

# Pass the object as `default_resolve:`
MySchema = GraphQL::Schema.from_definition(
  "path/to/schema.graphql",
  default_resolve: SchemaImplementation.new
)
```

The `default_resolve:` object may implement:

- `#call(type, field, obj, args, ctx)` for resolving fields
- `#resolve_type(abstract_type, obj, ctx)` for resolving `obj` as one of `abstract_type`'s possible object types
- `#coerce_input(type, value, ctx)` for coercing scalar input
- `#coerce_result(type, value, ctx)` for coercing scalar return values

### Implementation Hash

Alternatively, you can provide a Hash containing callable behaviors, for example:

```ruby
schema_implementation = {
  # ... see below for hash structure
}

# Pass the hash as `default_resolve:`
MySchema = GraphQL::Schema.from_definition(
  "path/to/schema.graphql",
  default_resolve: schema_implementation
)
```

The hash may contain:

- A key for each object type name, containing a sub-hash of `{ field_name => ->(obj, args, ctx) { ... } }` for resolving object fields
- A key for each scalar type name, containing a sub-hash with keys `"coerce_result"` and `"coerce_input"`, each pointing to a `->(value, ctx) { ... }` for handling scalar values at runtime
- A `"resolve_type"` key pointing to a `->(abstract_type, object, ctx) { ... }` callable, used for resolving `object` to one of `abstract_type`'s possible types

## Plugins

[GraphQL::Schema.from_definition](rdoc-ref:GraphQL::Schema.from_definition) accepts a `using:` argument, which may be given as a map of `plugin => args` pairs. For example:

```ruby
MySchema = GraphQL::Schema.from_definition("path/to/schema.graphql", using: {
  GraphQL::Pro::PusherSubscriptions => { redis: $redis },
  GraphQL::Pro::OperationStore => nil, # no options here
})
```

## Directives

Although GraphQL-Ruby doesn't have special handling for directives in the SDL, you can build custom behavior in your own app. If part of the schema had a directive, you can access it using `.ast_node.directives`. For example:

```ruby
schema = GraphQL::Schema.from_definition <<-GRAPHQL
type Query @flagged {
  secret: Boolean @privacy(secret: true)
}
GRAPHQL

pp schema.query.ast_node.directives.map(&:to_query_string)
# => ["@flagged"]
pp schema.get_field("Query", "secret").ast_node.directives.map(&:to_query_string)
# => ["@privacy(secret: true)"]
```

See [GraphQL::Language::Nodes::Directive](rdoc-ref:GraphQL::Language::Nodes::Directive) for available methods.

---

### Guides Subscriptions Ably Implementation (`guides/subscriptions/ably_implementation.md`)

# Ably Implementation

[GraphQL Pro](https://graphql.pro) includes a subscription system based on [Redis](https://redis.io) and [Ably](https://ably.io) which works with any Ruby web framework.

After creating an app on Ably, you can hook it up to your GraphQL schema.

## How it Works

This subscription implementation uses a hybrid approach:

- __Your app__ takes GraphQL queries an runs them
- __Redis__ stores subscription data for later updates
- __Ably__ sends updates to subscribed clients

So, the lifecycle goes like this:

- A `subscription` query is sent by HTTP Post to your server (just like a `query` or `mutation`)
- The response contains an Ably channel ID (as an HTTP header) which the client may subscribe to
- The client opens that Ably channel
- When the server triggers updates, they're delivered over the Ably channel
- When the client unsubscribes, the server receives a webhook and responds by removing its subscription data

Here's another look:

```
1. Subscription is created in your app

          HTTP POST
        .---------->   write to Redis
      📱            ⚙️ -----> 💾
        <---------'
        X-Subscription-ID: 1234


2. Client opens a connection to Ably

          websocket
      📱 <---------> ☁️


3. The app sends updates via Ably

      ⚙️ ---------> ☁️ ------> 📱
        POST           update
      (via gem)   (via websocket)


4. When the client unsubscribes, Ably notifies the app

          webhook
      ⚙️ <-------- ☁️  (disconnect) 📱
```


By using this configuration, you can use GraphQL subscriptions without hosting a push server yourself!

## Ably setup
Add `ably-rest` to your `Gemfile`:

```ruby
gem 'ably-rest'
```

and `bundle install`.

## Database setup

Subscriptions require a _persistent_ Redis database, configured with:

```sh
maxmemory-policy noeviction
# optional, more durable persistence:
appendonly yes
```

Otherwise, Redis will drop data that doesn't fit in memory (read more in ["Redis persistence"](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/)).

If you're already using Redis in your application, see ["Storing Data in Redis"](https://www.mikeperham.com/2015/09/24/storing-data-with-redis/) for options to isolate data and tune your configuration.

## Schema configuration

Add `redis` to your `Gemfile`:

```ruby
gem 'redis'
```

and `bundle install`. Then create a Redis instance:

```ruby
# for example, in an initializer:
$graphql_subscriptions_redis = Redis.new # default connection
```

Then, that Redis client is passed to the Subscription configuration:

```ruby
class MySchema < GraphQL::Schema
  use GraphQL::Pro::AblySubscriptions,
    redis: $graphql_subscriptions_redis,
    ably: Ably::Rest.new(key: ABLY_API_KEY)
end
```

That connection will be used for managing subscription state. All writes to Redis are prefixed with `graphql:sub:`.

There are also two configurations for managing persistence:

- `stale_ttl_s:` expires subscription data after the given number of seconds without any update. After `stale_ttl_s` has passed, the data will expire from Redis. Each time a subscription receives an update, its TTL is refreshed. (Generally, this isn't required because the backend is built to clean itself up. But, if you find that Redis is collecting stale queries, you can set them to expire after some very long time as a safeguard.)
- `cleanup_delay_s:` (default: `5`) prevents deleting a subscription during those first seconds after it's created. Usually, a longer delay isn't necessary, but if you observe latency between the subscription's initial response and the client's subscription to the delivery channel, you can set this configuration to account for it.

### Connection Pool

For better performance reading and writing to Redis, you can pass a `connection_pool:` instead of `redis:`, using the [`connection_pool` gem](https://github.com/mperham/connection_pool):

```ruby
  use GraphQL::Pro::AblySubscriptions,
    connection_pool: ConnectionPool.new(size: 5, timeout: 5) { Redis.new },
    ably: Ably::Rest.new(key: ABLY_API_KEY)
```

### Broadcasts

If you set up [Broadcasts](/subscriptions/broadcast), then you can update many clients over a single Ably channel.

Broadcast channels have stable, predictable IDs. To prevent unauthorized clients from "listening in," use [token authorization](#authorization) for transport. Broadcasts channels use the namespace `gqlbdcst:`, so you can provide capabilities to receive them using `"gqlbdcst:*" => [ ... ]` in your authorization code. (If you're using [encryption](#encryption), the prefix will be `ablyencr-gqlbdcst:` instead.)

## Execution configuration

During execution, GraphQL will assign a `subscription_id` to the `context` hash. The client will use that ID to listen for updates, so you must return the `subscription_id` in the response headers.

Return `result.context[:subscription_id]` as the `X-Subscription-ID` header. For example:

```ruby
result = MySchema.execute(...)
# For subscriptions, return the subscription_id as a header
if result.subscription?
  response.headers["X-Subscription-ID"] = result.context[:subscription_id]
end
render json: result
```

This way, the client can use that ID as a Ably channel.

For __CORS requests__, you need a special header so that clients can read the custom header:

```ruby
if result.subscription?
  response.headers["X-Subscription-ID"] = result.context[:subscription_id]
  # Required for CORS requests:
  response.headers["Access-Control-Expose-Headers"] = "X-Subscription-ID"
end
```

Read more here: ["Using CORS"](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS).

## Webhook configuration

Your server needs to receive webhooks from Ably when clients disconnect. This keeps your local subscription database in sync with Ably.

### Server

*Note: if you're setting up in a development environment you should follow the [Developing with webhooks](#developing-with-webhooks) section first*

Mount the Rack app for handling webhooks from Ably. For example, on Rails:

```ruby
# config/routes.rb

# Include GraphQL::Pro's routing extensions:
using GraphQL::Pro::Routes

Rails.application.routes.draw do
  # ...
  # Handle webhooks for subscriptions:
  mount MySchema.ably_webhooks_client, at: "/ably_webhooks"
end
```

__Alternatively__, you can configure the routes to load your schema lazily, during the first request:

```ruby
# Provide the fully-qualified class name of your schema:
lazy_routes = GraphQL::Pro::Routes::Lazy.new("MySchema")
mount lazy_routes.ably_webhooks_client, at: "/ably_webhooks"
```

### Ably

1. Go to the Ably dashboard
2. Click on your application
3. Select the **"Integrations"** tab
4. Click on the **"+ New Integration Rule"** button
5. Click on the "Choose" button for **"Webhook"**
6. Click on the "Choose" button for **"Webhook"** (again)
7. Enter **your URL (including the webhooks path from above)** in the URL field.
8. Select **"Batch request"** for "Request Mode"
9. Under "Source", select **"Presence"**
10. Under "Sign with key", select the API Key prefix that matches the prefix of the `ABLY_API_KEY` you provided
11. Click **"Create"**

## Authorization

You can use Ably's [token authentication](https://www.ably.io/documentation/realtime/authentication#token-authentication) by implementing an endpoint in your app, for example:

```ruby
class AblyController < ActionController::Base
  def auth
    render status: 201, json: ably_rest_client.auth.create_token_request(
      capability: { '*' => ['presence', 'subscribe'] },
      client_id: 'graphql-subscriber',
    )
  end
end
```

[Ably's tutorial](https://www.ably.io/tutorials/webhook-chuck-norris#tutorial-step-4) also demonstrates some of the setup for this.

## Encryption

You can use Ably's [end-to-end encryption](https://www.ably.io/documentation/realtime/encryption) with GraphQL subscriptions. To enable it, add `cipher_base:` to your setup:

```ruby
  use GraphQL::Pro::AblySubscriptions,
    redis: $graphql_subscriptions_redis,
    ably: Ably::Rest.new(key: ABLY_API_KEY),
    # Add `cipher_base:` to enable end-to-end encryption
    cipher_base: "ff16381ae2f2b6c6de6ff696226009f3"
```

(Any random string will do, eg `ruby -e "require 'securerandom'; puts SecureRandom.hex"`.)

Also, return a header to client so that it can decrypt subscription updates. The key is put in `context[:ably_cipher_base64]`, and `graphql-ruby-client` expects to find it in the `X-Subscription-Key` header:

```ruby
result = MySchema.execute(...)
# For subscriptions, return the subscription_id as a header
if result.subscription?
  response.headers["X-Subscription-ID"] = result.context[:subscription_id]
  # Also return the encryption key so that clients
  # can decode subscription updates
  response.headers["X-Subscription-Key"] = result.context[:ably_cipher_base64]
end
```

(Also, if you're using CORS requests, update `Access-Control-Expose-Headers` to include `X-Subscription-Key`)

With this setup,

- `GraphQL::Pro::AblySubscriptions` will generate per-subscription keys (using `cipher_base` and the subscription ID) and use them to encrypt Ably payloads
- Those keys will be returned to clients in `X-Subscription-Key`
- Clients will use those keys to decrypt incoming messages

__Backwards compatibility:__ `GraphQL::Pro::AblySubscriptions` will only encrypt payloads whose `query.context[:ably_cipher_base64]` is present. Any subscriptions created _before_ `cipher_base:` was added to the setup will _not_ be encrypted. (There was no key to encrypt them, and clients don't have a key to _decrypt_ them!)

## Serializing Context

Since subscription state is stored in the database, then reloaded for pushing updates, you have to serialize and reload your query `context`.

By default, this is done with `GraphQL::Subscriptions::Serialize`'s `dump` and `load` methods, but you can provide custom implementations as well. To customize the serialization logic, create a subclass of `GraphQL::Pro::AblySubscriptions` and override `#dump_context(ctx)` and `#load_context(ctx_string)`:

```ruby
class CustomSubscriptions < GraphQL::Pro::AblySubscriptions
  def dump_context(ctx)
    context_hash = ctx.to_h
    # somehow convert this hash to a string, return the string
  end

  def load_context(ctx_string)
    # Given the string from the DB, create a new hash
    # to use as `context:`
  end
end
```

Then, use your _custom_ subscriptions class instead of the built-in one for your schema:

```ruby
class MySchema < GraphQL::Schema
  # Use custom subscriptions instead of GraphQL::Pro::AblySubscriptions
  # to get custom serialization logic
  use CustomSubscriptions, ...
end
```

That gives you fine-grained control of context reloading.

## Dashboard

You can monitor subscription state in the [GraphQL-Pro Dashboard](/pro/dashboard):





## Development Tips

#### Clear subscription data

At any time, you can reset your subscription database with the __"Reset"__ button in the [GraphQL-Pro Dashboard](/pro/dashboard), or in Ruby:

```ruby
# Wipe all subscription data from the DB:
MySchema.subscriptions.clear
```

#### Developing with webhooks

To receive webhooks in development, you can [use ngrok](https://www.ably.io/tutorials/webhook-chuck-norris). It gives you a public URL which you can setup with Ably, then any hooks delivered to that URL will be forwarded to your development environment.

## Client configuration

Install the [Ably JS client](https://github.com/ably/ably-js) then see docs for:

- [Apollo Client](/javascript_client/apollo_subscriptions)
- [Relay Modern](/javascript_client/relay_subscriptions).
- [GraphiQL](/javascript_client/graphiql_subscriptions)

---

### Guides Subscriptions Action Cable Implementation (`guides/subscriptions/action_cable_implementation.md`)

# Action Cable Implementation

[ActionCable](https://guides.rubyonrails.org/action_cable_overview.html) is a great platform for delivering GraphQL subscriptions on Rails 5+. It handles message passing (via `broadcast`) and transport (via `transmit` over a websocket).

To get started, see examples in the API docs: [GraphQL::Subscriptions::ActionCableSubscriptions](rdoc-ref:GraphQL::Subscriptions::ActionCableSubscriptions). GraphQL-Ruby also includes a mock ActionCable implementation for testing: [GraphQL::Testing::MockActionCable](rdoc-ref:GraphQL::Testing::MockActionCable).

See client usage for:

- [Apollo Client](/javascript_client/apollo_subscriptions)
- [Relay Modern](/javascript_client/relay_subscriptions).
- [GraphiQL](/javascript_client/graphiql_subscriptions)

---

### Guides Subscriptions Broadcast (`guides/subscriptions/broadcast.md`)

# Broadcasts

GraphQL subscription updates may _broadcast_ data to multiple subscribers.

A broadcast is a subscription update which is executed _once_, then delivered to _any number_ of subscribers. This reduces the time your server spends running GraphQL queries, since it doesn't have to re-run the query for every subscriber.

But, __take care__: this approach risks leaking information to subscribers who shouldn't receive it.

## Setup

To enable broadcasts, add `broadcast: true` to your subscription setup:

```ruby
class MyAppSchema < GraphQL::Schema
  # ...
  use SomeSubscriptionImplementation,
    broadcast: true # <----
end
```

Then, any broadcastable field can be configured with `broadcastable: true`:

```ruby
field :name, String, null: false,
  broadcastable: true
```

When a subscription comes in where _all_ of its fields are `broadcastable: true`, then it will be handled as a broadcast.

Additionally, you can set `default_broadcastable: true`:

```ruby
class MyAppSchema < GraphQL::Schema
  # ...
  use SomeSubscriptionImplementation,
    broadcast: true,
    default_broadcastable: true # <----
end
```

With this setting, fields are broadcastable by default. Only a field with `broadcastable: false` in its configuration will cause a subscription to be handled on a subscriber-by-subscriber basis.

## What fields are broadcastable?

GraphQL-Ruby can't infer whether a field is broadcastable or not. You must configure it explicitly with `broadcastable: true` or `broadcastable: false`. (The subscription plugin also accepts `default_broadcastable: true|false`.)

A field is broadcastable if _all clients who request the field will see the same value_. For example:

- General facts: celebrity names, laws of physics, historical dates
- Public information: object names, document updated-at timestamps, boilerplate info

For fields like this, you can add `broadcastable: true`.

A field is __not broadcastable__ if its value is different for different clients. For example:

- __Viewer-specific information:__ if a field is specifically viewer-based, then it can't be broadcasted to other viewers. For example, `discussion { viewerCanModerate }` might be true for a moderator, but it shouldn't be broadcasted to other viewers.
- __Context-specific information:__ if a field's value takes the request context into consideration, it shouldn't be broadcasted. For example, IP addresses or HTTP header values probably can't be broadcasted. If a field reflects the viewer's timezone, it can't be broadcasted.
- __Restricted information:__ if some viewers see one value, while other viewers see a different value, then it's not broadcastable. Broadcasting this data might leak private information to unauthorized clients. (This includes filtered lists: if the filtering is viewer-by-viewer, it's not broadcastable.)
- __Fields with side effects:__ if the system requires a side effect (eg, logging a metric, updating a database, incrementing a counter) whenever a resolver is executed, it's not a good candidate for broadcasting because some executions will be optimized away.

These fields can be tagged with `broadcastable: false` so that GraphQL-Ruby will handle them on a subscriber-by-subscriber basis.

If you want to use subscriptions but have a lot of non-broadcastable fields in your schema, consider building a new set of subscription fields with limited access to other schema objects. Instead, optimize those subscriptions for broacastability.

## Under the Hood

GraphQL-Ruby determines which subscribers can receive a broadcast by inspecting:

- __Query string__. Only exactly-matching query strings will receive the same broadcast.
- __Variables__. Only exactly-matching variable values will receive the same broadcast.
- __Field and Arguments__ given to `.trigger`. They must match the ones initially sent when subscribing. (Subscriptions always worked this way.)
- __Subscription scope__. Only clients with exactly-matching subscription scope can receive the same broadcasts.

So, take care to [set subscription_scope](/subscriptions/subscription_classes#scope) whenever a subscription should be implicitly scoped!

(See [GraphQL::Subscriptions::Event#fingerprint](rdoc-ref:GraphQL::Subscriptions::Event#fingerprint) for the implementation of broadcast fingerprints.)

## Checking for Broadcastable

For testing purposes, you can confirm that a GraphQL query string is broadcastable by using [Subscriptions#broadcastable?](rdoc-ref:GraphQL::Subscriptions#broadcastable?):

```ruby
subscription_string = "subscription { ... }"
MySchema.subscriptions.broadcastable?(subscription_string)
# => true or false
```

Use this in your application's tests to make sure that broadcastable fields aren't accidentally made non-broadcastable.

## Connections and Edges

You can configure your generated `Connection` and `Edge` types to be broadcastable by setting `default_broadcastable(true)` in their definition:

```ruby
# app/types/base_connection.rb
class Types::BaseConnection < Types::BaseObject
  include GraphQL::Types::Relay::ConnectionBehaviors
  default_broadcastable(true)
end

# app/types/base_edge.rb
class Types::BaseEdge < Types::BaseObject
  include GraphQL::Types::Relay::EdgeBehaviors
  default_broadcastable(true)
end
```

(In your `BaseObject`, you should also have `connection_type_class(Types::BaseConnection)` and `edge_type_class(Types::BaseEdge)`.)

`PageInfo` is broadcastable by default.

---

### Guides Subscriptions Implementation (`guides/subscriptions/implementation.md`)

# Implementation

The [GraphQL::Subscriptions](rdoc-ref:GraphQL::Subscriptions) plugin is a base class for implementing subscriptions.

Each method corresponds to a step in the subscription lifecycle. See the API docs for method-by-method documentation: [GraphQL::Subscriptions](rdoc-ref:GraphQL::Subscriptions).

Also, see the [Pusher implementation guide](/subscriptions/pusher_implementation), the [Ably implementation guide](/subscriptions/ably_implementation), the [ActionCable implementation guide](/subscriptions/action_cable_implementation) or [GraphQL::Subscriptions::ActionCableSubscriptions](rdoc-ref:GraphQL::Subscriptions::ActionCableSubscriptions) docs for an example implementation.

## Considerations

Every Ruby application is different, so consider these points when implementing subscriptions:

- Is your application single-process or multiprocess? Single-process applications can store state in memory while multiprocess applications need a message broker to keep all processes up-to-date.
- What components of your application can be used for persistence and message passing?
- How will you deliver push updates to subscribed clients? (For example, websockets, ActionCable, Pusher, webhooks, or something else?)
- How will you handle [thundering herd](https://en.wikipedia.org/wiki/Thundering_herd_problem)s? When an event is triggered, how will you manage database access to update clients without swamping your system?

## Broadcasts

_Broadcasting_ updates to multiple subscribers is supported by GraphQL-Ruby, but requires implementation-specific work, see more in the [Broadcast guide](/subscriptions/broadcast).

---

### Guides Subscriptions Multi Tenant (`guides/subscriptions/multi_tenant.md`)

# Multi-Tenant

In a multi-tenant system, data from many different accounts is stored on the same server. (An account might be an organization, a customer, a namespace, a domain, etc -- these are all _tenants_.) Gems like [Apartment](https://github.com/influitive/apartment) assist with this arrangement, but it can also be implemented in the application. Here are a few considerations for this architecture when using GraphQL subscriptions.

## Add Tenant to `context`

All the approaches below will use `context[:tenant]` to identify the tenant during GraphQL execution, so make sure to assign it before executing a query:

```ruby
context = {
  viewer: current_user,
  tenant: current_user.tenant,
  # ...
}

MySchema.execute(query_str, context: context, ...)
```

## Tenant-based `subscription_scope`

When subscriptions are delivered, [`subscription_scope`](/subscriptions/subscription_classes#scope) is one element used to route data to the right subscriber. In short, it's the _implicit_ identifier for the receiver. In a multi-tenant architecture, `subscription_scope` should reference the context key that names the tenant, for example:

```ruby
class BudgetWasApproved < GraphQL::Schema::Subscription
  subscription_scope :tenant # This would work with `context[:tenant] => "acme-corp"`
  # ...
end

# Include the scope when `.trigger`ing:
BudgetSchema.subscriptions.trigger(:budget_was_approved, {}, { ... }, scope: "acme-corp")
```


Alternatively, `subscription_scope` might name something that _belongs_ to the tenant:

```ruby
class BudgetWasApproved < GraphQL::Schema::Subscription
  subscription_scope :project_id # This would work with `context[:project_id] = 1234`
end

# Include the scope when `.trigger`ing:
BudgetSchema.subscriptions.trigger(:budget_was_approved, {}, { ... }, scope: 1234)
```

As long as `project_id` is unique among _all_ tenants, that would work fine too. But _some_ scope is required so that subscriptions can be disambiguated between tenants.

## Choosing a tenant for execution

There are a few places where subscriptions might need to load data:

- When building the payload for the subscription (fetching data to prepare the result)
- `ActionCableSubscriptions`: when deserializing the JSON string broadcasted by `ActionCable`
- `PusherSubscriptions` and `AblySubscriptions`: when deserializing query context

Each of these operations will need to select the right tenant in order to load data properly.

For __building the payload__, use a [Trace module](/queries/tracing):

```ruby
module TenantSelectionTrace
  def execute_multiplex(multiplex:) # this is the top-level, umbrella event
    context = data[:multiplex].queries.first.context # This assumes that all queries in a multiplex have the same tenant
    MultiTenancy.select_tenant(context[:tenant]) do
      # ^^ your multi-tenancy implementation here
      super # Call through to the rest of execution
    end
  end
end

# ...
class MySchema < GraphQL::Schema
  trace_with(TenantSelectionTrace)
end
```

The tracer above will use `context[:tenant]` to select a tenant for the duration of execution for _all_ queries, mutations, and subscriptions.

For __deserializing ActionCable messages__, provide a `serializer:` object that implements `.dump(obj)` and `.load(string, context)`:

```ruby
class MultiTenantSerializer
  def self.dump(obj)
    GraphQL::Subscriptions::Serialize.dump(obj)
  end

  def self.load(string, context)
    MultiTenancy.select_tenant(context[:tenant]) do
      GraphQL::Subscriptions::Serialize.load(string)
    end
  end
end

# ...
class MySchema < GraphQL::Schema
  # ...
  use GraphQL::Subscriptions::ActionCableSubscriptions, serializer: MultiTenantSerializer
end
```

The implementation above will use the built-in serialization algorithms, but it will do so _in the context of_ the selected tenant.

For __loading query context in Pusher and Ably__, add tenant selection to your `load_context` method, if required:

```ruby
class CustomSubscriptions < GraphQL::Pro::PusherSubscriptions # or `GraphQL::Pro::AblySubscriptions`
  def dump_context(ctx)
    JSON.dump(ctx.to_h)
  end

  def load_context(ctx_string)
    ctx_data = JSON.parse(ctx_string)
    MultiTenancy.select_tenant(ctx_data["tenant"]) do
      # Build a symbol-keyed hash, loading objects from the database if necessary
      # to use a `context: ...`
    end
  end
end
```

With that approach, the selected tenant will be active when building the context hash, in case any objects need to be loaded from the database.

---

### Guides Subscriptions Pusher Implementation (`guides/subscriptions/pusher_implementation.md`)

# Pusher Implementation

[GraphQL Pro](https://graphql.pro) includes a subscription system based on [Redis](https://redis.io) and [Pusher](https://pusher.com) which works with any Ruby web framework.

After creating an app on Pusher and [configuring the Ruby gem](https://github.com/pusher/pusher-http-ruby#global), you can hook it up to your GraphQL schema.

## How it Works

This subscription implementation uses a hybrid approach:

- __Your app__ takes GraphQL queries an runs them
- __Redis__ stores subscription data for later updates
- __Pusher__ sends updates to subscribed clients

So, the lifecycle goes like this:

- A `subscription` query is sent by HTTP Post to your server (just like a `query` or `mutation`)
- The response contains a Pusher channel ID (as an HTTP header) which the client may subscribe to
- The client opens that Pusher channel
- When the server triggers updates, they're delivered over the Pusher channel
- When the client unsubscribes, the server receives a webhook and responds by removing its subscription data

Here's another look:

```
1. Subscription is created in your app

          HTTP POST
        .---------->   write to Redis
      📱            ⚙️ -----> 💾
        <---------'
        X-Subscription-ID: 1234


2. Client opens a connection to Pusher

          websocket
      📱 <---------> ☁️


3. The app sends updates via Pusher

      ⚙️ ---------> ☁️ ------> 📱
        POST           update
      (via gem)   (via websocket)


4. When the client unsubscribes, Pusher notifies the app

          webhook
      ⚙️ <-------- ☁️  (disconnect) 📱
```


By using this configuration, you can use GraphQL subscriptions without hosting a push server yourself!

## Database setup

Subscriptions require a _persistent_ Redis database, configured with:

```sh
maxmemory-policy noeviction
# optional, more durable persistence:
appendonly yes
```

Otherwise, Redis will drop data that doesn't fit in memory (read more in ["Redis persistence"](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/)).

If you're already using Redis in your application, see ["Storing Data in Redis"](https://www.mikeperham.com/2015/09/24/storing-data-with-redis/) for options to isolate data and tune your configuration.

## Schema configuration

Add `redis` to your `Gemfile`:

```ruby
gem 'redis'
```

and `bundle install`. Then create a Redis instance:

```ruby
# for example, in an initializer:
$graphql_subscriptions_redis = Redis.new # default connection
```

Then, that Redis client is passed to the Subscription configuration:

```ruby
class MySchema < GraphQL::Schema
  use GraphQL::Pro::PusherSubscriptions, redis: $graphql_subscriptions_redis
end
```

That connection will be used for managing subscription state. All writes to Redis are prefixed with `graphql:sub:`.

There are two configurations for managing persistence:

- `stale_ttl_s:` expires subscription data after the given number of seconds without any update. After `stale_ttl_s` has passed, the data will expire from Redis. Each time a subscription receives an update, its TTL is refreshed. (Generally, this isn't required because the backend is built to clean itself up. But, if you find that Redis is collecting stale queries, you can set them to expire after some very long time as a safeguard.)
- `cleanup_delay_s:` (default: `5`) prevents deleting a subscription during those first seconds after it's created. Usually, a longer delay isn't necessary, but if you observe latency between the subscription's initial response and the client's subscription to the delivery channel, you can set this configuration to account for it.

Also, you can use `extra_webhook_tokens: [{ key: "...", secret: "..." }, ...]` when you need to roll Pusher keys. [Pusher uses the oldest active token](https://pusher.com/docs/channels/server_api/webhooks/#authentication), so you can pass the old credentials there while you're rolling over to the new one.

### Connection Pool

For better performance reading and writing to Redis, you can pass a `connection_pool:` instead of `redis:`, using the [`connection_pool` gem](https://github.com/mperham/connection_pool):

```ruby
  use GraphQL::Pro::PusherSubscriptions,
    connection_pool: ConnectionPool.new(size: 5, timeout: 5) { Redis.new },
```

### Broadcasts

If you set up [Broadcasts](/subscriptions/broadcast), then you can update many clients over a single Pusher channel.

Broadcast channels have stable, predictable IDs. To prevent unauthorized clients from "listening in," use an [authorized Pusher channel](#authorization) for transport. In your authorization code, you can check for a broadcast using `.broadcast_subscription_id?`:

```ruby
# In your Pusher authorization endpoint:
channel_name = params[:channel_name]
MySchema.subscriptions.broadcast_subscription_id?(channel_name)
# => true | false
```

## Execution configuration

During execution, GraphQL will assign a `subscription_id` to the `context` hash. The client will use that ID to listen for updates, so you must return the `subscription_id` in the response headers.

Return `result.context[:subscription_id]` as the `X-Subscription-ID` header. For example:

```ruby
result = MySchema.execute(...)
# For subscriptions, return the subscription_id as a header
if result.subscription?
  response.headers["X-Subscription-ID"] = result.context[:subscription_id]
end
render json: result
```

This way, the client can use that ID as a Pusher channel.

For __CORS requests__, you need a special header so that clients can read the custom header:

```ruby
if result.subscription?
  response.headers["X-Subscription-ID"] = result.context[:subscription_id]
  # Required for CORS requests:
  response.headers["Access-Control-Expose-Headers"] = "X-Subscription-ID"
end
```

Read more here: ["Using CORS"](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS).

### Payload Compression

To mitigate problems with [Pusher's 10kb message limit](https://support.pusher.com/hc/en-us/articles/4412243423761-What-Is-The-Message-Size-Limit-When-Publishing-an-Event-in-Channels-), you can specify `compress_pusher_payload: true` in the `context` of your subscription. For example:

```ruby
# app/controllers/graphql_controller.rb
def execute
  # ...
  # Somehow detect whether the client supports compressed payloads,
  # for example, User-Agent, query param, or request header:
  if client_supports_compressed_payloads?
    context[:compress_pusher_payload] = true
  end
  # ...
end
```

This will cause subscription payloads to include `compressed_result: "..."` instead of `result: "..."` when they're sent over Pusher. See docs for [Apollo Client](/javascript_client/apollo_subscriptions) or [Relay Modern](/javascript_client/relay_subscriptions) to read about preparing clients for compressed payloads.

By configuring `compress_pusher_payload: true` on a query-by-query basis, the subscription backend can continue to support clients running _old_ client code (by not compressing) while upgrading new clients to compressed payloads.

### Batched Deliveries

By default, `PusherSubscriptions` sends updates in batches of up to 10 at a time, using [batch triggers](https://github.com/pusher/pusher-http-ruby#batches). You can customize the batch size by passing `batch_size:` when installing it, for example:

```ruby
use GraphQL::Pro::PusherSubscriptions, batch_size: 1, ...
```

`batch_size: 1` will make `PusherSubscriptions` use the single trigger API instead of batch triggers.

## Webhook configuration

Your server needs to receive webhooks from Pusher when clients disconnect. This keeps your local subscription database in sync with Pusher.

In the Pusher web UI, Add a webhook for "Channel existence"



Then, mount the Rack app for handling webhooks from Pusher. For example, on Rails:

```ruby
# config/routes.rb

# Include GraphQL::Pro's routing extensions:
using GraphQL::Pro::Routes

Rails.application.routes.draw do
  # ...
  # Handle Pusher webhooks for subscriptions:
  mount MySchema.pusher_webhooks_client, at: "/pusher_webhooks"
end
```

This way, we'll be kept up-to-date with Pusher's unsubscribe events.

__Alternatively__, you can configure the routes to load your schema lazily, during the first request:

```ruby
# Provide the fully-qualified class name of your schema:
lazy_routes = GraphQL::Pro::Routes::Lazy.new("MySchema")
mount lazy_routes.pusher_webhooks_client, at: "/pusher_webhooks"
```

## Authorization

To ensure the privacy of subscription updates, you should use a [private channel](https://pusher.com/docs/client_api_guide/client_private_channels) for transport.

To use a private channel, add a `channel_prefix:` key to your query context:

```ruby
MySchema.execute(
  query_string,
  context: {
    # If this query is a subscription, use this prefix for the Pusher channel:
    channel_prefix: "private-user-#{current_user.id}-",
    # ...
  },
  # ...
)
```

That prefix will be applied to GraphQL-related Pusher channel names. (The prefix should begin with `private-`, as required by Pusher.)

Then, in your [auth endpoint](https://pusher.com/docs/authenticating_users#implementing_private_endpoints), you can assert that the logged-in user matches the channel name:

```ruby
if params[:channel_name].start_with?("private-user-#{current_user.id}-")
  # success, render the auth token
else
  # failure, render unauthorized
end
```

## Serializing Context

Since subscription state is stored in the database, then reloaded for pushing updates, you have to serialize and reload your query `context`.

By default, this is done with `GraphQL::Subscriptions::Serialize`'s `dump` and `load` methods, but you can provide custom implementations as well. To customize the serialization logic, create a subclass of `GraphQL::Pro::PusherSubscriptions` and override `#dump_context(ctx)` and `#load_context(ctx_string)`:

```ruby
class CustomSubscriptions < GraphQL::Pro::PusherSubscriptions
  def dump_context(ctx)
    context_hash = ctx.to_h
    # somehow convert this hash to a string, return the string
  end

  def load_context(ctx_string)
    # Given the string from the DB, create a new hash
    # to use as `context:`
  end
end
```

Then, use your _custom_ subscriptions class instead of the built-in one for your schema:

```ruby
class MySchema < GraphQL::Schema
  # Use custom subscriptions instead of GraphQL::Pro::PusherSubscriptions
  # to get custom serialization logic
  use CustomSubscriptions, redis: $redis
end
```

That gives you fine-grained control of context reloading.

## Dashboard

You can monitor subscription state in the [GraphQL-Pro Dashboard](/pro/dashboard):





## Development Tips

#### Clear subscription data

At any time, you can reset your subscription database with the __"Reset"__ button in the [GraphQL-Pro Dashboard](/pro/dashboard), or in Ruby:

```ruby
# Wipe all subscription data from the DB:
MySchema.subscriptions.clear
```

#### Developing with Pusher webhooks

To receive Pusher's webhooks in development, Pusher [suggests using ngrok](https://support.pusher.com/hc/en-us/articles/203112227-Developing-against-and-testing-WebHooks). It gives you a public URL which you can setup with Pusher, then any hooks delivered to that URL will be forwarded to your development environment.

## Client configuration

Install the [Pusher JS client](https://github.com/pusher/pusher-js) then see docs for:

- [Apollo Client](/javascript_client/apollo_subscriptions)
- [Relay Modern](/javascript_client/relay_subscriptions)
- [GraphiQL](/javascript_client/graphiql_subscriptions)
- [urql](/javascript_client/urql_subscriptions)

---

### Guides Subscriptions Subscription Classes (`guides/subscriptions/subscription_classes.md`)

# Subscription Classes

You can extend [GraphQL::Schema::Subscription](rdoc-ref:GraphQL::Schema::Subscription) to create fields that can be subscribed to.

These classes support several behaviors:

- [Authorizing](#check-permissions-with-authorized) (or rejecting) initial subscription requests and subsequent updates
- Returning values for [initial subscription requests](#initial-subscription-with-subscribe)
- [Unsubscribing](#terminating-the-subscription-with-unsubscribe) from the server
- Implicitly [scoping updates](#scope), to direct data to the right subscriber
- [Skipping updates](#subsequent-updates-with-update) for certain clients (eg, don't send updates to the person who triggered the event)

Continue reading to set up subscription classes.

## Add a base class

First, add a base class for your application. You can hook up your base classes there:

```ruby
# app/graphql/subscriptions/base_subscription.rb
class Subscriptions::BaseSubscription < GraphQL::Schema::Subscription
  # Hook up base classes
  object_class Types::BaseObject
  field_class Types::BaseField
  argument_class Types::BaseArgument
end
```

(This base class is a lot like the [mutation base class](/mutations/mutation_classes). They're both subclasses of [GraphQL::Schema::Resolver](rdoc-ref:GraphQL::Schema::Resolver).)

## Extend the base class and hook it up

Define a class for each subscribable event in your system. For example, if you run a chat room, you might publish events whenever messages are posted in a room:

```ruby
# app/graphql/subscriptions/message_was_posted.rb
class Subscriptions::MessageWasPosted < Subscriptions::BaseSubscription
end
```

Then, hook up the new class to the [Subscription root type](/subscriptions/subscription_type) with the `subscription:` option:

```ruby
class Types::SubscriptionType < Types::BaseObject
  field :message_was_posted, subscription: Subscriptions::MessageWasPosted
end
```

Now, it will be accessible as:

```graphql
subscription {
  messageWasPosted(roomId: "abcd") {
    # ...
  }
}
```

## Arguments

Subscription fields take [arguments](/fields/arguments) just like normal fields. They also accept a [`loads:` option](/mutations/mutation_classes#auto-loading-arguments) just like mutations. For example:

```ruby
class Subscriptions::MessageWasPosted < Subscriptions::BaseSubscription
  # `room_id` loads a `room`
  argument :room_id, ID, loads: Types::RoomType

  # It's passed to other methods as `room`
  def subscribe(room:)
    # ...
  end

  def update(room:)
    # ...
  end
end
```

This can be invoked as

```graphql
subscription($roomId: ID!) {
  messageWasPosted(roomId: $roomId) {
    # ...
  }
}
```

If the ID doesn't find an object, then the subscription will be unsubscribed (with `#unsubscribe`, see below).

## Fields

Like mutations, you can use a generated return type for subscriptions. When you add `field(...)`s to a subscription, they'll be added to the subscription's generated return type. For example:

```ruby
class Subscriptions::MessageWasPosted < Subscriptions::BaseSubscription
  field :room, Types::RoomType, null: false
  field :message, Types::MessageType, null: false
end
```

will generate:

```graphql
type MessageWasPostedPayload {
  room: Room!
  message: Message!
}
```

Which you can use in queries like:

```graphql
subscription($roomId: ID!) {
  messageWasPosted(roomId: $roomId) {
    room {
      name
    }
    message {
      author {
        handle
      }
      body
      postedAt
    }
  }
}
```

If you remove `null: false`, then you can return different data in the initial subscription and the subsequent updates. (See lifecycle methods below.)

Instead of a generated type, you can provide an already-configured type with `payload_type`:

```ruby
# Just return a message
payload_type Types::MessageType
```

(In that case, don't return a hash from `#subscribe` or `#update`, return a `message` object instead.)

## Scope

Usually, GraphQL-Ruby uses explicitly-passed arguments to determine when a [trigger](/subscriptions/triggers) applies to an active subscription. But, you can use `subscription_scope` to configure _implicit_ conditions on updates. When `subscription_scope` is configured, only triggers with a matching `scope:` value will cause clients to receive updates.

`subscription_scope` accepts a symbol and the given symbol will be looked up in `context` to find a scope value.

For example, this subscription will use `context[:current_organization_id]` as a scope:

```ruby
class Subscriptions::EmployeeHired < Subscriptions::BaseSubscription
  # ...
  subscription_scope :current_organization_id
end
```

Clients subscribe _without_ any arguments:

```graphql
subscription {
  employeeHired {
    hireDate
    employee {
      name
      department
    }
  }
}
```

But `.trigger`s are routed using `scope:`. So, if the subscriber's context includes `current_organization_id: 100`, then the trigger must include the same `scope:` value:

```ruby
MyAppSchema.subscriptions.trigger(
  # Field name
  :employee_hired,
  # Arguments
  {},
  # Object
  { hire_date: Time.now, employee: new_employee },
  # This corresponds to `context[:current_organization_id]`
  # in the original subscription:
  scope: 100
 )
```

Scope is also used for determining whether subscribers can receive the same [broadcast](/subscriptions/implementation#broadcasts).

## Check Permissions with #authorized?

Suppose a client is subscribing to messages in a chat room:

```graphql
subscription($roomId: ID!) {
  messageWasPosted(roomId: $roomId) {
    message {
      author { handle }
      body
      postedAt
    }
  }
}
```

You can implement `#authorized?` to check that the user has permission to subscribe to these arguments (and receive updates for these arguments), for example:

```ruby
def authorized?(room:)
  super && context[:viewer].can_read_messages?(room)
end
```

The method may return `false` or raise a `GraphQL::ExecutionError` to halt execution.

This method is called _before_ `#subscribe` and `#update`, described below. This way, if a user's permissions have changed since they subscribed, they won't receive updates unauthorized updates.

Also, if this method fails before calling `#update`, then the client will be automatically unsubscribed (with `#unsubscribe`).

## Initial Subscription with #subscribe

`def subscribe(**args)` is called when a client _first_ sends a `subscription { ... }` request. In this method, you can do a few things:

- Raise `GraphQL::ExecutionError` to halt and return an error
- Return a value to give the client an initial response
- Return `:no_response` to skip the initial response
- Return `super` to fall back to the default behavior (which is `:no_response`).

You can define this method to add initial responses or perform other logic before subscribing.

### Adding an Initial Response

By default, GraphQL-Ruby returns _nothing_ (`:no_response`) on an initial subscription. But, you may choose to override this and return a value in `def subscribe`. For example:

```ruby
class Subscriptions::MessageWasPosted < Subscriptions::BaseSubscription
  # ...
  field :room, Types::RoomType

  def subscribe(room:)
    # authorize, etc ...
    # Return the room in the initial response
    {
      room: room
    }
  end
end
```

Now, a client can get some initial data with:

```graphql
subscription($roomId: ID!) {
  messageWasPosted(roomId: $roomId) {
    room {
      name
      messages(last: 40) {
        # ...
      }
    }
  }
}
```

## Subsequent Updates with #update

After a client has registered a subscription, the application may trigger subscription updates with `MySchema.subscriptions.trigger(...)` (see the [Triggers guide](/subscriptions/triggers) for more). Then, `def update` will be called for each client's subscription. In this method you can:

- Unsubscribe the client with `unsubscribe`
- Return a value with `super` (which returns `object`) or by returning a different value.
- Return `NO_UPDATE` to skip this update

### Skipping subscription updates

Perhaps you don't want to send updates to a certain subscriber. For example, if someone leaves a comment, you might want to push the new comment to _other_ subscribers, but not the commenter, who already has that comment data. You can accomplish this by returning `NO_UPDATE`.

```ruby
class Subscriptions::CommentWasAdded < Subscriptions::BaseSubscription
  def update(post_id:)
    comment = object # #<Comment ...>
    if comment.author == context[:viewer]
      NO_UPDATE
    else
      # Continue updating this client, since it's not the commenter
      super
    end
  end
end
```

### Returning a different object for subscription updates

By default, whatever object you pass to `.trigger(event_name, args, object)` will be used for responding to subscription fields. But, you can return a different object from `#update` to override this:

```ruby
field :queue, Types::QueueType, null: false

# eg, `MySchema.subscriptions.trigger("queueWasUpdated", {name: "low-priority"}, :low_priority)`
def update(name:)
  # Make a Queue object which _represents_ the queue with this name
  queue = JobQueue.new(name)

  # This object was passed to `.trigger`, but we're ignoring it:
  object # => :low_priority

  # return the queue instead:
  { queue: queue }
end
```

## Terminating the subscription with #unsubscribe

Within a subscription method, you may call `unsubscribe` to terminate the client's subscription, for example:

```ruby
def update(room:)
  if room.archived?
    # Don't let anyone subscribe to messages on an archived room
    unsubscribe
  else
    super
  end
end
```

`#unsubscribe` has the following effects:

- The subscription is unregistered from the backend (this is backend-specific)
- The client is told to unsubscribe (this is transport-specific)

Arguments with `loads:` configurations will call `unsubscribe` if they are `required: true` (which is the default) and their ID doesn't return a value. (It's assumed that the subscribed object was deleted.)

You can provide a final update value with `unsubscribe` by passing a value to the method:

```ruby
def update(room:)
  if room.archived?
    # Don't let anyone subscribe to messages on an archived room
    unsubscribe({message: "This room has been archived"})
  else
    super
  end
end
```

## Extras

Subscription methods can access query-related metadata by configuring `extras [...]` in the class definition. For example, to use a `lookahead` and the `ast_node`:

```ruby
class Subscriptions::JobFinished < GraphQL::Schema::Subscription
  # ...
  extras [:lookahead, :ast_node]

  def subscribe(lookahead:, ast_node:)
    # ...
  end

  def update(lookahead:, ast_node:)
    # ...
  end
end
```

See the [Extra Field Metadata](/fields/introduction#extra-field-metadata) for more information about available metadata.

---

### Guides Subscriptions Subscription Type (`guides/subscriptions/subscription_type.md`)

# Subscription Type

`Subscription` is the entry point for all subscriptions in a GraphQL system. Each field corresponds to an event which may be subscribed to:

```graphql
type Subscription {
  # Triggered whenever a post is added
  postWasPublished: Post
  # Triggered whenever a comment is added;
  # to watch a certain post, provide a `postId`
  commentWasPublished(postId: ID): Comment
}
```

This type is the root for `subscription` operations, for example:

```graphql
subscription {
  postWasPublished {
    # This data will be delivered whenever `postWasPublished`
    # is triggered by the server:
    title
    author {
      name
    }
  }
}
```

To add subscriptions to your system, define an `ObjectType` named `Subscription`:

```ruby
# app/graphql/types/subscription_type.rb
class Types::SubscriptionType < GraphQL::Schema::Object
  field :post_was_published, subscription: Subscriptions::PostWasPublished
  # ...
end
```

Then, add it as the subscription root with `subscription(...)`:

```ruby
# app/graphql/my_schema.rb
class MySchema < GraphQL::Schema
  query(Types::QueryType)
  # ...
  # Add Subscription to
  subscription(Types::SubscriptionType)
end
```

See [Implementing Subscriptions](/subscriptions/implementation) for more about actually delivering updates.

See [Subscription Classes](/subscriptions/subscription_classes) for more about implementing subscription root fields.

---

### Guides Subscriptions Triggers (`guides/subscriptions/triggers.md`)

# Triggers

From your application, you can push updates to GraphQL clients with `.trigger`.

Events are triggered _by name_, and the name must match fields on your [Subscription Type](/subscriptions/subscription_type)

```ruby
# Update the system with the new blog post:
MySchema.subscriptions.trigger(:post_added, {}, new_post)
```

The arguments are:

- `name`, which corresponds to the field on subscription type
- `arguments`, which corresponds to the arguments on subscription type (for example, if you subscribe to comments on a certain post, the arguments would be `{post_id: comment.post_id}`.)
- `object`, which will be the root object of the subscription update
- `scope:` (shown below) for implicitly scoping the clients who will receive updates.

## Scope

To send updates to _certain clients only_, you can use `scope:` to narrow the trigger's reach.

Scopes are based on query context: a value in `context:` is used as the scope; an equivalent value must be passed with `.trigger(... scope:)` to update that client. (The value is serialized with `GraphQL::Subscriptions::Serialize`.)

To specify that a topic is scoped, add a `subscription_scope` option to the Subscription class:

```ruby
class Subscriptions::CommentAdded < Subscription::BaseSubscription
  description "A comment was added to one of the viewer's posts"
  # For a given viewer, this will be triggered
  # whenever one of their posts gets a new comment
  subscription_scope :current_user_id
  # ...
end
```

(Read more in the [Subscription Classes guide](/subscriptions/subscription_classes#scope).)

Then, subscription operations should have a `context: { current_user_id: ... }` value, for example:

```ruby
# current_user_id will be the scope for some subscriptions:
MySchema.execute(query_string, context: { current_user_id: current_user.id })
```

Finally, when events happen in your app, you should provide the scoping value as `scope:`, for example:

```ruby
# A new comment is added
comment = post.comments.create!(attrs)
# notify the author
author_id = post.author.id
MySchema.subscriptions.trigger(:comment_added, {}, comment, scope: author_id)
```

Since this trigger has a `scope:`, only subscribers with a matching scope value will be updated.

## Validation

By default, subscriptions are re-validated when a trigger causes them to send updates. To disable this, you can pass `validate_update: false` when hooking up subscriptions to your schema. For example:

```ruby
use SomeSubscriptions, validate_update: false
```

If you're sure you won't be releasing breaking changes to your schema, this setting can reduce overhead in evaluating updates.

---

### Guides Testing Helpers (`guides/testing/helpers.md`)

# Helpers

GraphQL-Ruby ships with a test helper method, `run_graphql_field`, that can execute a GraphQL field in isolation. To use it in your test suite, include the module with your schema class:

```ruby
# Mix in `run_graphql_field(...)` to run on `MySchema`
include GraphQL::Testing::Helpers.for(MySchema)
```

Then, you can run fields using [Testing::Helpers#run_graphql_field](rdoc-ref:GraphQL::Testing::Helpers#run_graphql_field):

```ruby
post = Post.first
graphql_post_title = run_graphql_field("Post.title", post)
assert_equal "100 Great Ideas", graphql_post_title
```

`run_graphql_field` accepts two required arguments:

- Field _path_, in `Type.field` format
- Runtime object: some non-`nil` object to resolve the field on.

Additionally, it accepts some keyword arguments:

- `arguments:`, GraphQL arguments to the field, in Ruby-style (underscore, symbol) or GraphQL-style (camel-case, string)
- `context:`, the GraphQL context to use for this query

`run_graphql_field` performs several GraphQL-related steps:

- Checks `.visible?` on the named Object Type, raising an error if it isn't visible
- Wraps the given runtime object in the GraphQL Object Type
- Checks `.authorized?` on the type, calling [Schema.unauthorized_object](rdoc-ref:GraphQL::Schema.unauthorized_object) if authorization fails
- Prepares arguments for field resolution
- Checks `#visible?` on the field, raising an error if the field isn't visible
- Checks `#authorized?` on the field, calling [Schema.unauthorized_field](rdoc-ref:GraphQL::Schema.unauthorized_field) if it fails
- Calls any [field extensions](/type_definitions/field_extensions)
- Runs [Dataloader](/dataloader/overview) and/or GraphQL-Batch, as needed

## Resolving fields on the same object

You can use [Testing::Helpers#with_resolution_context](rdoc-ref:GraphQL::Testing::Helpers#with_resolution_context) to use the same type, runtime object, and GraphQL context for multiple field resolutions. For example:

```ruby
# Assuming `include GraphQL::Testing::Helpers.for(MySchema)`
# was used above ...
with_resolution_context(type: "Post", object: example_post, context: { current_user: author }) do |rc|
  assert_equal "100 Great Ideas", rc.run_graphql_field("title")
  assert_equal true, rc.run_graphql_field("viewerIsAuthor")
  assert_equal 5, rc.run_graphql_field("commentsCount")
  # Optionally, pass `arguments:` for the field:
  assert_equal 9, rc.run_graphql_field("commentsCount", arguments: { include_unmoderated: true })
end
```

The method yields a resolution context (`rc`, above) which responds to `run_graphql_field`.

---

### Guides Testing Integration Tests (`guides/testing/integration_tests.md`)

# Integration Tests

Besides testing [schema structure](/testing/schema_structure), you should also test your GraphQL system's behavior. There are really a few levels to this:

- __Application-level__ behaviors, like business logic, permissions, and persistence. These behaviors may be shared by your API and user interface.
- __Interface-level__ behaviors, like GraphQL fields, mutations, error scenarios, and HTTP-specific behaviors. These are unique to your GraphQL system.
- __Transport-level__ behaviors, like HTTP headers, parameters and status codes

## Testing Application-Level Behaviors

When it comes to _how your application behaves_, you should lean on _unit tests_ which exercise application primitives directly. For example, if postings require a title and a body, you should write a test for `Post` which asserts that invalid `Post`s fail to save. Several other components of the application may be tested this way:

- Permissions: test your authorization system using example resources and actors. Dedicated, high-level frameworks like [Pundit](https://github.com/varvet/pundit) make it easy to test authorization in isolation.
- Business logic: What are the _operations_ that a user can perform in your system? For example, on a blog, they might be: drafting and publishing posts, moderating comments, filtering posts by category, or blocking users. Test these operations in isolation so you can be confident that the core code is correct.
- Persistence (and other external services): how does your app interact with the "outside world", like databases, files, and third-party APIs? These interactions also deserve specific tests.


By testing these (and other) application-level behaviors _without_ GraphQL, you can reduce the overhead of your test suite and simplify your testing scenarios.

## Testing Interface-Level Behaviors

After building your application, you give it an interface so that people (or other software) can interact with it. Sometimes the interface is a website, other times it's a GraphQL API. The interface has transport-specific primitives that map to your application's primitives. For example, a React app might have components that correspond to `Post`, `Comment`, and a `Moderation` operation. (These components might even be context-specific, like `ThreadComment` or `DraftPost`.) Similarly, a GraphQL interface has types and fields that correspond to the underlying application primitives (like `Post` and `Comment` types, a `Post.isDraft` field, or a `ModerateComment` mutation).

The best way to test a GraphQL interface is with _integration tests_ which run the whole GraphQL system (using `MySchema.execute(...)`). By using an integration test, you can be sure that all of GraphQL-Ruby's internal systems are engaged (validation, analysis, authorization, data loading, response type-checking, etc.).

A basic integration test might look like:

```ruby
it "loads posts by ID" do
  query_string = <<-GRAPHQL
    query($id: ID!){
      node(id: $id) {
        ... on Post {
          title
          id
          isDraft
          comments(first: 5) {
            nodes {
              body
            }
          }
        }
      }
    }
  GRAPHQL

  post = create(:post_with_comments, title: "My Cool Thoughts")
  post_id = MySchema.id_from_object(post, Types::Post, {})
  result = MySchema.execute(query_string, variables: { id: post_id })

  post_result = result["data"]["node"]
  # Make sure the query worked
  assert_equal post_id, post_result["id"]
  assert_equal "My Cool Thoughts", post_result["title"]
end
```

To make sure that different parts of the system are properly engaged, you can add integration tests for specific scenarios, too. For example, you could add a test to make sure that data is hidden from some users:


```ruby
it "doesn't show draft posts to anyone except their author" do
  author = create(:user)
  non_author = create(:non_user)
  draft_post = create(:post, draft: true, author: author)

  query_string = <<-GRAPHQL
  query($id: ID!) {
    node(id: $id) {
      ... on Post {
        isDraft
      }
    }
  }
  GRAPHQL

  post_id = MySchema.id_from_object(draft_post, Types::Post, {})

  # Authors can see their drafts:
  author_result = MySchema.execute(query_string, context: { viewer: author }, variables: { id: post_id })
  assert_equal true, author_result["data"]["node"]["isDraft"]

  # Other users can't see others' drafts
  non_author_result = MySchema.execute(query_string, context: { viewer: non_author }, variables: { id: post_id })
  assert_nil author_result["data"]["node"]
end
```

This test engages the underlying authorization and business logic, and provides a sanity check at the GraphQL interface layer.

## Testing Transport-Level Behaviors

GraphQL is usually served over HTTP. You probably want tests that make sure that HTTP inputs are correctly prepared for GraphQL. For example, you might test that:

- POST data is correctly turned into query variables
- Authentication headers are used to load a `context[:viewer]`


In Rails, you might use a [functional test](https://guides.rubyonrails.org/testing.html#functional-testing-for-controllers) for this, for example:

```ruby
it "loads user token into the viewer" do
  query_string = "{ viewer { username } }"
  post graphql_path, params: { query: query_string }
  json_response = JSON.parse(@response.body)
  assert_nil json_response["data"]["viewer"], "Unauthenticated requests have no viewer"

  # This time, add some authentication to the HTTP request
  user = create(:user)
  post graphql_path,
    params: { query: query_string },
    headers: { "Authorization" => "Bearer #{user.auth_token}" }

  json_response = JSON.parse(@response.body)
  assert_equal user.username, json_response["data"]["viewer"]["username"], "Authenticated requests load the viewer"
end
```

---

### Guides Testing Profiling (`guides/testing/profiling.md`)

# Profiling

If you want to know more about how time is spent during GraphQL queries, including GraphQL-Ruby internals, you can use Ruby profiling tools to take a closer look.

If you want to investigate GraphQL-Ruby performance together, prepare a runtime profile and memory profile as described below and [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=Performance+investigation&body=) on GitHub, including those files.

## StackProf

[StackProf](https://github.com/tmm1/stackprof) is a Ruby library for figuring out where an operation's time is spent. To capture a profile, surround a block with `StackProf.run { ... }`.

```ruby
require "stackprof"

# Prepare any GraphQL-related data or context:
query_string = "{ someGraphQL ... }"
context = { ... }

# This will dump a profile in `tmp/graphql-prof.dump`
StackProf.run(mode: :wall, interval: 10, out: "tmp/graphql-prof.dump") do
  # Execute the query inside the block:
  MySchema.execute(query_string, context: context)
end
```

The `out:` option tells StackProf to create a "dump" at the given location. Then, anyone who has that file can investigate the profile using the `stackprof` command, for example:

```
$ stackprof tmp/graphql-prof.dump
==================================
  Mode: wall(1)
  Samples: 2492 (58.06% miss rate)
  GC: 0 (0.00%)
==================================
     TOTAL    (pct)     SAMPLES    (pct)     FRAME
       902  (36.2%)          94   (3.8%)     GraphQL::Execution::Interpreter::Runtime#evaluate_selection_with_resolved_keyword_args
      1283  (51.5%)          87   (3.5%)     GraphQL::Execution::Interpreter::Runtime#continue_field
       274  (11.0%)          78   (3.1%)     GraphQL::Schema::Field#resolve
      1068  (42.9%)          73   (2.9%)     GraphQL::Execution::Interpreter::Runtime#evaluate_selection
      # ...
```

Additionally, `stackprof` accepts a `--method` argument which provides details about the performance and usage of a specific method, for example:

```
$ stackprof tmp/small.dump --method #gather_selections
GraphQL::Execution::Interpreter::Runtime#gather_selections (/Users/rmosolgo/code/graphql-ruby/lib/graphql/execution/interpreter/runtime.rb:305)
  samples:    17 self (0.7%)  /     17 total (0.7%)
  callers:
      16  (   94.1%)  GraphQL::Execution::Interpreter::Runtime#continue_field
       6  (   35.3%)  Array#each
       1  (    5.9%)  GraphQL::Execution::Interpreter::Runtime#run_eager
  callees (0 total):
       6  (    Inf%)  Array#each
  code:
    1    (0.0%) /     1   (0.0%)  |   305  |                 when :lookahead
    6    (0.2%) /     6   (0.2%)  |   306  |                   if !field_ast_nodes
    3    (0.1%) /     3   (0.1%)  |   307  |                     field_ast_nodes = [ast_node]
                                  |   308  |                   end
```

Anyone with the `.dump` file can perform this analysis -- it's a really useful file! If you want to investigate GraphQL-Ruby performance together, please share a runtime profile.


## MemoryProfiler

[MemoryProfiler](https://github.com/SamSaffron/memory_profiler) provides insight into where an operation interacts with system memory and the Ruby heap. This is helpful because memory usage problems cause code to run slowly; fixing them can make code run fast.

To produce a report, wrap a block in `MemoryProfiler.report { ... }` and then call `.pretty_print` on the result. For example, to create a report on a GraphQL query:

```ruby
require 'memory_profiler'

# Prepare any GraphQL-related data or context:
query_string = "{ someGraphQL ... }"
context = { ... }

report = MemoryProfiler.report do
  # Execute the query inside the block:
  MySchema.execute(query_string, context: context)
end

# Write the result to a file
report.pretty_print(to_file: "tmp/graphql-memory.txt")
```

The report will include many interesting sections including:

- Total memory and objects allocated
- Objects allocated by location and by class
- String allocations, including the number of times a string with the same value was allocated

All of these can indicate "hot spots" in the code and inform refactors to reduce memory use. In turn, this reduces time spent in Ruby GC.

If you want to investigate GraphQL-Ruby performance together, please share a memory profile.

---

### Guides Testing Schema Structure (`guides/testing/schema_structure.md`)

# Schema Structure

Structural changes to a GraphQL schema come in two categories:

- __Breaking__ changes may cause previously-valid queries to become invalid. For example, if you remove the `title` field, anyone who tries to query that field will get a validation error instead of response data.
- __Non-breaking__ changes _add_ options to a schema without breaking previously-valid queries.

Making a _breaking_ change can be bad news for your API clients, since their applications may break. But, sometimes they're required. _Non-breaking_ changes don't affect existing queries, since they just _add_ new parts to the schema.

Here are few tips for managing schema structure changes.

## Maintain a `.graphql` schema dump

Make structure changes part of the normal code review process by adding a `schema.graphql` artifact to your project. This way, any changes to schema structure will show up clearly in a pull request as a diff to that file.

You can read about this approach in ["Tracking Schema Changes with GraphQL-Ruby"](https://rmosolgo.github.io/ruby/graphql/2017/03/16/tracking-schema-changes-with-graphql-ruby) or the built-in [GraphQL::RakeTask](rdoc-ref:GraphQL::RakeTask) for generating schema dumps.

## Automatically check for breaking changes

You can use [GraphQL::SchemaComparator](https://github.com/xuorig/graphql-schema_comparator) to check for breaking changes during development or CI. If you maintain a dump of queries that typically run against your server, you may also utilize `GraphQL::StaticValidation` to validate these queries directly. A Rake task such as the one below can be used to identify changes that are incompatible with existing queries.

```ruby
namespace :graphql do
  namespace :queries do
    desc 'Validates GraphQL queries against the current schema'
    task validate: [:environment] do
      queries_file = 'test/fixtures/files/queries.json'
      queries = Oj.load(File.read(queries_file))

      Validate.run_validate(queries, MySchema)
    end

    module Validate
      def self.run_validate(queries, schema)
        puts '⏳  Validating queries...'
        puts "\n"

        results = queries.map { |query| schema.validate(query) }
        errors = results.flatten

        if errors.empty?
          puts '✅  All queries are valid'
        else
          print_errors(errors)
        end
      end

      def self.print_errors(errors)
        puts 'Detected the following errors:'
        puts "\n"

        errors.each do |error|
          path = error.path.join(', ')
          puts "❌  #{path}: #{error.message}"
        end
      end
    end
  end
end
```

---

### Guides Type Definitions Directives (`guides/type_definitions/directives.md`)

# Directives

Directives are system-defined keywords with two kinds of uses:

- [runtime directives](#runtime-directives) _modify execution_, so that when they are present, the GraphQL runtime does something different;
- [schema directives](#schema-directives) _annotate schema definitions_, indicating different configurations or metadata about schemas and types.

## Runtime Directives

Runtime directives are server-defined keywords that modify GraphQL execution. All GraphQL systems have at least _two_ directives, `@skip` and `@include`. For example:

```ruby
query ProfileView($renderingDetailedProfile: Boolean!){
  viewer {
    handle
    # These fields will be included only if the check passes:
    ... @include(if: $renderingDetailedProfile) {
      location
      homepageUrl
    }
  }
}
```

Here's how the two built-in directives work:

- `@skip(if: ...)` skips the selection if the `if: ...` value is truthy ([GraphQL::Schema::Directive::Skip](rdoc-ref:GraphQL::Schema::Directive::Skip))
- `@include(if: ...)` includes the selection if the `if: ...` value is truthy ([GraphQL::Schema::Directive::Include](rdoc-ref:GraphQL::Schema::Directive::Include))

### Custom Runtime Directives

Custom directives extend [GraphQL::Schema::Directive](rdoc-ref:GraphQL::Schema::Directive):

```ruby
# app/graphql/directives/my_directive.rb
class Directives::MyDirective < GraphQL::Schema::Directive
  description "A nice runtime customization"
  location FIELD
end
```

Then, they're hooked up to the schema using `directive(...)`:

```ruby
class MySchema < GraphQL::Schema
  # Attach the custom directive to the schema
  directive(Directives::MyDirective)
end
```

And you can reference them in the query with `@myDirective(...)`:

```ruby
query {
  field @myDirective {
    id
  }
}
```

[GraphQL::Schema::Directive::Feature](rdoc-ref:GraphQL::Schema::Directive::Feature) and [GraphQL::Schema::Directive::Transform](rdoc-ref:GraphQL::Schema::Directive::Transform) are included in the library as examples.

### Runtime hooks

Directive classes may implement the following class methods to interact with the runtime:

- `def self.include?(obj, args, ctx)`: If this hook returns `false`, the nodes flagged by this directive will be skipped at runtime.
- `def self.resolve(obj, args, ctx)`: Wraps the resolution of flagged nodes. Resolution is passed as a __block__, so `yield` will continue resolution.

Looking for a runtime hook that isn't listed here? [open an issue](https://github.com/rmosolgo/graphql-ruby/issues/new?title=New+directive+hook%3A+%40something&body=%3C%21--+Describe+how+the+directive+would+be+used+and+then+how+you+might+implement+it+--%3E+) to start the conversation!

## Schema Directives

Schema directives are used in GraphQL's interface definition language (IDL). For example, `@deprecated` is built in to GraphQL-Ruby:

```ruby
type User {
  firstName @deprecated(reason: "Use `name` instead")
  lastName @deprecated(reason: "Use `name` instead")
  name
}
```

In the schema definition, directives express metadata about types, fields, and arguments.

### Custom Schema Directives

To make a custom schema directive, extend [GraphQL::Schema::Directive](rdoc-ref:GraphQL::Schema::Directive):

```ruby
# app/graphql/directives/permission.rb
class Directives::Permission < GraphQL::Schema::Directive
  argument :level, String
  locations FIELD_DEFINITION, OBJECT
end
```

Then, attach it to parts of your schema with `directive(...)`:

```ruby
class Types::JobPosting < Types::BaseObject
  directive Directives::Permission, level: "manager"
end
```

Arguments and fields also accept a `directives:` keyword:

```ruby
field :salary, Integer, null: false,
  directives: { Directives::Permission => { level: "manager" } }
```

After that:

- the configured object's `.directives` method will return an array containing an instance of the specified directive
- IDL dumps (from [Schema.to_definition](rdoc-ref:GraphQL::Schema.to_definition)) will include the configured directives

Similarly, [Schema.from_definition](rdoc-ref:GraphQL::Schema.from_definition) parses directives from IDL strings.

For a couple of built-in examples, check out:

- [GraphQL::Schema::Directive::Deprecated](rdoc-ref:GraphQL::Schema::Directive::Deprecated) which implements `deprecation_reason` (via [GraphQL::Schema::Member::HasDeprecationReason](rdoc-ref:GraphQL::Schema::Member::HasDeprecationReason))
- [GraphQL::Schema::Directive::Flagged](rdoc-ref:GraphQL::Schema::Directive::Flagged), which is an example of using schema directives to implement [visibility](/authorization/visibility)

## Custom Name

By default, the directive's name is taken from the class name. You can override this with `graphql_name`, for example:

```ruby
class Directives::IsPrivate < GraphQL::Schema::Directive
  graphql_name "someOtherName"
end
```

## Arguments

Like fields, directives may have [arguments](/fields/arguments) :

```ruby
argument :if, Boolean,
  description: "Skips the selection if this condition is true"
```

---

### Guides Type Definitions Enums (`guides/type_definitions/enums.md`)

# Enums

Enum types are sets of discrete values. An enum field must return one of the possible values of the enum. In the [GraphQL Schema Definition Language](https://graphql.org/learn/schema/#enum-types) (SDL), enums are described like this:

```ruby
enum MediaCategory {
  AUDIO
  IMAGE
  TEXT
  VIDEO
}
```

So, a `MediaCategory` value is one of: `AUDIO`, `IMAGE`, `TEXT`, or `VIDEO`. This is similar to [ActiveRecord enums](https://api.rubyonrails.org/classes/ActiveRecord/Enum.html).

In a GraphQL query, enums are written as identifiers (not strings), for example:

```ruby
search(term: "puppies", mediaType: IMAGE) { ... }
```

(Notice that `IMAGE` doesn't have quotes.)

But, when GraphQL responses or variables are transported using JSON, enum values are expressed as strings, for example:

```ruby
# in a graphql controller:
params["variables"]
# { "mediaType" => "IMAGE" }
```

## Defining Enum Types

In your application, enums extend [GraphQL::Schema::Enum](rdoc-ref:GraphQL::Schema::Enum) and define values with the `value(...)` method:

```ruby
# First, a base class
# app/graphql/types/base_enum.rb
class Types::BaseEnum < GraphQL::Schema::Enum
end

# app/graphql/types/media_category.rb
class Types::MediaCategory < Types::BaseEnum
  value "AUDIO", "An audio file, such as music or spoken word"
  value "IMAGE", "A still image, such as a photo or graphic"
  value "TEXT", "Written words"
  value "VIDEO", "Motion picture, may have audio"
end
```

Each value may have:

- A description (as the second argument or `description:` keyword)
- A comment (as a `comment:` keyword)
- A deprecation reason (as `deprecation_reason:`), marking this value as deprecated
- A corresponding Ruby value (as `value:`), see below

By default, Ruby strings correspond to GraphQL enum values. But, you can provide `value:` options to specify a different mapping. For example, if you use symbols instead of strings, you can say:

```ruby
value "AUDIO", value: :audio
```

Then, GraphQL inputs of `AUDIO` will be converted to `:audio` and Ruby values of `:audio` will be converted to `"AUDIO"` in GraphQL responses.

Enum classes are never instantiated and their methods are never called.

You can get the GraphQL name of the enum value using the method matching its downcased name:

```ruby
Types::MediaCategory.audio # => "AUDIO"
```

You can pass a `value_method:` to override the value of the generated method:

```ruby
value "AUDIO", value: :audio, value_method: :lo_fi_audio

# ...

Types::MediaCategory.lo_fi_audio # => "AUDIO"
```

Also, you can completely skip the method generation by setting `value_method` to `false`

```ruby
value "AUDIO", value: :audio, value_method: false
```

---

### Guides Type Definitions Extensions (`guides/type_definitions/extensions.md`)

# Extending the GraphQL-Ruby Type Definition System

While integrating GraphQL into your app, you can customize the definition DSL. For example, you might:

- Assign "area of responsibility" to different types and fields
- DRY up shared logic between types and fields
- Attach metadata for use during authorization

This guide describes various options for extending the class-based definition API. Keep in mind that these approaches may change as the API matures. If you're having trouble, consider opening an issue on GitHub to get help.

**Note**: This document describes best practice with GraphQL-Ruby 1.10+. For customizing schemas on older versions, use GitHub to browse older versions of this page.

## Customization Overview

In general, the schema definition process goes like this:

- The application defines lots of classes for the GraphQL types
- Starting from root types (`query`, `mutation`, and `subscription`) and any defined `orphan_types`, the schema discovers all types, fields, arguments, enum values, and directives in the schema
- Non-type objects (fields, arguments, enum values) are initialized when they're attached to the classes or instances they belong to.

## Customizing type definitions

In your custom classes, you can add class-level instance variables that hold configuration. For example:

```ruby
class Types::BaseObject < GraphQL::Schema::Object
  # Call this method in an Object class to get or set the permission level:
  def self.required_permission(permission_level = nil)
    if permission_level.nil?
      # return the configured value
      @required_permission
    else
      @required_permission = permission_level
    end
  end
end

# Then, in concrete classes
class Dossier < BaseObject
  # The Dossier object type will have `.metadata[:required_permission] # => :admin`
  required_permission :admin
end

# Now, the type responds to that method:
Dossier.required_permission
# => :admin
```

Now, any runtime code which calls `type.required_permission` will get the configured value.

### Customizing fields

Fields are generated in a different way. Instead of using classes, they are generated with instances of `GraphQL::Schema::Field` (or a subclass). In short, the definition process works like this:

```ruby
# This is what happens under the hood, roughly:
# In an object class:
field :name, String, null: false
# ...
# Leads to:
field_config = GraphQL::Schema::Field.new(name: :name, type: String, null: false)
```

So, you can customize this process by:

- creating a custom class which extends `GraphQL::Schema::Field`
- overriding `#initialize` on that class (instance methods)
- registering that class as the `field_class` on Objects and Interfaces which should use the customized field.

For example, you can create a custom class which accepts a new parameter to `initialize`:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  # Override #initialize to take a new argument:
  def initialize(*args, required_permission: nil, **kwargs, &block)
    @required_permission = required_permission
    # Pass on the default args:
    super(*args, **kwargs, &block)
  end

  attr_reader :required_permission
end
```

Then, pass the field class as `field_class(...)` wherever it should be used:

```ruby
class Types::BaseObject < GraphQL::Schema::Object
  # Use this class for defining fields
  field_class BaseField
end

# And....
class Types::BaseInterface < GraphQL::Schema::Interface
  field_class BaseField
end

class Mutations::BaseMutation < GraphQL::Schema::RelayClassicMutation
  field_class BaseField
end
```

Now, `BaseField.new(*args, &block)` will be used to create `GraphQL::Schema::Field`s on those types. At runtime `field.required_permission` will return the configured value.

### Customizing Connections

Connections may be customized in a similar way to Fields.

- Create a new class extending 'GraphQL::Types::Relay::BaseConnection'
- Assign it to your object/interface type with `connection_type_class(MyCustomConnection)`

For example, you can create a custom connection:

```ruby
class Types::MyCustomConnection < GraphQL::Types::Relay::BaseConnection
  # BaseConnection has these nullable configurations
  # and the nodes field by default, but you can change
  # these options if you want
  edges_nullable(true)
  edge_nullable(true)
  node_nullable(true)
  has_nodes_field(true)

  field :total_count, Integer, null: false

  def total_count
    object.items.size
  end
end
```

Then, pass the field class as `connection_type_class(...)` wherever it should be used:

```ruby
module Types
  class Types::BaseObject < GraphQL::Schema::Object
    # Use this class for defining connections
    connection_type_class MyCustomConnection
  end
end
```

Now, all type classes that extend `BaseObject` will have a connection_type with the additional field `totalCount`.

### Customizing Edges

Edges may be customized in a similar way to Connections.

- Create a new class extending 'GraphQL::Types::Relay::BaseEdge'
- Assign it to your object/interface type with `edge_type_class(MyCustomEdge)`

### Customizing Arguments

Arguments may be customized in a similar way to Fields.

- Create a new class extending `GraphQL::Schema::Argument`
- Use `argument_class(MyArgClass)` to assign it to your base field class, base resolver class, and base mutation class

Then, in your custom argument class, you can use `#initialize(name, type, desc = nil, **kwargs)` to take input from the DSL.

### Customizing Enum Values

Enum values may be customized in a similar way to Fields.

- Create a new class extending `GraphQL::Schema::EnumValue`
- Assign it to your base `Enum` class with `enum_value_class(MyEnumValueClass)`

Then, in your custom enum class, you can use `#initialize(name, desc = nil, **kwargs)` to take input from the DSL.

---

### Guides Type Definitions Field Extensions (`guides/type_definitions/field_extensions.md`)

# Field Extensions

[GraphQL::Schema::FieldExtension](rdoc-ref:GraphQL::Schema::FieldExtension) provides a way to modify user-defined fields in a programmatic way. For example, Relay connections are implemented as a field extension ([GraphQL::Schema::Field::ConnectionExtension](rdoc-ref:GraphQL::Schema::Field::ConnectionExtension)).

## Making a new extension

Field extensions are subclasses of [GraphQL::Schema::FieldExtension](rdoc-ref:GraphQL::Schema::FieldExtension):

```ruby
class MyExtension < GraphQL::Schema::FieldExtension
end
```

## Using an extension

Defined extensions can be added to fields using the `extensions: [...]` option or the `extension(...)` method:

```ruby
field :name, String, null: false, extensions: [UpcaseExtension]
# or:
field :description, String, null: false do
  extension(UpcaseExtension)
end
```

See below for how extensions may modify fields.

## Modifying field configuration

When extensions are attached, they are initialized with a `field:` and `options:`. Then, `#apply` is called, when they may extend the field they're attached to. For example:

```ruby
class SearchableExtension < GraphQL::Schema::FieldExtension
  def apply
    # add an argument to this field:
    field.argument(:query, String, required: false, description: "A search query")
  end
end
```

This way, an extension can encapsulate a behavior requiring several configuration options.

## Adding default argument configurations

Extensions may provide _default_ argument configurations which are applied if the field doesn't define the argument for itself. The configuration is passed to [Schema::FieldExtension.default_argument](rdoc-ref:GraphQL::Schema::FieldExtension.default_argument). For example, to define a `:query` argument if the field doesn't already have one:

```ruby
class SearchableExtension < GraphQL::Schema::FieldExtension
  # Any field which uses this extension and _doesn't_ define
  # its own `:query` argument will get an argument configured with this:
  default_argument(:query, String, required: false, description: "A search query")
end
```

Additionally, extensions may implement `def after_define` which is called _after_ the field's `do .. . end` block. This is helpful when an extension should provide _default_ configurations without overriding anything in the field definition. (When extensions are added by calling `field.extension(...)` on an already-defined field `def after_define` is called immediately.)

## Modifying field execution

Extensions have two hooks that wrap field resolution. Since GraphQL-Ruby supports deferred execution, these hooks _might not_ be called back-to-back.

First, [GraphQL::Schema::FieldExtension#resolve](rdoc-ref:GraphQL::Schema::FieldExtension#resolve) is called. `resolve` should `yield(object, arguments)` to continue execution. If it doesn't `yield`, then the underlying field won't be called. Whatever `#resolve` returns will be used for continuing execution.

After resolution and _after_ syncing lazy values (like `Promise`s from `graphql-batch`), [GraphQL::Schema::FieldExtension#after_resolve](rdoc-ref:GraphQL::Schema::FieldExtension#after_resolve) is called. Whatever that method returns will be used as the field's return value.

See the linked API docs for the parameters of those methods.

### Execution "memo"

One parameter to `after_resolve` deserves special attention: `memo:`. `resolve` _may_ yield a third value. For example:

```ruby
def resolve(object:, arguments:, **rest)
  # yield the current time as `memo`
  yield(object, arguments, Time.now.to_i)
end
```

If a third value is yielded, it will be passed to `after_resolve` as `memo:`, for example:

```ruby
def after_resolve(value:, memo:, **rest)
  puts "Elapsed: #{Time.now.to_i - memo}"
  # Return the original value
  value
end
```

This allows the `resolve` hook to pass data to `after_resolve`.

Instance variables may not be used because, in a given GraphQL query, the same field may be resolved several times concurrently, and that would result in overriding the instance variable in an unpredictable way. (In fact, extensions are frozen to prevent instance variable writes.)

## Extension options

The `extension(...)` method takes an optional second argument, for example:

```ruby
extension(LimitExtension, limit: 20)
```

In this case, `{limit: 20}` will be passed as `options:` to `#initialize` and `options[:limit]` will be `20`.

For example, options can be used for modifying execution:

```ruby
def after_resolve(value:, **rest)
  # Apply the limit from the options, a readable attribute on the class
  value.limit(options[:limit])
end
```

If you use the `extensions: [...]` option, you can pass options using a hash:

```ruby
field :name, String, null: false, extensions: [LimitExtension => { limit: 20 }]
```

## Using `extras`

Extensions can have the same `extras` as fields (see [Extra Field Metadata](/fields/introduction#extra-field-metadata)). Add them by calling `extras` in the class definition:

```ruby
class MyExtension < GraphQL::Schema::FieldExtension
  extras [:ast_node, :errors, ...]
end
```

Any configured `extras` will be present in the given `arguments`, but removed before the field is resolved. (However, `extras` from _any_ extension will be present in `arguments` for _all_ extensions.)

## Adding an extension by default

If you want to apply an extension to _all_ your fields, you can do this in your [BaseField](/type_definitions/extensions.html#customizing-fields)'s `def initialize`, for example:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  def initialize(*args, **kwargs, &block)
    super
    # Add this to all fields based on this class:
    extension(MyDefaultExtension)
  end
end
```

You can also _conditionally_ apply extensions in `def initialize` by adding keywords to the method definition, for example:

```ruby
class Types::BaseField < GraphQL::Schema::Field
  # @param custom_extension [Boolean] if false, `MyCustomExtension` won't be added
  # @example skipping `MyCustomExtension`
  #   field :no_extension, String, custom_extension: false
  def initialize(*args, custom_extension: true, **kwargs, &block)
    super(*args, **kwargs, &block)
    # Don't apply this extension if the field is configured with `custom_extension: false`:
    if custom_extension
      extension(MyCustomExtensions)
    end
  end
end
```

---

--- METRICS ---
- Files Extracted: 121
- Estimated Token Budget: ~125773 tokens
- Canonical Reference: https://codewiki.google/github.com/rmosolgo/graphql-ruby
