File: setup.md

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (169 lines) | stat: -rw-r--r-- 5,421 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
---
layout: guide
doc_stub: false
search: true
section: GraphQL Pro - Defer
title: Server Setup
desc: Configuring the schema and server to use @defer
index: 1
pro: true
---

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 {% internal_link "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 "Server support for @defer with ..." %} 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 {% internal_link "client usage", "/defer/usage" %} of `@defer`.