lighthouse-graphql 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +212 -1
- data/README.md +38 -22
- data/docs/best-practices.md +6 -4
- data/docs/caching.md +4 -3
- data/docs/configuration.md +20 -26
- data/docs/custom-directives.md +2 -1
- data/docs/directives.md +79 -14
- data/docs/federation.md +1 -1
- data/docs/filtering-and-ordering.md +330 -30
- data/docs/generators.md +42 -40
- data/docs/getting-started.md +128 -58
- data/docs/motivation.md +3 -2
- data/docs/performance.md +5 -4
- data/docs/relationships.md +7 -5
- data/docs/relay.md +63 -0
- data/docs/resolvers.md +92 -39
- data/docs/schema-organisation.md +177 -0
- data/docs/security.md +64 -16
- data/docs/subscriptions.md +16 -9
- data/docs/types.md +16 -17
- data/docs/uploads.md +41 -14
- data/lib/generators/lighthouse/base_classes.rb +9 -14
- data/lib/generators/lighthouse/install/graphql_ruby_install.rb +113 -0
- data/lib/generators/lighthouse/install/install_generator.rb +125 -99
- data/lib/generators/lighthouse/install/templates/graphql.config.yml.tt +2 -1
- data/lib/generators/lighthouse/install/templates/initializer.rb.tt +14 -13
- data/lib/lighthouse/ast_builder.rb +124 -0
- data/lib/lighthouse/caching.rb +9 -3
- data/lib/lighthouse/config.rb +27 -25
- data/lib/lighthouse/contracts/arg_manipulator.rb +9 -5
- data/lib/lighthouse/contracts/field_builder.rb +13 -0
- data/lib/lighthouse/controller.rb +28 -5
- data/lib/lighthouse/directive_resolvers/aggregate_resolver.rb +1 -2
- data/lib/lighthouse/directive_resolvers/all_resolver.rb +0 -2
- data/lib/lighthouse/directive_resolvers/argument_transforms_resolver.rb +18 -18
- data/lib/lighthouse/directive_resolvers/association_resolvers.rb +9 -6
- data/lib/lighthouse/directive_resolvers/base.rb +6 -37
- data/lib/lighthouse/directive_resolvers/builder_applier.rb +21 -6
- data/lib/lighthouse/directive_resolvers/cache_resolvers.rb +1 -1
- data/lib/lighthouse/directive_resolvers/count_resolver.rb +0 -1
- data/lib/lighthouse/directive_resolvers/first_resolver.rb +0 -2
- data/lib/lighthouse/directive_resolvers/global_id_resolver.rb +3 -1
- data/lib/lighthouse/directive_resolvers/paginate_resolver.rb +2 -3
- data/lib/lighthouse/directive_resolvers/throttle_resolver.rb +85 -26
- data/lib/lighthouse/directive_resolvers/type_method_resolver.rb +12 -6
- data/lib/lighthouse/directives/arguments/more_filters.rb +1 -1
- data/lib/lighthouse/directives/arguments/order_by.rb +190 -26
- data/lib/lighthouse/directives/arguments/where_conditions.rb +25 -0
- data/lib/lighthouse/directives/arguments/where_conditions_base.rb +84 -0
- data/lib/lighthouse/directives/arguments/where_has_conditions.rb +56 -0
- data/lib/lighthouse/directives/definitions.rb +0 -1
- data/lib/lighthouse/directives/generated_types.rb +103 -0
- data/lib/lighthouse/directives/registry.rb +39 -10
- data/lib/lighthouse/document_ast.rb +111 -0
- data/lib/lighthouse/field_resolver_stack.rb +9 -6
- data/lib/lighthouse/global_id.rb +29 -3
- data/lib/lighthouse/graphql/version.rb +1 -1
- data/lib/lighthouse/graphql_ruby_patches.rb +45 -0
- data/lib/lighthouse/ide_helper.rb +80 -23
- data/lib/lighthouse/namespaces.rb +1 -0
- data/lib/lighthouse/order_by/types.rb +137 -0
- data/lib/lighthouse/railtie.rb +9 -2
- data/lib/lighthouse/rb_lighthouse.rb +16 -5
- data/lib/lighthouse/resolve_info.rb +5 -5
- data/lib/lighthouse/resolver_bases.rb +18 -4
- data/lib/lighthouse/resolver_dispatch.rb +16 -1
- data/lib/lighthouse/root_types.rb +117 -0
- data/lib/lighthouse/schema_cache.rb +8 -4
- data/lib/lighthouse/schema_factory.rb +6 -9
- data/lib/lighthouse/schema_generator.rb +8 -18
- data/lib/lighthouse/schema_stitcher.rb +64 -0
- data/lib/lighthouse/support/naming.rb +0 -12
- data/lib/lighthouse/upload.rb +4 -2
- data/lib/lighthouse/upload_store.rb +39 -14
- data/lib/lighthouse/where_conditions/columns.rb +36 -0
- data/lib/lighthouse/where_conditions/handler.rb +152 -0
- data/lib/lighthouse/where_conditions/json_column.rb +99 -0
- data/lib/lighthouse/where_conditions/operator_map.rb +1 -4
- data/lib/lighthouse/where_conditions/types.rb +110 -0
- data/sig/lighthouse/directives.rbs +16 -3
- data/sig/lighthouse/graphql.rbs +48 -4
- data/sig/lighthouse.rbs +8 -12
- metadata +17 -7
- data/lib/generators/lighthouse/install/templates/schema.rb.tt +0 -29
- data/lib/generators/lighthouse/mutation_generator.rb +0 -31
- data/lib/generators/lighthouse/query_generator.rb +0 -30
- data/lib/lighthouse/directive_resolvers/where_conditions_applier.rb +0 -170
- data/lib/lighthouse/directives/where_conditions_directive.rb +0 -244
- data/lib/lighthouse/sdl_loader.rb +0 -22
data/docs/getting-started.md
CHANGED
|
@@ -24,29 +24,32 @@ a hard dependency of Lighthouse so non-federated apps stay lean.
|
|
|
24
24
|
bin/rails generate lighthouse:install
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
does steps 1–5 below for you. It
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
does steps 1–5 below for you. It builds on graphql-ruby's own install,
|
|
28
|
+
`rails g graphql:install`, with whichever of its options the app chose (`--relay`,
|
|
29
|
+
`--batch`, `--api`, ...). When the app has no graphql-ruby schema class yet, it runs
|
|
30
|
+
`graphql:install` first, with graphql-ruby's defaults. Then it adapts what
|
|
31
|
+
graphql-ruby wrote, and adds Lighthouse's own:
|
|
30
32
|
|
|
31
33
|
| File | What it is |
|
|
32
34
|
| --- | --- |
|
|
35
|
+
| `app/graphql/app_schema.rb` | graphql-ruby's schema class, now built by Lighthouse from the SDL on every class graphql-ruby wrote (`base_types`). The rest of the class is graphql-ruby's |
|
|
36
|
+
| `app/graphql/types/query_type.rb`, `mutation_type.rb` | graphql-ruby's root types, which the SDL's `Query` and `Mutation` are built on. Their example `test_field` goes; Relay's `node` / `nodes` stay |
|
|
37
|
+
| `app/graphql/types/base_*.rb`, `resolvers/base_resolver.rb`, `mutations/base_mutation.rb` | graphql-ruby's base classes. `BaseObject`, `BaseInterface`, `BaseUnion` and `BaseScalar` include `Lighthouse::GraphQL::Type` |
|
|
38
|
+
| `app/controllers/graphql_controller.rb` | graphql-ruby's controller, replaced by one including `Lighthouse::GraphQL::Controller` |
|
|
33
39
|
| `config/initializers/lighthouse.rb` | Every [configuration](configuration.md) option, commented out at its default |
|
|
34
|
-
| `graphql/schema.graphql` |
|
|
35
|
-
| `app/graphql/app_schema.rb` | The executable schema, built from the SDL, with `GraphQL::Dataloader` enabled and the base classes in `base_types` |
|
|
36
|
-
| `app/graphql/{types,resolvers,mutations,subscriptions}/base_*.rb` | graphql-ruby's base classes, and Lighthouse's `Subscriptions::BaseSubscription` |
|
|
37
|
-
| `app/controllers/graphql_controller.rb` | The endpoint, plus a `post '/graphql'` route |
|
|
40
|
+
| `graphql/schema.graphql` | The schema's entrypoint: a starter schema (`user` via `@find`, `users` via `@paginate`) that assumes a `User` model. Skipped when the file exists |
|
|
38
41
|
| `graphql.config.yml` | IDE configuration — see [IDE support](#ide-support) |
|
|
39
42
|
|
|
40
|
-
`--schema=ApiSchema`, `--
|
|
41
|
-
`--skip-ide` change what it
|
|
42
|
-
controller the app
|
|
43
|
+
`--schema=ApiSchema`, `--schema-path=app/schema/schema.graphql`, `--subscriptions`,
|
|
44
|
+
`--skip-controller` and `--skip-ide` change what it does. Rerunning it changes
|
|
45
|
+
nothing, and a controller the app changed is kept.
|
|
43
46
|
[generators.md](generators.md) has the details, every generator Lighthouse has, and
|
|
44
47
|
which of graphql-ruby's `graphql:*` generators fit a Lighthouse app.
|
|
45
48
|
|
|
46
49
|
## 1. Write your schema (SDL)
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
Lighthouse reads the schema from one entrypoint, `RAILS_ROOT/graphql/schema.graphql`
|
|
52
|
+
(`config.schema_path`), as PHP Lighthouse does:
|
|
50
53
|
|
|
51
54
|
```graphql
|
|
52
55
|
# graphql/schema.graphql
|
|
@@ -67,29 +70,64 @@ argument (the `@eq` makes `id` a filter). Lighthouse infers the model from the
|
|
|
67
70
|
field's return type name (`User`), or you can be explicit with
|
|
68
71
|
`@all(model: "User")`.
|
|
69
72
|
|
|
73
|
+
To split the schema, the entrypoint imports the other files. A type is defined
|
|
74
|
+
once, and the other files add to it with `extend type`:
|
|
75
|
+
|
|
76
|
+
```graphql
|
|
77
|
+
# graphql/schema.graphql
|
|
78
|
+
type Query
|
|
79
|
+
|
|
80
|
+
#import user.graphql
|
|
81
|
+
#import blog/*.graphql
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```graphql
|
|
85
|
+
# graphql/user.graphql
|
|
86
|
+
type User {
|
|
87
|
+
id: ID!
|
|
88
|
+
name: String
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
extend type Query {
|
|
92
|
+
users: [User!]! @all
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A file nothing imports is not part of the schema, and a type defined twice is an
|
|
97
|
+
error. See [schema-organisation.md](schema-organisation.md).
|
|
98
|
+
|
|
70
99
|
## 2. Provide your base classes
|
|
71
100
|
|
|
72
101
|
Lighthouse builds on graphql-ruby, and follows its convention: each kind of class
|
|
73
102
|
inherits from a base class in its own namespace, where what they all share goes
|
|
74
|
-
once. graphql-ruby's `graphql:install` writes them, and `lighthouse:install`
|
|
75
|
-
|
|
76
|
-
none of, and make the mutation base a `GraphQL::Schema::Mutation` (the SDL declares a
|
|
77
|
-
mutation's arguments, where graphql-ruby's `RelayClassicMutation` takes one `input:`):
|
|
103
|
+
once. graphql-ruby's `graphql:install` writes them, and `lighthouse:install` builds
|
|
104
|
+
on them. By hand, run graphql-ruby's install with its defaults:
|
|
78
105
|
|
|
79
106
|
```shell
|
|
80
|
-
bin/rails generate graphql:install
|
|
107
|
+
bin/rails generate graphql:install
|
|
81
108
|
```
|
|
82
109
|
|
|
110
|
+
Then include `Lighthouse::GraphQL::Type` in the base object, interface, union and
|
|
111
|
+
scalar, so every type class that inherits from them lends its methods to the SDL
|
|
112
|
+
type of its name (see [type classes](resolvers.md#type-classes)):
|
|
113
|
+
|
|
83
114
|
```ruby
|
|
84
|
-
# app/graphql/
|
|
85
|
-
module
|
|
86
|
-
class
|
|
87
|
-
|
|
115
|
+
# app/graphql/types/base_object.rb
|
|
116
|
+
module Types
|
|
117
|
+
class BaseObject < GraphQL::Schema::Object
|
|
118
|
+
include Lighthouse::GraphQL::Type
|
|
119
|
+
edge_type_class(Types::BaseEdge)
|
|
120
|
+
connection_type_class(Types::BaseConnection)
|
|
88
121
|
field_class Types::BaseField
|
|
89
|
-
|
|
122
|
+
# include ApolloFederation::Object # if you use federation
|
|
90
123
|
end
|
|
91
124
|
end
|
|
125
|
+
```
|
|
92
126
|
|
|
127
|
+
Add the subscription base, which graphql-ruby has none of, if the schema has a
|
|
128
|
+
`Subscription` type (`lighthouse:install --subscriptions` writes it):
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
93
131
|
# app/graphql/subscriptions/base_subscription.rb — Subscription fields
|
|
94
132
|
module Subscriptions
|
|
95
133
|
class BaseSubscription < GraphQL::Schema::Subscription
|
|
@@ -98,62 +136,85 @@ module Subscriptions
|
|
|
98
136
|
end
|
|
99
137
|
```
|
|
100
138
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```ruby
|
|
106
|
-
# app/graphql/types/base_object.rb
|
|
107
|
-
module Types
|
|
108
|
-
class BaseObject < GraphQL::Schema::Object
|
|
109
|
-
field_class Types::BaseField
|
|
110
|
-
# include ApolloFederation::Object # if you use federation
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
```
|
|
139
|
+
graphql-ruby's `Mutations::BaseMutation` stays a `RelayClassicMutation`. A
|
|
140
|
+
`Mutation` field that declares its arguments one by one calls it as a plain
|
|
141
|
+
mutation, with those arguments as keywords; one with a single `input:` argument
|
|
142
|
+
goes through `RelayClassicMutation`'s own `input:` handling.
|
|
114
143
|
|
|
115
144
|
## 3. Build the schema
|
|
116
145
|
|
|
146
|
+
graphql-ruby's schema class becomes Lighthouse's: it inherits from the schema
|
|
147
|
+
`get_schema` builds from the SDL, instead of `GraphQL::Schema`.
|
|
148
|
+
|
|
117
149
|
```ruby
|
|
118
150
|
# app/graphql/app_schema.rb
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
AppSchema = Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
122
|
-
sdl_folder: Rails.root.join('graphql').to_s,
|
|
151
|
+
class AppSchema < Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
123
152
|
options: {
|
|
124
153
|
base_types: {
|
|
154
|
+
query_type: Types::QueryType,
|
|
155
|
+
mutation_type: Types::MutationType,
|
|
156
|
+
node: Types::NodeType,
|
|
125
157
|
resolver: Resolvers::BaseResolver,
|
|
126
158
|
mutation: Mutations::BaseMutation,
|
|
127
159
|
subscription: Subscriptions::BaseSubscription,
|
|
128
160
|
object: Types::BaseObject,
|
|
129
|
-
|
|
161
|
+
interface: Types::BaseInterface,
|
|
162
|
+
union: Types::BaseUnion,
|
|
163
|
+
scalar: Types::BaseScalar,
|
|
164
|
+
enum: Types::BaseEnum,
|
|
165
|
+
input_object: Types::BaseInputObject
|
|
130
166
|
}
|
|
131
167
|
}
|
|
132
168
|
)
|
|
169
|
+
# Lighthouse batches relationship loads through graphql-ruby's Dataloader.
|
|
170
|
+
use GraphQL::Dataloader
|
|
171
|
+
|
|
172
|
+
max_depth(15)
|
|
173
|
+
max_query_string_tokens(5000)
|
|
174
|
+
validate_max_errors(100)
|
|
175
|
+
|
|
176
|
+
# Relay-style Object Identification, as graphql-ruby's install writes it:
|
|
177
|
+
def self.id_from_object(object, type_definition, query_ctx)
|
|
178
|
+
object.to_gid_param
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def self.object_from_id(global_id, query_ctx)
|
|
182
|
+
GlobalID.find(global_id)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
133
185
|
```
|
|
134
186
|
|
|
187
|
+
The SDL is read from `config.schema_path` and the files it imports
|
|
188
|
+
(`get_schema(schema_path: ...)` overrides it). The class body is graphql-ruby's, so plugins and settings go there as in any
|
|
189
|
+
graphql-ruby schema (`use ...`, `max_complexity(...)`), but for `query(...)` and
|
|
190
|
+
`mutation(...)`, which the SDL's root types replace, and `resolve_type`, which
|
|
191
|
+
Lighthouse answers (see [types.md](types.md)).
|
|
192
|
+
|
|
135
193
|
`object:` and the other SDL kinds go to graphql-ruby, which builds the SDL's types
|
|
136
|
-
from them. `
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
194
|
+
from them. `query_type:` and `mutation_type:` are the classes the SDL's `Query` and
|
|
195
|
+
`Mutation` are built on, so their own `field`s (Relay's `node` / `nodes`) are the
|
|
196
|
+
schema's too; `node:` is the SDL's `Node` interface (see [relay.md](relay.md)).
|
|
197
|
+
`resolver:`, `mutation:` and `subscription:` are Lighthouse's: every class a
|
|
198
|
+
`Query`, `Mutation` or `Subscription` field resolves to must inherit from its kind's
|
|
199
|
+
base, and a `Subscription` field with no class of its own resolves with the
|
|
200
|
+
subscription base. See [resolvers.md](resolvers.md#base-classes).
|
|
140
201
|
|
|
141
|
-
|
|
142
|
-
subclass further (e.g. to add federation, tracing, a Dataloader):
|
|
202
|
+
A constant works as well, with the settings passed as options or set on it:
|
|
143
203
|
|
|
144
204
|
```ruby
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
205
|
+
AppSchema = Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
206
|
+
options: { base_types: { object: Types::BaseObject }, using: { GraphQL::Dataloader => {} } }
|
|
207
|
+
)
|
|
148
208
|
```
|
|
149
209
|
|
|
150
|
-
> Lighthouse uses graphql-ruby's `Dataloader` to batch relationship loads.
|
|
151
|
-
> `use GraphQL::Dataloader`
|
|
210
|
+
> Lighthouse uses graphql-ruby's `Dataloader` to batch relationship loads. Keep
|
|
211
|
+
> `use GraphQL::Dataloader` in your schema to get N+1-free relationships.
|
|
152
212
|
|
|
153
213
|
## 4. Serve it
|
|
154
214
|
|
|
155
|
-
`rails g lighthouse:install`
|
|
156
|
-
`Lighthouse::GraphQL::Controller`, a concern that
|
|
215
|
+
`rails g lighthouse:install` replaces graphql-ruby's controller (when the app has
|
|
216
|
+
not changed it) with one including `Lighthouse::GraphQL::Controller`, a concern that
|
|
217
|
+
serves the schema:
|
|
157
218
|
|
|
158
219
|
```ruby
|
|
159
220
|
class GraphqlController < ApplicationController
|
|
@@ -225,7 +286,9 @@ Your IDE validates and autocompletes `.graphql` files against the schema it can
|
|
|
225
286
|
see. Lighthouse's directive definitions and the types its manipulation phase
|
|
226
287
|
generates live in Ruby, so without help the IDE flags `@paginate` as unknown and
|
|
227
288
|
can't resolve `UserPaginator`. PHP Lighthouse solves this with an IDE helper
|
|
228
|
-
command
|
|
289
|
+
command, and so does Lighthouse for Rails. In development, an app with a
|
|
290
|
+
`graphql.config.yml` gets the files rewritten on boot and on every reload
|
|
291
|
+
(`config.ide_helper`), so you never run it by hand there. Elsewhere:
|
|
229
292
|
|
|
230
293
|
```shell
|
|
231
294
|
bin/rails lighthouse:ide_helper # writes to Rails.root
|
|
@@ -239,8 +302,11 @@ It writes two files:
|
|
|
239
302
|
declares (so the IDE never sees a duplicate).
|
|
240
303
|
- **`programmatic-types.graphql`** — the types Lighthouse adds to the executable
|
|
241
304
|
schema: `<Type>Paginator`, `<Type>SimplePaginator`, `<Type>Connection`/`Edge`,
|
|
242
|
-
`PaginatorInfo`, `SimplePaginatorInfo`, `PageInfo`, the
|
|
243
|
-
inputs and enums, and the `_` placeholder scalar
|
|
305
|
+
`PaginatorInfo`, `SimplePaginatorInfo`, `PageInfo`, the `@whereConditions` and
|
|
306
|
+
`@orderBy` inputs and enums, and the `_` placeholder scalar (without it, the
|
|
307
|
+
IDE reports `Unknown type "_"`). It leaves out every type the SDL or
|
|
308
|
+
`schema-directives.graphql` defines, because graphql-js, which the IDE plugins
|
|
309
|
+
use, rejects a type defined twice.
|
|
244
310
|
|
|
245
311
|
Point your IDE's [graphql-config](https://the-guild.dev/graphql/config) at them
|
|
246
312
|
next to your SDL — the install generator writes this `graphql.config.yml`:
|
|
@@ -254,7 +320,9 @@ schema:
|
|
|
254
320
|
|
|
255
321
|
It works with the GraphQL plugin for JetBrains IDEs and the GraphQL extension for
|
|
256
322
|
VS Code. The files are derived from your schema, so the generator adds them to
|
|
257
|
-
`.gitignore
|
|
323
|
+
`.gitignore`. Outside development, rerun the task after changing the schema or
|
|
324
|
+
registering a directive. The IDE reads every `.graphql` file the glob matches, as
|
|
325
|
+
one schema: it does not follow `#import`.
|
|
258
326
|
|
|
259
327
|
> The IDE reads fields as you wrote them: `users: [User!]! @paginate` still looks
|
|
260
328
|
> like a list to it, although clients query `users { data { … } }`. Field
|
|
@@ -286,7 +354,9 @@ When a field needs Ruby, write a graphql-ruby resolver or mutation class named
|
|
|
286
354
|
after it — `Query.latestPosts` resolves to `Resolvers::LatestPosts`
|
|
287
355
|
(`app/graphql/resolvers/latest_posts.rb`, a `Resolvers::BaseResolver`),
|
|
288
356
|
`Mutation.publishPost` to `Mutations::PublishPost` (a `Mutations::BaseMutation`).
|
|
289
|
-
|
|
357
|
+
A method of the root type class comes first, as in graphql-ruby: `Query.latestPosts`
|
|
358
|
+
calls `Types::QueryType#latest_posts` when it defines one. `bin/rails generate
|
|
359
|
+
graphql:mutation PublishPost` writes a code-first mutation. See
|
|
290
360
|
[resolvers.md](resolvers.md).
|
|
291
361
|
|
|
292
362
|
## Next steps
|
data/docs/motivation.md
CHANGED
|
@@ -98,8 +98,9 @@ generated from Ruby classes:
|
|
|
98
98
|
- **It is designed before it is implemented.** You can agree on the types and fields
|
|
99
99
|
first, as a team, and the server follows the design rather than leaking its internals.
|
|
100
100
|
- **Tools understand it directly.** IDE plugins, code generators and schema registries
|
|
101
|
-
read SDL. Lighthouse
|
|
102
|
-
|
|
101
|
+
read SDL. Lighthouse writes out its directives and generated types (in development
|
|
102
|
+
on every reload, or with `lighthouse:ide_helper`), so editors validate and
|
|
103
|
+
autocomplete the schema.
|
|
103
104
|
- **It says what each field does.** `posts: [Post!]! @paginate @guard @cacheControl(maxAge: 60)`
|
|
104
105
|
tells a reader, without opening any Ruby, that the field is paginated, requires a
|
|
105
106
|
signed-in user and can be cached for a minute.
|
data/docs/performance.md
CHANGED
|
@@ -59,10 +59,11 @@ Lighthouse.configure { |config| config.watch_sdl = Rails.env.development? }
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
Rails' reloader watches Ruby files, not `.graphql` ones, so without this an SDL
|
|
62
|
-
edit only shows up after restarting the server. With it, the
|
|
63
|
-
`config.watchable_dirs`, and an edit
|
|
64
|
-
the
|
|
65
|
-
|
|
62
|
+
edit only shows up after restarting the server. With it, the folder of
|
|
63
|
+
`config.schema_path` joins `config.watchable_dirs`, and an edit to any `.graphql`
|
|
64
|
+
file below it reloads the app, and with it the schema, on the next request, like
|
|
65
|
+
a Ruby change. The reload also rewrites the IDE helper files (`config.ide_helper`). This relies on the schema class living
|
|
66
|
+
in an autoloaded file, as `app/graphql/app_schema.rb` does.
|
|
66
67
|
|
|
67
68
|
## Persisted queries
|
|
68
69
|
|
data/docs/relationships.md
CHANGED
|
@@ -39,12 +39,13 @@ aliases, so an SDL written for it builds unchanged.
|
|
|
39
39
|
|
|
40
40
|
The relationship directives load through graphql-ruby's **Dataloader** and
|
|
41
41
|
ActiveRecord's preloader, one query per association per batch. Enable the
|
|
42
|
-
Dataloader on your schema (
|
|
42
|
+
Dataloader on your schema (graphql-ruby's install does, and with `--batch`
|
|
43
|
+
`lighthouse:install` adds it beside GraphQL::Batch):
|
|
43
44
|
|
|
44
45
|
```ruby
|
|
45
|
-
AppSchema
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
class AppSchema < Lighthouse::GraphQL::RbLightHouse.get_schema(options: { base_types: { object: Types::BaseObject } })
|
|
47
|
+
use GraphQL::Dataloader
|
|
48
|
+
end
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
A query for 100 posts and their authors then issues two queries, not 101. A
|
|
@@ -173,7 +174,8 @@ queries:
|
|
|
173
174
|
type User {
|
|
174
175
|
publishedPosts(
|
|
175
176
|
title: String @like
|
|
176
|
-
orderBy:
|
|
177
|
+
orderBy: _ @orderBy(columns: ["title", "created_at"])
|
|
178
|
+
where: _ @whereConditions(columns: ["title"])
|
|
177
179
|
): [Post!]! @hasMany(relation: "posts", scopes: ["published"])
|
|
178
180
|
}
|
|
179
181
|
```
|
data/docs/relay.md
CHANGED
|
@@ -6,6 +6,11 @@ does, with two directives: `@globalId` encodes and decodes the ids, and `@node`
|
|
|
6
6
|
makes a type findable by them. For Relay's cursor pagination, see
|
|
7
7
|
[`@paginate(type: CONNECTION)`](directives.md#paginate).
|
|
8
8
|
|
|
9
|
+
An app whose schema has graphql-ruby's Relay, as `rails g graphql:install` writes it
|
|
10
|
+
by default, uses the same directives on graphql-ruby's ids and `Node`: see
|
|
11
|
+
[Relay through graphql-ruby](#relay-through-graphql-ruby). The sections before it
|
|
12
|
+
describe a schema without it.
|
|
13
|
+
|
|
9
14
|
## Global ids
|
|
10
15
|
|
|
11
16
|
`@globalId` on a field encodes its value with the type's name:
|
|
@@ -125,6 +130,64 @@ type Query {
|
|
|
125
130
|
}
|
|
126
131
|
```
|
|
127
132
|
|
|
133
|
+
## Relay through graphql-ruby
|
|
134
|
+
|
|
135
|
+
graphql-ruby's install (`--relay`, its default) writes Relay's side in Ruby:
|
|
136
|
+
`Types::NodeType`, the `node` and `nodes` fields of `Types::QueryType`, and the
|
|
137
|
+
schema's `id_from_object` / `object_from_id`, on Rails' GlobalID:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
class AppSchema < Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
141
|
+
options: { base_types: { query_type: Types::QueryType, node: Types::NodeType, object: Types::BaseObject } }
|
|
142
|
+
)
|
|
143
|
+
def self.id_from_object(object, type_definition, query_ctx)
|
|
144
|
+
object.to_gid_param
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def self.object_from_id(global_id, query_ctx)
|
|
148
|
+
GlobalID.find(global_id)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`lighthouse:install` names those classes in `base_types`, and Lighthouse's
|
|
154
|
+
directives then work on them. The schema has one kind of global id and one `Node`:
|
|
155
|
+
|
|
156
|
+
- `@globalId` on a field that resolves an object's own id encodes it with
|
|
157
|
+
`id_from_object`: `Tag.id` answers `tag.to_gid_param`, as graphql-ruby's `Node.id`
|
|
158
|
+
does. Any other value is encoded as before.
|
|
159
|
+
- `@globalId` on an argument decodes with `object_from_id`, which loads the object.
|
|
160
|
+
`ID` gives its id, and `ARRAY` / `TYPE` its class name (`["Tag", 1]`, `"Tag"`).
|
|
161
|
+
- `node:` is the SDL's `Node` interface: `interface Node`, which `@node` adds, and
|
|
162
|
+
`implements Node` refer to `Types::NodeType`. A `@node` type implements it.
|
|
163
|
+
- `Query.node` and `nodes` are graphql-ruby's code-first fields of
|
|
164
|
+
`Types::QueryType`, which find objects with `object_from_id`. The `node` field
|
|
165
|
+
`@node` would add to `Query` gives way to them.
|
|
166
|
+
|
|
167
|
+
```graphql
|
|
168
|
+
type Tag @node {
|
|
169
|
+
id: ID! @globalId
|
|
170
|
+
name: String!
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
type Query {
|
|
174
|
+
tag(id: ID! @globalId(decode: ID) @eq): Tag @find
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```graphql
|
|
179
|
+
{
|
|
180
|
+
node(id: "Z2lkOi8vYXBwL1RhZy8x") { id ... on Tag { name } }
|
|
181
|
+
tag(id: "Z2lkOi8vYXBwL1RhZy8x") { name }
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`Tag.id` and both arguments carry `Tag` 1's GlobalID (`gid://app/Tag/1`, in base64).
|
|
186
|
+
`Query.node` finds every object with `object_from_id`, so `@node(resolver:)` and
|
|
187
|
+
`@node(model:)` play no part in it, and [`global_id`](configuration.md#global_id)
|
|
188
|
+
encodes only what `id_from_object` does not. A schema without `id_from_object` /
|
|
189
|
+
`object_from_id` works as the sections above describe.
|
|
190
|
+
|
|
128
191
|
## Your own encoding
|
|
129
192
|
|
|
130
193
|
[`global_id`](configuration.md#global_id) takes any object with
|