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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eec6f789caae61cc196024238623ebc042783884cf8ebb53b19bb0e3f6d2cf3f
|
|
4
|
+
data.tar.gz: 61bdd5cef3df0d2c4ac9701501576c96a9c207d81b5f2f976d22e5fee14df9ba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 11c76fea28a691e29eea4910674585891907cefbeef72e9efc7519b8f812cd26f39d6895c90a46c91259b53c0dc64e3a0437add651d9eed07e5cd74807f245f2
|
|
7
|
+
data.tar.gz: 530507331c8736355448b67e1cf6ac354a0745351038fc9e7f8535df47f30299e2cf4ea203bee15482bc3fbcf3b8f9d0e5d7e72c61f263f0d9759016ae210a10
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,215 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.7.0] - 2026-10-18
|
|
10
|
+
|
|
11
|
+
The schema is written by PHP Lighthouse's rules now: one entrypoint that imports
|
|
12
|
+
the other files, every type defined once, and `extend type` to add to it.
|
|
13
|
+
`@orderBy`, `@whereConditions` and `@whereHasConditions` generate and apply what
|
|
14
|
+
PHP Lighthouse's do. In development, the IDE helper files are rewritten
|
|
15
|
+
automatically.
|
|
16
|
+
|
|
17
|
+
**Upgrading from 0.6:**
|
|
18
|
+
|
|
19
|
+
1. Replace `config.sdl_folder = ...` with `config.schema_path = Rails.root.join('graphql/schema.graphql').to_s`
|
|
20
|
+
(that is the default), and `get_schema(sdl_folder:)` with `get_schema(schema_path:)`.
|
|
21
|
+
2. Import every other file from `graphql/schema.graphql`. `#import **/*.graphql` imports
|
|
22
|
+
every file below its folder, which matches what was loaded before.
|
|
23
|
+
3. Turn a second `type Query { ... }` (or `type Mutation`, or any type defined in two files)
|
|
24
|
+
into `extend type Query { ... }`. A type defined twice now fails the build.
|
|
25
|
+
4. Write `orderBy:` arguments as `orderBy: _ @orderBy(columns: [...])`, and drop the
|
|
26
|
+
`OrderByClause` inputs and the `SortOrder` enum you declared. `order` is required in a
|
|
27
|
+
clause, and `{ field:, direction: }` clauses are no longer accepted.
|
|
28
|
+
5. `SQLOperator` has PHP Lighthouse's values. Replace `NIN` with `NOT_IN`, and
|
|
29
|
+
`CONTAINS` / `STARTS_WITH` / `ENDS_WITH` with `LIKE` and a `%` pattern
|
|
30
|
+
(`value: "%Rails%"`, `"Rails%"`, `"%Rails"`). `@where(operator:)` keeps its operators.
|
|
31
|
+
6. A `HAS` condition is on the related records' columns (`…HasCondition`, a `String`
|
|
32
|
+
column), as in PHP Lighthouse, no longer on the parent's column enum.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **`#import`.** The schema's entrypoint, `config.schema_path`, imports other files by a
|
|
37
|
+
path relative to the importing file. Globs (`#import blog/*.graphql`) work too. An
|
|
38
|
+
imported file can import more files, and each file is read once.
|
|
39
|
+
- **Type extensions merged as PHP Lighthouse merges them.** `extend type`, `input`,
|
|
40
|
+
`interface`, `enum`, `union` and `scalar` add to their definition, and the
|
|
41
|
+
directives on their fields work as on the type's own. `extend type Query` alone
|
|
42
|
+
defines `Query`.
|
|
43
|
+
- **`@orderBy` generates its types.** `orderBy: _ @orderBy(columns: [...])` gets
|
|
44
|
+
`[<Type><Field><Arg>OrderByClause!]`, a column enum (`@enum(value:)` per column)
|
|
45
|
+
and `SortOrder`. `columnsEnum:` reuses an enum, and without columns the shared
|
|
46
|
+
`OrderByClause` takes any column.
|
|
47
|
+
- **`@orderBy(relations:)`.** Clients order by `COUNT` of a relation, or by
|
|
48
|
+
`AVG`/`MIN`/`MAX`/`SUM` of one of its columns
|
|
49
|
+
(`{ posts: { aggregate: COUNT }, order: DESC }`). Records without related ones
|
|
50
|
+
are ordered too, and this works on batched relationship pages.
|
|
51
|
+
- **`@orderBy(column:, direction:)` on a field.** It always orders the result, after
|
|
52
|
+
the client's clauses.
|
|
53
|
+
- **`@whereConditions` and `@whereHasConditions`, as in PHP Lighthouse.** They have
|
|
54
|
+
PHP Lighthouse's `SQLOperator` (`LIKE`, `NOT_LIKE`, `NOT_IN`, `BETWEEN`,
|
|
55
|
+
`NOT_BETWEEN`, ...), and `HAS` with `operator:` / `amount:`
|
|
56
|
+
(`{ relation: "posts", operator: LT, amount: 1 }`: the records with none). The
|
|
57
|
+
`…Relation` / `…HasCondition` inputs filter on the related table, and
|
|
58
|
+
`handler:` names a class that applies the conditions instead.
|
|
59
|
+
- **JSON columns.** `@whereConditions`, `@whereHasConditions` and `@orderBy` take a path into a
|
|
60
|
+
JSON column, `meta->author->name` or `meta->tags->0`, on PostgreSQL, MySQL and SQLite.
|
|
61
|
+
Numbers compare and sort as numbers, booleans compare as booleans, and `IS_NULL` holds
|
|
62
|
+
for a JSON `null` too.
|
|
63
|
+
- **`config.ide_helper`.** `schema-directives.graphql` and
|
|
64
|
+
`programmatic-types.graphql` are rewritten on boot and on every reload. By
|
|
65
|
+
default this happens in development when the app has a `graphql.config.yml`.
|
|
66
|
+
- `Contracts::FieldBuilder`, and `Contracts::ArgManipulator` as `ASTBuilder` calls
|
|
67
|
+
it, for custom directives.
|
|
68
|
+
- The guide [docs/schema-organisation.md](docs/schema-organisation.md).
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- **Breaking:** The schema is read from `config.schema_path` and its imports, not from every
|
|
73
|
+
file in `config.sdl_folder`, which is removed. `lighthouse:install` takes `--schema-path`
|
|
74
|
+
instead of `--sdl-folder`.
|
|
75
|
+
- **Breaking:** A type defined twice fails the build: "Duplicate definition X found when parsing
|
|
76
|
+
the schema", pointing at `extend`. PHP Lighthouse keeps the last definition.
|
|
77
|
+
- **Breaking:** An argument typed `_` that no directive replaces fails the build. It used to
|
|
78
|
+
become a scalar.
|
|
79
|
+
- **Breaking:** `@orderBy` clauses need `order`, take only `{ column:, order: }`, and a
|
|
80
|
+
column outside an enum must be an identifier.
|
|
81
|
+
- **Breaking:** `SQLOperator`'s values are PHP Lighthouse's (see above).
|
|
82
|
+
- **Breaking:** `@builder` and `@whereAuth` on a field apply after the argument directives,
|
|
83
|
+
as PHP Lighthouse's field builder directives do. An `order` a builder adds now breaks the
|
|
84
|
+
ties of the client's `orderBy` instead of preceding it. They also apply on relationship
|
|
85
|
+
fields (`@hasMany`, ...), where they used to be ignored, and on `@aggregate`, which now
|
|
86
|
+
gets the argument directives too.
|
|
87
|
+
|
|
88
|
+
### Fixed
|
|
89
|
+
|
|
90
|
+
- A schema that stops building while you edit it keeps its `programmatic-types.graphql`.
|
|
91
|
+
If there is none yet, the file declares the `_` placeholder alone, so the IDE never
|
|
92
|
+
reports `Unknown type "_"`. When an import is missing, the IDE helper reads every
|
|
93
|
+
`.graphql` file in the folder, as the IDE does.
|
|
94
|
+
- `programmatic-types.graphql` no longer repeats the types `schema-directives.graphql`
|
|
95
|
+
defines (`Mixed`, `CanAction`, `PaginateType`, ...). graphql-js, which the VS Code
|
|
96
|
+
and JetBrains GraphQL plugins use, rejected the schema.
|
|
97
|
+
- `@whereConditions` applied only to an argument named `where`. It and `@whereHasConditions`
|
|
98
|
+
are argument directives like the others now: any name, in SDL order.
|
|
99
|
+
- An error in a `@whereConditions` value (an unknown relation or operator) is the client's
|
|
100
|
+
error. It used to be swallowed and leave the query unfiltered.
|
|
101
|
+
|
|
102
|
+
## [0.6.0] - 2026-10-18
|
|
103
|
+
|
|
104
|
+
Lighthouse now uses what the Rails app and graphql-ruby already have instead of settings and
|
|
105
|
+
files of its own: the app's cache, its controllers' rate limiting, its ActiveStorage, and
|
|
106
|
+
everything graphql-ruby's `graphql:install` writes, which `lighthouse:install` now builds on.
|
|
107
|
+
Several defaults change.
|
|
108
|
+
|
|
109
|
+
**Upgrading from 0.5:**
|
|
110
|
+
|
|
111
|
+
1. Remove `config.cache_store =` and `config.upload_store =` from
|
|
112
|
+
`config/initializers/lighthouse.rb`. Configure the app's `config.cache_store` instead, and
|
|
113
|
+
`config.action_controller.cache_store` if rate limits should be counted somewhere else.
|
|
114
|
+
2. `@throttle` counts per IP now. Add `by: "current_user.id"` where a field counted per user.
|
|
115
|
+
3. Past a `@throttle` limit the request is answered with a 429, as Rails' `rate_limit` answers it.
|
|
116
|
+
To fail only the field, as before, give `with:` a controller method that raises a
|
|
117
|
+
`GraphQL::ExecutionError`.
|
|
118
|
+
4. `@upload` checks `public:` against the ActiveStorage service. On a public service, add
|
|
119
|
+
`public: true`. Where `public: true` meets a private service, name a public one with `disk:`.
|
|
120
|
+
5. `rails g lighthouse:query` and `lighthouse:mutation` are gone. Resolve a root field with a
|
|
121
|
+
method of `Types::QueryType` / `MutationType`, with `rails g graphql:mutation`, or with a
|
|
122
|
+
`Resolvers::` / `Mutations::` class written by hand, which convention still finds. An app
|
|
123
|
+
installed with 0.5 keeps working as it is: `AppSchema = get_schema(...)` is still supported.
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
|
|
127
|
+
- `@throttle(by:)`: who the client is, as `rate_limit`'s `by:`. It is a path in the context, read
|
|
128
|
+
as `@inject` reads one (`by: "current_user.id"`).
|
|
129
|
+
- `@throttle(with:)`: a controller method to call past the limit, as `rate_limit`'s `with:`. One
|
|
130
|
+
that renders or redirects ends the request with its response, as a `before_action` that renders
|
|
131
|
+
does. One that returns lets the call through, and one that raises a `GraphQL::ExecutionError`
|
|
132
|
+
fails only the field.
|
|
133
|
+
- `@throttle` refusals are instrumented as `rate_limit.action_controller`, with `rate_limit`'s
|
|
134
|
+
payload (`request`, `count`, `to`, `within`, `by`, `name`, `scope`, `cache_key`).
|
|
135
|
+
- `base_types` takes graphql-ruby's code-first root types and Node interface. The SDL's root type
|
|
136
|
+
of a kind is built on `query_type:` / `mutation_type:` / `subscription_type:` (`Types::QueryType`),
|
|
137
|
+
so the `field`s the class declares (graphql-ruby's Relay `node` and `nodes`, what
|
|
138
|
+
`graphql:mutation` adds to `Types::MutationType`) are the schema's too, beside the SDL's, and
|
|
139
|
+
resolve as in graphql-ruby. With no such root type in the SDL, the class is the root. `node:`
|
|
140
|
+
(`Types::NodeType`) is the SDL's `Node` interface, which `@node` types implement, so
|
|
141
|
+
graphql-ruby's Relay and `@node` share one `Node`.
|
|
142
|
+
- A root field with no resolving directive resolves with the method of its name on the root type
|
|
143
|
+
class, when the class includes `Lighthouse::GraphQL::Type` (`Query.latestPosts` →
|
|
144
|
+
`Types::QueryType#latest_posts`), before the conventional `Resolvers::` / `Mutations::` class.
|
|
145
|
+
- A schema that implements graphql-ruby's `id_from_object` / `object_from_id` has one kind of
|
|
146
|
+
global id: `@globalId` encodes an object's own id with `id_from_object`, and decodes an
|
|
147
|
+
argument with `object_from_id`.
|
|
148
|
+
- `lighthouse:install --subscriptions` writes `Subscriptions::BaseSubscription` and names it in
|
|
149
|
+
`base_types`; without it, the install writes no subscription base.
|
|
150
|
+
|
|
151
|
+
### Changed
|
|
152
|
+
|
|
153
|
+
- `@cache`, `@clearCache` and Automatic Persisted Queries use the app's `Rails.cache`
|
|
154
|
+
(`config.cache_store`), as PHP Lighthouse uses Laravel's cache. Outside Rails they use an
|
|
155
|
+
in-memory store.
|
|
156
|
+
- `@throttle` is Rails' `rate_limit` on one field, and counts as it does:
|
|
157
|
+
- It counts in the controller's `cache_store` (`config.action_controller.cache_store`,
|
|
158
|
+
`Rails.cache` unless set), under `rate_limit`'s key, `rate-limit:<scope>:<name>:<by>`.
|
|
159
|
+
Counts kept under the old `lighthouse:throttle:` keys start over.
|
|
160
|
+
- `name:` is `rate_limit`'s. Each field counts under its own name (`Query.x`) by default, apart
|
|
161
|
+
from the controller's `rate_limit`, or together with it when both use the same name.
|
|
162
|
+
`prefix:` is `rate_limit`'s `scope:`, the controller's path by default.
|
|
163
|
+
- The client is its IP, as `rate_limit`'s default `by:`, no longer the signed-in user.
|
|
164
|
+
- Past the limit it does what `rate_limit` does by default: it raises
|
|
165
|
+
`ActionController::TooManyRequests`, which Rails answers with a 429
|
|
166
|
+
(`head :too_many_requests` on Rails 7.2). The field used to fail on its own, with the rest of
|
|
167
|
+
the operation answered; that remains the behavior without Action Pack.
|
|
168
|
+
- `lighthouse:install` builds on graphql-ruby's `graphql:install`, run with whichever of its
|
|
169
|
+
options the app chose (`--relay`, `--batch`, `--skip-mutation-root-type`, `--schema`,
|
|
170
|
+
`--directory`, `--api`), or run first with its defaults when the app has none. It writes no
|
|
171
|
+
schema class of its own: graphql-ruby's `class AppSchema < GraphQL::Schema` becomes
|
|
172
|
+
`class AppSchema < Lighthouse::GraphQL::RbLightHouse.get_schema(options: { base_types: ... })`,
|
|
173
|
+
naming every class graphql-ruby wrote, and keeps the rest of the class (`use
|
|
174
|
+
GraphQL::Dataloader`, `max_depth`, `id_from_object`, ...). It takes out what Lighthouse replaces:
|
|
175
|
+
the `query(...)` / `mutation(...)` root types the SDL's are built on, the `resolve_type` stub,
|
|
176
|
+
and the example `test_field`. Its base object, interface, union and scalar include
|
|
177
|
+
`Lighthouse::GraphQL::Type`, so every type class is Lighthouse's without an `include` of its
|
|
178
|
+
own. With `--batch`, the schema uses `GraphQL::Dataloader` too. Its `Mutations::BaseMutation`, a
|
|
179
|
+
`RelayClassicMutation`, is kept; graphql-ruby's controller, unchanged since its install, becomes
|
|
180
|
+
one with `Lighthouse::GraphQL::Controller`. `Types::QueryType` and `MutationType` are kept.
|
|
181
|
+
- An SDL Mutation field that declares its arguments one by one resolves by convention to a
|
|
182
|
+
`GraphQL::Schema::RelayClassicMutation` subclass as to a plain mutation, its arguments as
|
|
183
|
+
keywords. It used to fail looking for `input:`.
|
|
184
|
+
- A subscriptions backend the app's schema class `use`s in its body
|
|
185
|
+
(`use GraphQL::Subscriptions::ActionCableSubscriptions`) delivers the SDL's Subscription fields.
|
|
186
|
+
graphql-ruby adds its subscription extension only to a root type of the class's own.
|
|
187
|
+
- `Lighthouse::GraphQL::Controller` puts the controller in the context (`:lighthouse_controller`)
|
|
188
|
+
for `@throttle`. In development, an error Rails answers with a status of its own
|
|
189
|
+
(`config.action_dispatch.rescue_responses`, such as a 429 or a 404) now gets that status, as in
|
|
190
|
+
production, instead of being rendered as a JSON 500.
|
|
191
|
+
- `@upload` stores files on the app's ActiveStorage only, with PHP Lighthouse's rules. `disk:`
|
|
192
|
+
names a service of `config/storage.yml` (`config.active_storage.service` without it), and
|
|
193
|
+
`path:` starts the blob's key. ActiveStorage sets visibility per service, so `public:` must now
|
|
194
|
+
agree with the service's: `public: true` on a private service, or the default `false` on a
|
|
195
|
+
public one, fails instead of being ignored. An unknown service, or a value that is not a file,
|
|
196
|
+
fails naming the argument. The file is rewound before it is stored, so a `@validator` may read
|
|
197
|
+
it first.
|
|
198
|
+
- The `Upload` scalar's errors are PHP Lighthouse's: a value that is not a file names the GraphQL
|
|
199
|
+
multipart request specification and what was sent instead.
|
|
200
|
+
|
|
201
|
+
### Removed
|
|
202
|
+
|
|
203
|
+
- `Config.cache_store`: configure the app's `config.cache_store` instead, and
|
|
204
|
+
`config.action_controller.cache_store` (or a controller's `self.cache_store =`) for `@throttle`.
|
|
205
|
+
`Config.persisted_queries_store` stays, defaulting to `Rails.cache`.
|
|
206
|
+
- `Config.upload_store`: to store files elsewhere, declare an ActiveStorage service and name it
|
|
207
|
+
with `@upload(disk:)`, as a Laravel app declares a disk.
|
|
208
|
+
- `rails g lighthouse:query` and `lighthouse:mutation`: graphql-ruby has no resolver generator,
|
|
209
|
+
and `graphql:mutation` works in a Lighthouse app now. See Upgrading.
|
|
210
|
+
|
|
211
|
+
### Fixed
|
|
212
|
+
|
|
213
|
+
- On graphql-ruby 2.5, a schema using `@limit`, `@bind` or `@upload` without one of their optional
|
|
214
|
+
arguments did not build (`Expected value to not be null`): graphql 2.5 does not apply the default
|
|
215
|
+
of a non-null directive argument. Those arguments (`builder`, `column`, `with`, `optional`,
|
|
216
|
+
`path`, `public`) are nullable now, with the same defaults.
|
|
217
|
+
|
|
9
218
|
## [0.5.0] - 2026-10-18
|
|
10
219
|
|
|
11
220
|
### Added
|
|
@@ -461,7 +670,9 @@ had been running inside a private monorepo as a path gem before being extracted.
|
|
|
461
670
|
- Config-driven extension seams: `Lighthouse.configure`, `resolver_namespaces`,
|
|
462
671
|
`on_error`, and `DirectiveRegistry.register` for app-defined directives.
|
|
463
672
|
|
|
464
|
-
[Unreleased]: https://github.com/Vaz-Innovation/lighthouse-graphql/compare/v0.
|
|
673
|
+
[Unreleased]: https://github.com/Vaz-Innovation/lighthouse-graphql/compare/v0.7.0...HEAD
|
|
674
|
+
[0.7.0]: https://github.com/Vaz-Innovation/lighthouse-graphql/compare/v0.6.0...v0.7.0
|
|
675
|
+
[0.6.0]: https://github.com/Vaz-Innovation/lighthouse-graphql/compare/v0.5.0...v0.6.0
|
|
465
676
|
[0.5.0]: https://github.com/Vaz-Innovation/lighthouse-graphql/compare/v0.4.0...v0.5.0
|
|
466
677
|
[0.4.0]: https://github.com/Vaz-Innovation/lighthouse-graphql/compare/v0.3.0...v0.4.0
|
|
467
678
|
[0.3.0]: https://github.com/Vaz-Innovation/lighthouse-graphql/compare/v0.2.1...v0.3.0
|
data/README.md
CHANGED
|
@@ -72,34 +72,46 @@ It depends on `graphql ~> 2.5` and `activesupport`. For federation you also need
|
|
|
72
72
|
On graphql 2.5, keep the `json` gem below 3: graphql 2.5 calls `JSON.generate`
|
|
73
73
|
with an option json 3 removed (graphql 2.6 no longer does).
|
|
74
74
|
|
|
75
|
-
Then let the
|
|
75
|
+
Then let the generators set the app up:
|
|
76
76
|
|
|
77
77
|
```shell
|
|
78
|
+
bin/rails generate graphql:install # graphql-ruby's, with the options you choose (optional)
|
|
78
79
|
bin/rails generate lighthouse:install
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
`lighthouse:install` builds on graphql-ruby's own install, and runs `graphql:install`
|
|
83
|
+
with its defaults when the app has none. It adapts what graphql-ruby wrote: the
|
|
84
|
+
schema class becomes Lighthouse's, built from the SDL on graphql-ruby's root types
|
|
85
|
+
and base classes, and the rest of its body stays; the base object, interface, union
|
|
86
|
+
and scalar include `Lighthouse::GraphQL::Type`; the controller serves the schema
|
|
87
|
+
through `Lighthouse::GraphQL::Controller`. It adds the initializer, a starter schema
|
|
88
|
+
in `graphql/schema.graphql` and a `graphql.config.yml` for your IDE. Run
|
|
85
89
|
`bin/rails generate lighthouse:install --help` for its options (`--schema`,
|
|
86
|
-
`--
|
|
90
|
+
`--schema-path`, `--subscriptions`, `--skip-controller`, `--skip-ide`), and see
|
|
87
91
|
[docs/generators.md](docs/generators.md) for every generator, graphql-ruby's included.
|
|
88
92
|
|
|
93
|
+
### Schema files
|
|
94
|
+
|
|
95
|
+
As in PHP Lighthouse, the schema is read from one entrypoint,
|
|
96
|
+
`graphql/schema.graphql`, which `#import`s the other files
|
|
97
|
+
(`#import user.graphql`, `#import blog/*.graphql`). A type is defined once, and the
|
|
98
|
+
other files add to it with `extend type Query`. See
|
|
99
|
+
[docs/schema-organisation.md](docs/schema-organisation.md).
|
|
100
|
+
|
|
89
101
|
### IDE support
|
|
90
102
|
|
|
91
103
|
Lighthouse's directives and the types it generates (Paginator/Connection types,
|
|
92
|
-
|
|
93
|
-
your `.graphql` files.
|
|
104
|
+
`@whereConditions` and `@orderBy` inputs, the `_` placeholder, …) are defined in
|
|
105
|
+
Ruby, so your IDE can't see them in your `.graphql` files. Lighthouse writes them
|
|
106
|
+
to `schema-directives.graphql` and `programmatic-types.graphql` in the app root:
|
|
107
|
+
in development on every boot and reload, or with:
|
|
94
108
|
|
|
95
109
|
```shell
|
|
96
110
|
bin/rails lighthouse:ide_helper
|
|
97
111
|
```
|
|
98
112
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
JetBrains IDEs and VS Code at them. Rerun it after changing your schema or
|
|
102
|
-
registering a directive. See [docs/getting-started.md](docs/getting-started.md#ide-support).
|
|
113
|
+
The generated `graphql.config.yml` already points the GraphQL plugins for
|
|
114
|
+
JetBrains IDEs and VS Code at them. See [docs/getting-started.md](docs/getting-started.md#ide-support).
|
|
103
115
|
|
|
104
116
|
## Quick start
|
|
105
117
|
|
|
@@ -123,22 +135,26 @@ Prefer to wire it by hand? These are the steps the generator performs.
|
|
|
123
135
|
}
|
|
124
136
|
```
|
|
125
137
|
|
|
126
|
-
2. **Build the schema
|
|
138
|
+
2. **Build the schema**: graphql-ruby's schema class (`app/graphql/app_schema.rb`),
|
|
139
|
+
inheriting from the schema Lighthouse builds instead of `GraphQL::Schema`:
|
|
127
140
|
|
|
128
141
|
```ruby
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
AppSchema = Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
132
|
-
sdl_folder: Rails.root.join('graphql').to_s,
|
|
142
|
+
class AppSchema < Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
133
143
|
options: {
|
|
134
|
-
base_types: {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
base_types: { # the classes graphql-ruby's install writes
|
|
145
|
+
query_type: Types::QueryType,
|
|
146
|
+
mutation_type: Types::MutationType,
|
|
147
|
+
node: Types::NodeType,
|
|
148
|
+
resolver: Resolvers::BaseResolver,
|
|
149
|
+
mutation: Mutations::BaseMutation,
|
|
150
|
+
object: Types::BaseObject
|
|
151
|
+
# interface: Types::BaseInterface, union:, scalar:, enum:, input_object: ...
|
|
139
152
|
}
|
|
140
153
|
}
|
|
141
154
|
)
|
|
155
|
+
use GraphQL::Dataloader # Lighthouse batches relationships through it
|
|
156
|
+
max_depth(15)
|
|
157
|
+
end
|
|
142
158
|
```
|
|
143
159
|
|
|
144
160
|
3. **Execute** it from your controller exactly like any graphql-ruby schema:
|
data/docs/best-practices.md
CHANGED
|
@@ -92,7 +92,9 @@ app's GraphQL setup easy to find.
|
|
|
92
92
|
|
|
93
93
|
## Split your SDL
|
|
94
94
|
|
|
95
|
-
Lighthouse
|
|
96
|
-
|
|
97
|
-
`
|
|
98
|
-
|
|
95
|
+
Lighthouse reads `graphql/schema.graphql` and the files it `#import`s
|
|
96
|
+
([schema-organisation.md](schema-organisation.md)). Organize
|
|
97
|
+
by domain (`users.graphql`, `orders.graphql`, …), each with its types and the
|
|
98
|
+
fields it adds with `extend type Query` / `extend type Mutation`. Keep a small
|
|
99
|
+
`directives.graphql`, imported too, only for directives you declare yourself
|
|
100
|
+
(built-ins are injected automatically).
|
data/docs/caching.md
CHANGED
|
@@ -41,9 +41,10 @@ such as a Relay connection object, is reported through
|
|
|
41
41
|
[`on_error`](configuration.md#on_error) and returned uncached. So cache list
|
|
42
42
|
fields, not connection fields.
|
|
43
43
|
|
|
44
|
-
The store is
|
|
45
|
-
|
|
46
|
-
you enable caching
|
|
44
|
+
The store is your app's cache, `Rails.cache` (`config.cache_store`), as PHP
|
|
45
|
+
Lighthouse uses Laravel's: Lighthouse keeps no store of its own. Note that Rails
|
|
46
|
+
uses a `:null_store` in development and test unless you enable caching
|
|
47
|
+
(`bin/rails dev:cache`).
|
|
47
48
|
|
|
48
49
|
A cached value is still authorized and validated on every request: `@cache` runs
|
|
49
50
|
inside `@guard`, `@can*` and `@validator`, never around them.
|
data/docs/configuration.md
CHANGED
|
@@ -9,7 +9,7 @@ reusable and publishable.
|
|
|
9
9
|
require 'lighthouse-graphql'
|
|
10
10
|
|
|
11
11
|
Lighthouse.configure do |config|
|
|
12
|
-
config.
|
|
12
|
+
config.schema_path = Rails.root.join('graphql/schema.graphql').to_s
|
|
13
13
|
config.namespaces = { queries: ['Resolvers'], mutations: ['Mutations'] }
|
|
14
14
|
config.resolver_namespaces = ['App::Graphql']
|
|
15
15
|
config.on_error = ->(error, _context) { Rails.error.report(error) }
|
|
@@ -18,11 +18,25 @@ end
|
|
|
18
18
|
|
|
19
19
|
## Options
|
|
20
20
|
|
|
21
|
-
### `
|
|
21
|
+
### `schema_path`
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
The schema's entrypoint, as PHP Lighthouse's `schema_path`. Lighthouse reads
|
|
24
|
+
this file and the files it imports (`#import user.graphql`, `#import blog/*.graphql`).
|
|
25
|
+
A type is defined once, and other files add to it with `extend type`. See
|
|
26
|
+
[schema-organisation.md](schema-organisation.md). Defaults to
|
|
27
|
+
`RAILS_ROOT/graphql/schema.graphql` in a Rails app, and `nil` otherwise. Outside
|
|
28
|
+
Rails, pass the path, or the SDL itself (`SchemaFactory.build_base_schema(sdl:)`).
|
|
29
|
+
|
|
30
|
+
`get_schema(schema_path: ...)` overrides it for one schema.
|
|
31
|
+
|
|
32
|
+
### `ide_helper`
|
|
33
|
+
|
|
34
|
+
Rewrite `schema-directives.graphql` and `programmatic-types.graphql` in the
|
|
35
|
+
app's root on boot and on every reload, for the IDE
|
|
36
|
+
([schema-organisation.md](schema-organisation.md#ide-support)). A file whose
|
|
37
|
+
content is unchanged is left alone. `nil` (the default) turns it on in
|
|
38
|
+
development when the app has a `graphql.config.yml`, which `lighthouse:install`
|
|
39
|
+
writes. Set `true` or `false` to decide yourself.
|
|
26
40
|
|
|
27
41
|
### `namespaces`
|
|
28
42
|
|
|
@@ -70,15 +84,6 @@ reporter in production:
|
|
|
70
84
|
config.on_error = ->(error, _ctx) { Sentry.capture_exception(error) }
|
|
71
85
|
```
|
|
72
86
|
|
|
73
|
-
### `cache_store`
|
|
74
|
-
|
|
75
|
-
The store behind [`@cache` / `@clearCache`](caching.md). Defaults to
|
|
76
|
-
`Rails.cache` (an in-memory store outside Rails).
|
|
77
|
-
|
|
78
|
-
```ruby
|
|
79
|
-
config.cache_store = ActiveSupport::Cache::RedisCacheStore.new(url: ENV['REDIS_URL'])
|
|
80
|
-
```
|
|
81
|
-
|
|
82
87
|
### `pagination_default_count`, `pagination_max_count`
|
|
83
88
|
|
|
84
89
|
Defaults for paginated fields: the `first` used when a client leaves it out (so
|
|
@@ -99,7 +104,7 @@ Options for large schemas and high traffic, all off by default. See
|
|
|
99
104
|
config.schema_cache = Rails.env.production? # cache the built schema in tmp/cache
|
|
100
105
|
config.watch_sdl = Rails.env.development? # reload when a .graphql file changes
|
|
101
106
|
config.persisted_queries = true # Apollo's Automatic Persisted Queries
|
|
102
|
-
config.persisted_queries_store = Rails.cache # where APQ keeps queries (default:
|
|
107
|
+
config.persisted_queries_store = Rails.cache # where APQ keeps queries (default: Rails.cache)
|
|
103
108
|
config.persisted_queries_ttl = 1.week.to_i # how long they live there
|
|
104
109
|
config.persisted_queries_manifest = Rails.root.join('persisted-queries.json') # trusted operations
|
|
105
110
|
config.persisted_queries_only = Rails.env.production? # run nothing else
|
|
@@ -128,17 +133,6 @@ returning `[type, id]`. Defaults to `Lighthouse::GraphQL::GlobalId`, the base64
|
|
|
128
133
|
config.global_id = SignedGlobalIds
|
|
129
134
|
```
|
|
130
135
|
|
|
131
|
-
### `upload_store`
|
|
132
|
-
|
|
133
|
-
Stores the file an [`@upload`](uploads.md) argument carries: any object with
|
|
134
|
-
`call(file, disk:, path:, public:)`, whose return value the argument then holds.
|
|
135
|
-
Defaults to `Lighthouse::GraphQL::UploadStore`, which makes an ActiveStorage blob and
|
|
136
|
-
returns its signed id.
|
|
137
|
-
|
|
138
|
-
```ruby
|
|
139
|
-
config.upload_store = ->(file, path: '/', **) { S3Uploader.put(file, prefix: path) }
|
|
140
|
-
```
|
|
141
|
-
|
|
142
136
|
### `queue_broadcasts`, `broadcast_queue`
|
|
143
137
|
|
|
144
138
|
Whether [`@broadcast`](directives.md#broadcast) triggers its subscription in an
|
data/docs/custom-directives.md
CHANGED
|
@@ -29,7 +29,8 @@ Include the marker module(s) your directive fulfils:
|
|
|
29
29
|
| `Contracts::FieldResolver` | provide a field's resolver | `#define_resolver(type)` (or `#resolve_field`) |
|
|
30
30
|
| `Contracts::FieldMiddleware` | wrap an existing resolver (e.g. `@auth`) | `#wrap_resolver(next)` |
|
|
31
31
|
| `Contracts::FieldManipulator` | reshape the schema for a field | `manipulate_field_definition(...)` |
|
|
32
|
-
| `Contracts::ArgManipulator` | reshape the schema for an argument |
|
|
32
|
+
| `Contracts::ArgManipulator` | reshape the schema for an argument, as `@orderBy` and `@whereConditions` generate their types | `#manipulate_arg_definition(document_ast, field_node, type_node)` returning the argument node |
|
|
33
|
+
| `Contracts::FieldBuilder` | constrain a field's query, whatever the client sends | `#handle_field_builder(relation)` |
|
|
33
34
|
| `Contracts::ArgBuilder` | compose a query constraint from an argument | `#handle_builder(relation, value)` |
|
|
34
35
|
|
|
35
36
|
A directive class also answers two class-level questions: its name and its SDL
|
data/docs/directives.md
CHANGED
|
@@ -78,7 +78,7 @@ type Query {
|
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
- **The query.** It is the model's (`model:`, else the return type), narrowed by
|
|
81
|
-
`scopes:`, [`@builder`](#builder)
|
|
81
|
+
`scopes:`, the field's argument filters and [`@builder`](#builder). On a nested
|
|
82
82
|
field it pages the parent's association of the same name: `User.posts @paginate`
|
|
83
83
|
pages the user's posts.
|
|
84
84
|
- **`first`.** It defaults to `defaultCount`, else
|
|
@@ -283,16 +283,23 @@ type PostQueries {
|
|
|
283
283
|
|
|
284
284
|
### `@throttle`
|
|
285
285
|
|
|
286
|
-
|
|
287
|
-
(default 60 a minute), counted
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
286
|
+
Rails' `rate_limit` on one field: at most `maxAttempts` calls (its `to:`) every
|
|
287
|
+
`decayMinutes` (its `within:`, default 60 a minute), counted before anything else runs.
|
|
288
|
+
It takes `rate_limit`'s options, and counts as `rate_limit` does: in the controller's
|
|
289
|
+
`cache_store`, under the same key, and instrumented as `rate_limit.action_controller`.
|
|
290
|
+
See [security.md](security.md#rate-limiting).
|
|
291
|
+
|
|
292
|
+
| Argument | `rate_limit`'s | Default |
|
|
293
|
+
| --- | --- | --- |
|
|
294
|
+
| `by:` | `by:`, who the client is: a path in the context, as [`@inject`](resolvers.md#spread-and-inject)'s (`"current_user.id"`) | the client's IP |
|
|
295
|
+
| `with:` | `with:`, the controller's method to call past the limit | `rate_limit`'s: a 429 (it raises `ActionController::TooManyRequests`) |
|
|
296
|
+
| `name:` | `name:`; fields, and the controller's own `rate_limit`, of the same name count together | the field (`Query.x`) |
|
|
297
|
+
| `prefix:` | `scope:` | the controller's path |
|
|
292
298
|
|
|
293
299
|
```graphql
|
|
294
300
|
type Mutation {
|
|
295
301
|
login(email: String!, password: String!): User @throttle(maxAttempts: 5)
|
|
302
|
+
createTag(name: String!): Tag! @throttle(maxAttempts: 20, by: "current_user.id", with: "too_many_requests") @guard @create
|
|
296
303
|
}
|
|
297
304
|
```
|
|
298
305
|
|
|
@@ -449,7 +456,12 @@ Builders are looked up in their own namespace, `Builders::` (app/graphql/builder
|
|
|
449
456
|
`class:` with `method:` (default `call`), or PHP Lighthouse's `method: "Class@method"`.
|
|
450
457
|
|
|
451
458
|
On a field, the method gets `(relation, args, context)`, or the directive's
|
|
452
|
-
`value:` in place of the arguments.
|
|
459
|
+
`value:` in place of the arguments. As in PHP Lighthouse, a field's `@builder`
|
|
460
|
+
(with `@whereAuth` and `@orderBy(column:)`) applies after the argument
|
|
461
|
+
directives, in SDL order. An `order` it adds therefore breaks the ties of the
|
|
462
|
+
client's `orderBy`. It applies on every field that queries records: `@all`,
|
|
463
|
+
`@paginate`, `@find`, `@first`, `@count`, `@aggregate`, the relationship
|
|
464
|
+
directives and `resolve_info.enhance_builder`. On an argument or an input field, it gets
|
|
453
465
|
`(relation, value, context)` whenever the client sends that argument, an explicit
|
|
454
466
|
`null` included, and not when it is left out:
|
|
455
467
|
|
|
@@ -511,7 +523,7 @@ Full guide: [filtering-and-ordering.md](filtering-and-ordering.md).
|
|
|
511
523
|
| `@whereBetween` / `@whereNotBetween` | `column BETWEEN` / `NOT BETWEEN` the input's two fields, in order |
|
|
512
524
|
| `@whereJsonContains` | a JSON column contains the value (every value of a list); `key: "meta->tags"` points into it |
|
|
513
525
|
| `@scope(name:)` | calls the model scope with the value (`name:` defaults to the argument's) |
|
|
514
|
-
| `@orderBy` | sort by
|
|
526
|
+
| `@orderBy` | sort by the client's clauses (columns, relations' aggregates); on a field, a fixed order |
|
|
515
527
|
|
|
516
528
|
```graphql
|
|
517
529
|
type Query {
|
|
@@ -519,7 +531,7 @@ type Query {
|
|
|
519
531
|
status: String @eq
|
|
520
532
|
nameSearch: String @like
|
|
521
533
|
minAge: Int @where(operator: "GTE", key: "age")
|
|
522
|
-
orderBy:
|
|
534
|
+
orderBy: _ @orderBy(columns: ["name", "created_at"])
|
|
523
535
|
): [User!]! @all
|
|
524
536
|
}
|
|
525
537
|
```
|
|
@@ -561,18 +573,71 @@ parents it would cut them all together, as PHP Lighthouse warns. PHP Lighthouse
|
|
|
561
573
|
also allows `@limit` on the field itself, where it only marks the field the argument
|
|
562
574
|
limits; it is accepted there and changes nothing.
|
|
563
575
|
|
|
576
|
+
### `@orderBy`
|
|
577
|
+
|
|
578
|
+
```graphql
|
|
579
|
+
directive @orderBy(
|
|
580
|
+
columns: [String!] # argument: the columns the client may order by
|
|
581
|
+
columnsEnum: String # argument: an enum of them instead (one of the two)
|
|
582
|
+
relations: [OrderByRelation!] # argument: { relation:, columns:, columnsEnum: } to order by an aggregate
|
|
583
|
+
column: String # field: the column to order by
|
|
584
|
+
direction: OrderByDirection = ASC
|
|
585
|
+
) on ARGUMENT_DEFINITION | FIELD_DEFINITION
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
On an argument written as `_`, Lighthouse generates the clause input
|
|
589
|
+
(`<Type><Field><Arg>OrderByClause`, or `…RelationOrderByClause` with
|
|
590
|
+
`relations:`) and the column enum. Without `columns:`, `columnsEnum:` or
|
|
591
|
+
`relations:`, the argument takes the shared `OrderByClause`, which allows any
|
|
592
|
+
column. A column can be a JSON path, `meta->rank`, in `@orderBy` and in
|
|
593
|
+
`@whereConditions` ([JSON columns](filtering-and-ordering.md#json-columns)). On a field, `@orderBy(column:, direction:)` orders the result after
|
|
594
|
+
the client's clauses.
|
|
595
|
+
|
|
596
|
+
```graphql
|
|
597
|
+
type Query {
|
|
598
|
+
posts(orderBy: _ @orderBy(columns: ["posted_at", "title"])): [Post!]! @all
|
|
599
|
+
users(orderBy: _ @orderBy(relations: [{ relation: "posts" }])): [User!]! @all
|
|
600
|
+
latestPosts: [Post!]! @all @orderBy(column: "posted_at", direction: DESC)
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
# { posts(orderBy: [{ column: POSTED_AT, order: DESC }]) { title } }
|
|
604
|
+
# { users(orderBy: [{ posts: { aggregate: COUNT }, order: DESC }]) { name } }
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
Guide: [filtering-and-ordering.md](filtering-and-ordering.md#ordering).
|
|
608
|
+
|
|
564
609
|
### `@whereConditions` / `@whereHasConditions`
|
|
565
610
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
[
|
|
611
|
+
```graphql
|
|
612
|
+
directive @whereConditions(columns: [String!], columnsEnum: String, handler: String) on ARGUMENT_DEFINITION
|
|
613
|
+
directive @whereHasConditions(relation: String, columns: [String!], columnsEnum: String, handler: String) on ARGUMENT_DEFINITION
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
These give clients a dynamic `WHERE` builder, with conditions joined by `AND`
|
|
617
|
+
and `OR`, PHP Lighthouse's `SQLOperator` operators, and `HAS` checks on
|
|
618
|
+
relations. On an argument written as `_`, Lighthouse generates the input
|
|
619
|
+
(`<Type><Field><Arg>WhereConditions` / `…WhereHasConditions`), its `…Relation`
|
|
620
|
+
and `…HasCondition` inputs, and the column enum. Without `columns:` or
|
|
621
|
+
`columnsEnum:`, the argument takes the shared `WhereConditions`, which allows
|
|
622
|
+
any column.
|
|
623
|
+
|
|
624
|
+
`@whereHasConditions` keeps the records with a related record matching the
|
|
625
|
+
conditions. The relation is `relation:`, or the argument's name without `has`.
|
|
626
|
+
`handler:` names a `Builders::` class whose `call(relation, conditions)` applies
|
|
627
|
+
the conditions instead.
|
|
569
628
|
|
|
570
629
|
```graphql
|
|
571
630
|
type Query {
|
|
572
631
|
people(where: _ @whereConditions(columns: ["age", "type"])): [Person!]! @all
|
|
632
|
+
users(hasPosts: _ @whereHasConditions(columns: ["title"])): [User!]! @all
|
|
573
633
|
}
|
|
634
|
+
|
|
635
|
+
# { people(where: { AND: [{ column: AGE, operator: GT, value: 37 }, { column: TYPE, value: "Actor" }] }) { name } }
|
|
636
|
+
# { users(hasPosts: { column: TITLE, operator: LIKE, value: "%Rails%" }) { name } }
|
|
574
637
|
```
|
|
575
638
|
|
|
639
|
+
Guide: [filtering-and-ordering.md](filtering-and-ordering.md#complex-where-conditions).
|
|
640
|
+
|
|
576
641
|
### `@trim`, `@hash`, `@drop`, `@bind` and `@upload`
|
|
577
642
|
|
|
578
643
|
Rewrite an argument's or input field's value before the field resolves, in the
|
|
@@ -584,7 +649,7 @@ order the SDL lists them:
|
|
|
584
649
|
| `@drop` | nothing: the argument is removed |
|
|
585
650
|
| `@hash` | the value hashed with BCrypt (the `bcrypt` gem, as `has_secure_password`) |
|
|
586
651
|
| `@bind(class:, column: "id", with: [], optional: false)` | the model record(s) the value identifies, eager-loading `with`; or what a class's `call(value, context)` returns |
|
|
587
|
-
| `@upload(disk:, path: "/")` |
|
|
652
|
+
| `@upload(disk:, path: "/", public: false)` | the signed id of the ActiveStorage blob the file was stored as, on the app's service `disk` names ([uploads.md](uploads.md)) |
|
|
588
653
|
|
|
589
654
|
```graphql
|
|
590
655
|
type Mutation {
|
data/docs/federation.md
CHANGED
|
@@ -29,7 +29,7 @@ class Types::BaseObject < GraphQL::Schema::Object
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
BaseAppSchema = Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
32
|
-
|
|
32
|
+
schema_path: Rails.root.join('graphql/schema.graphql').to_s,
|
|
33
33
|
options: { federation: true, base_types: { object: Types::BaseObject, resolver: Resolvers::BaseResolver } }
|
|
34
34
|
)
|
|
35
35
|
|