Skip to content

Commit 4dfca1e

Browse files
authored
Overhaul loadMany docs (#2183)
2 parents fbb1b31 + 2f5473e commit 4dfca1e

File tree

1 file changed

+128
-32
lines changed
  • grafast/website/grafast/step-library/standard-steps

1 file changed

+128
-32
lines changed

grafast/website/grafast/step-library/standard-steps/loadMany.md

Lines changed: 128 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,47 @@ stateDiagram
115115

116116
## Usage
117117

118-
### Basic usage
119-
120-
```ts
121-
const $userId = $user.get("id");
122-
const $friendships = loadMany($userId, getFriendshipsByUserIds);
118+
```
119+
loadMany($spec, [$unaryStep,] [ioEquivalence,] callback)
123120
```
124121

125122
`loadMany` accepts two to four arguments, the first is the step that specifies
126123
which records to load (the _specifier step_), and the last is the callback
127124
function called with these specs responsible for loading them.
128125

129-
The callback function is called with two arguments, the first is a list of the
130-
values from the _specifier step_ and the second is options that may affect the
131-
fetching of the records.
126+
```ts
127+
// Basic usage:
128+
const $records = loadMany($spec, callback);
129+
130+
// Advanced usage:
131+
const $records = loadMany($spec, $unaryStep, ioEquivalence, callback);
132+
const $records = loadMany($spec, $unaryStep, callback);
133+
const $records = loadMany($spec, ioEquivalence, callback);
134+
```
135+
136+
Where:
137+
138+
- `$spec` is any step
139+
- `$unaryStep` is any _unary_ step - see [Unary step usage](#unary-step-usage) below
140+
- `ioEquivalence` is either `null`, a string, an array of strings, or a string-string object map - see [ioEquivalence usage](#ioequivalence-usage) below
141+
- and `callback` is a callback function responsible for fetching the data.
142+
143+
### Callback
144+
145+
The `callback` function is called with two arguments, the first is
146+
a list of the values from the _specifier step_ `$spec` and the second is options that
147+
may affect the fetching of the records.
148+
149+
```ts
150+
function callback(
151+
specs: ReadonlyArray<unknown>,
152+
options: {
153+
unary: unknown;
154+
attributes: ReadonlyArray<string>;
155+
params: Record<string, unknown>;
156+
},
157+
): PromiseOrDirect<ReadonlyArray<unknown>>;
158+
```
132159

133160
:::tip
134161

@@ -138,6 +165,39 @@ defined inline. This will allow LoadManyStep to optimise calls to this function.
138165

139166
:::
140167

168+
Within this definition of `callback`:
169+
170+
- `specs` is the runtime values of each value that `$spec` represented
171+
- `options` is an object containing:
172+
- `unary`: the runtime value that `$unaryStep` (if any) represented
173+
- `attributes`: the list of keys that have been accessed via
174+
`$record.get('<key>')` for each of the records in `$records`
175+
- `params`: the params set via `$records.setParam('<key>', <value>)`
176+
177+
`specs` is deduplicated using strict equality; so it is best to keep `$spec`
178+
simple - typically it should only represent a single scalar value - which is
179+
why `$unaryStep` exists...
180+
181+
`options.unary` is very useful to keep specs simple (so that fetch
182+
deduplication can work optimally) whilst passing in global values that you may
183+
need such as a database or API client.
184+
185+
`options.attributes` is useful for optimizing your fetch - e.g. if the user
186+
only ever requested `$record.get('id')` and `$record.get('avatarUrl')` then
187+
there's no need to fetch all the other attributes from your datasource.
188+
189+
`options.params` can be used to pass additional context to your callback
190+
function, perhaps options like "should we include archived records" or "should
191+
we expand 'customer' into a full object rather than just returning the
192+
identifier".
193+
194+
### Basic usage
195+
196+
```ts
197+
const $userId = $user.get("id");
198+
const $friendships = loadMany($userId, getFriendshipsByUserIds);
199+
```
200+
141201
An example of the callback function might be:
142202

143203
```ts
@@ -151,8 +211,33 @@ const friendshipsByUserIdCallback = (ids, { attributes }) => {
151211

152212
[dataloader]: https://github.com/graphql/dataloader
153213

154-
Optionally a penultimate argument (2nd of 3 arguments, or 3rd of 4 arguments)
155-
can indicate the input/output equivalence - this can be:
214+
### Unary step usage
215+
216+
(a step that only ever represents one value, e.g. simple derivatives of `context()`, `fieldArgs`, or `constant()`)
217+
218+
In addition to the forms seen in "Basic usage" above, you can pass a second
219+
step to `loadMany`. This second step must be a [**unary
220+
step**](../../step-classes.md#addUnaryDependency), meaning that it must represent
221+
exactly one value across the entire request (not a batch of values like most
222+
steps).
223+
224+
```ts
225+
const $userId = $user.get("id");
226+
const $dbClient = context().get("dbClient");
227+
const $friendships = loadMany($userId, $dbClient, getFriendshipsByUserIds);
228+
```
229+
230+
Since we know it will have exactly one value, we can pass it into the
231+
callback as a single value and our callback will be able to use it directly
232+
without having to perform any manual grouping.
233+
234+
This unary dependency is useful for fixed values (for example, those from
235+
GraphQL field arguments) and values on the GraphQL context such as clients to
236+
various APIs and other data sources.
237+
238+
### ioEquivalence usage
239+
240+
The `ioEquivalence` optional parameter can accept the following values:
156241

157242
- `null` to indicate no input/output equivalence
158243
- a string to indicate that the same named property on the output is equivalent
@@ -164,43 +249,45 @@ can indicate the input/output equivalence - this can be:
164249
the attributes of the object and the key(s) in the output that are equivalent
165250
to the given entry on the input
166251

252+
```ts title="Example for a scalar step"
253+
const $posts = loadMany(
254+
$userId,
255+
256+
// States that $post.get('user_id') should return $userId directly, since it
257+
// will have the same value.
258+
"user_id",
259+
260+
friendshipsByUserIdCallback,
261+
);
262+
```
263+
167264
```ts title="Example for a list step"
168265
const $posts = loadMany(
169266
list([$organizationId, $userId]),
267+
268+
// States that:
269+
// - $post.get('organization_id') should return $organizationId directly, and
270+
// - $post.get('user_id') should return $userId directly
170271
["organization_id", "user_id"],
272+
171273
batchGetMemberPostsByOrganizationIdAndUserId,
172274
);
173275
```
174276

175277
```ts title="Example for an object step"
176278
const $posts = loadMany(
177-
list({ oid: $organizationId, uid: $userId }),
279+
object({ oid: $organizationId, uid: $userId }),
280+
281+
// States that:
282+
// - $post.get('organization_id') should return $organizationId directly (the value for the `oid` input), and
283+
// - $post.get('user_id') should return $userId directly (the value for the `uid` input
178284
{ oid: "organization_id", uid: "user_id" },
285+
179286
batchGetMemberPostsByOrganizationIdAndUserId,
180287
);
181288
```
182289

183-
### Advanced usage
184-
185-
```ts
186-
const $userId = $user.get("id");
187-
const $dbClient = context().get("dbClient");
188-
const $friendships = loadMany($userId, $dbClient, getFriendshipsByUserIds);
189-
```
190-
191-
In addition to the forms seen in "Basic usage" above, you can pass a second
192-
step to `loadMany`. This second step must be a [**unary
193-
step**](../../step-classes.md#addUnaryDependency), meaning that it must represent
194-
exactly one value across the entire request (not a batch of values like most
195-
steps). Since we know it will have exactly one value, we can pass it into the
196-
callback as a single value and our callback will be able to use it directly
197-
without having to perform any manual grouping.
198-
199-
This unary dependency is useful for fixed values (for example, those from
200-
GraphQL field arguments) and values on the GraphQL context such as clients to
201-
various APIs and other data sources.
202-
203-
## Multiple steps
290+
### Passing multiple steps
204291

205292
The [`list()`](./list) or [`object()`](./object) step can be used if you need
206293
to pass the value of more than one step into your callback:
@@ -211,3 +298,12 @@ const $result = loadMany(list([$a, $b, $c]), callback);
211298

212299
The first argument to `callback` will then be an array of all the tuples of
213300
values from these plans: `ReadonlyArray<[a: AValue, b: BValue, c: CValue]>`.
301+
302+
:::tip Performance impact from using list/object
303+
304+
Using `list()` / `object()` like this will likely reduce the effectiveness of
305+
loadMany's built in deduplication; to address this a stable object/list is
306+
required - please track this issue:
307+
https://github.com/graphile/crystal/issues/2170
308+
309+
:::

0 commit comments

Comments
 (0)