@pothos/plugin-drizzle 0.15.0 → 0.16.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @pothos/plugin-drizzle
2
2
 
3
+ ## 0.16.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 9993d01: Fix a bug where selecting only connection count at the same time as another field selected the same relation would cause an error"
8
+
9
+ ## 0.16.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 3509aab: Add new field builder methods and connection enhancements:
14
+ - `t.relatedField`: New method for creating custom fields based on relations with custom selections using the `buildFilter` callback
15
+ - `t.relatedCount`: Simplified method for counting related records, with optional `where` filter support
16
+ - `totalCount: true` option for `relatedConnection`: Adds a `totalCount` field to related connections
17
+ - `totalCount` callback for `drizzleConnection`: Allows adding a `totalCount` field to drizzle connections
18
+ - `totalCountOnly` optimization: Skips the main query when only `totalCount` is requested (for both connection types)
19
+
3
20
  ## 0.15.0
4
21
 
5
22
  ### Minor Changes
package/README.md CHANGED
@@ -3,35 +3,38 @@
3
3
  ## Installing
4
4
 
5
5
  ```package-install
6
- npm install --save @pothos/plugin-drizzle
6
+ npm install --save @pothos/plugin-drizzle drizzle-orm@beta
7
7
  ```
8
8
 
9
9
  The drizzle plugin is built on top of drizzles relational query builder, and requires that you
10
10
  define and configure all the relevant relations in your drizzle schema. See
11
- https://orm.drizzle.team/docs/rqb for detailed documentation on the relations API.
11
+ https://rqbv2.drizzle-orm-fe.pages.dev/docs/relations-v2 for detailed documentation on the relations API.
12
12
 
13
- In addition to defining relations, the Pothos drizzle plugin also requires defining a `primaryKey`
14
- for every table used as a `drizzleObject`, `drizzleNode`, or `drizzleInterface`.
15
-
16
- Once you have configured you have configured you drizzle schema, you can initialize your Pothos
13
+ Once you have configured your drizzle schema, you can initialize your Pothos
17
14
  SchemaBuilder with the drizzle plugin:
18
15
 
19
16
  ```ts
20
- import * as schema from './schema';
21
17
  import { drizzle } from 'drizzle-orm/...';
18
+ // Import the appropriate getTableConfig for your dialect
19
+ import { getTableConfig } from 'drizzle-orm/sqlite-core';
22
20
  import SchemaBuilder from '@pothos/core';
23
21
  import DrizzlePlugin from '@pothos/plugin-drizzle';
22
+ import { relations } from './db/relations';
23
+
24
+ const db = drizzle({ client, relations });
24
25
 
25
- const db = drizzle({ client, schema });
26
+ type DrizzleRelations = typeof relations
26
27
 
27
28
  export interface PothosTypes {
28
- DrizzleSchema: typeof schema;
29
+ DrizzleRelations: DrizzleRelations;
29
30
  }
30
31
 
31
32
  const builder = new SchemaBuilder<PothosTypes>({
32
33
  plugins: [DrizzlePlugin],
33
34
  drizzle: {
34
- client: db,
35
+ client: db, // or (ctx) => db if you want to create a request specific client
36
+ getTableConfig,
37
+ relations,
35
38
  },
36
39
  });
37
40
  ```
@@ -42,23 +45,26 @@ The drizzle plugin has integrations with several other plugins. While the `with-
42
45
  plugins are not required, many examples will assume these plugins have been installed:
43
46
 
44
47
  ```ts
45
- import * as schema from './schema';
46
48
  import { drizzle } from 'drizzle-orm/...';
47
49
  import SchemaBuilder from '@pothos/core';
48
50
  import DrizzlePlugin from '@pothos/plugin-drizzle';
49
- import RelayPlugin from '@pothos/plugin-scope-auth';
51
+ import RelayPlugin from '@pothos/plugin-relay';
50
52
  import WithInputPlugin from '@pothos/plugin-with-input';
53
+ import { getTableConfig } from 'drizzle-orm/sqlite-core';
54
+ import { relations } from './db/relations';
51
55
 
52
- const db = drizzle({ client, schema });
56
+ const db = drizzle({ client, relations });
53
57
 
54
58
  export interface PothosTypes {
55
- DrizzleSchema: typeof schema;
59
+ DrizzleRelations: typeof relations;
56
60
  }
57
61
 
58
62
  const builder = new SchemaBuilder<PothosTypes>({
59
63
  plugins: [RelayPlugin, WithInputPlugin, DrizzlePlugin],
60
64
  drizzle: {
61
65
  client: db,
66
+ getTableConfig,
67
+ relations,
62
68
  },
63
69
  });
64
70
  ```
@@ -72,8 +78,8 @@ table:
72
78
  const UserRef = builder.drizzleObject('users', {
73
79
  name: 'User',
74
80
  fields: (t) => ({
75
- firstName: t.exposeString('first_name'),
76
- lastName: t.exposeString('last_name'),
81
+ firstName: t.exposeString('firstName'),
82
+ lastName: t.exposeString('lastName'),
77
83
  }),
78
84
  });
79
85
  ```
@@ -92,7 +98,7 @@ const UserRef = builder.drizzleObject('users', {
92
98
  name: 'User',
93
99
  fields: (t) => ({
94
100
  fullName: t.string({
95
- resolve: (user, args, ctx, info) => `${user.first_name} ${user.last_name}`,
101
+ resolve: (user, args, ctx, info) => `${user.firstName} ${user.lastName}`,
96
102
  }),
97
103
  }),
98
104
  });
@@ -109,19 +115,19 @@ const UserRef = builder.drizzleObject('users', {
109
115
  name: 'User',
110
116
  select: {
111
117
  columns: {
112
- first_name: true,
113
- last_name: true,
118
+ firstName: true,
119
+ lastName: true,
114
120
  },
115
121
  with: {
116
122
  profile: true,
117
123
  },
118
124
  extras: {
119
- lowercaseName: sql<string>`lower(${users.firstName})`.as('lowercaseName'),
125
+ lowercaseName: (users, sql) => sql<string>`lower(${users.firstName})`
120
126
  },
121
127
  },
122
128
  fields: (t) => ({
123
129
  fullName: t.string({
124
- resolve: (user, args, ctx, info) => `${user.first_name} ${user.last_name}`,
130
+ resolve: (user, args, ctx, info) => `${user.firstName} ${user.lastName}`,
125
131
  }),
126
132
  bio: t.string({
127
133
  resolve: (user) => user.profile.bio,
@@ -145,30 +151,24 @@ appropriate selections on each field:
145
151
  ```ts
146
152
  const UserRef = builder.drizzleObject('users', {
147
153
  name: 'User',
148
- select: {
149
- // By default all columns are selected, so this is required to default to an empty selection
150
- columns: {},
151
- },
154
+ select: {},
152
155
  fields: (t) => ({
153
156
  fullName: t.string({
154
157
  select: {
155
- columns: { first_name: true, last_name: true },
158
+ columns: { firstName: true, lastName: true },
156
159
  },
157
- resolve: (user, args, ctx, info) => `${user.first_name} ${user.last_name}`,
160
+ resolve: (user, args, ctx, info) => `${user.firstName} ${user.lastName}`,
158
161
  }),
159
162
  bio: t.string({
160
163
  select: {
161
- // Currently, adding a selection without explicitly defining columns will cause all columns to be selected
162
- columns: {},
163
164
  with: { profile: true },
164
165
  },
165
166
  resolve: (user) => user.profile.bio,
166
167
  }),
167
168
  email: t.string({
168
169
  select: {
169
- columns: {},
170
170
  extras: {
171
- lowercaseName: sql<string>`lower(${users.firstName})`.as('lowercaseName'),
171
+ lowercaseName: (users, sql) => sql<string>`lower(${users.firstName})`
172
172
  },
173
173
  },
174
174
  resolve: (user) => `${user.lowercaseName}@example.com`,
@@ -180,7 +180,7 @@ const UserRef = builder.drizzleObject('users', {
180
180
  ## Relations
181
181
 
182
182
  Drizzles relational query builder allows you to define the relationships between your tables. The
183
- `builder.relation` method makes it easy to add fields to your GraphQL API that implement those
183
+ `t.relation` method makes it easy to add fields to your GraphQL API that implement those
184
184
  relations:
185
185
 
186
186
  ```ts
@@ -202,7 +202,7 @@ builder.drizzleObject('posts', {
202
202
  builder.drizzleObject('users', {
203
203
  name: 'User',
204
204
  fields: (t) => ({
205
- firstName: t.exposeString('first_name'),
205
+ firstName: t.exposeString('firstName'),
206
206
  profile: t.relation('profile'),
207
207
  posts: t.relation('posts'),
208
208
  }),
@@ -222,7 +222,7 @@ specifying a `query` option on the relation:
222
222
  builder.drizzleObject('users', {
223
223
  name: 'User',
224
224
  fields: (t) => ({
225
- firstName: t.exposeString('first_name'),
225
+ firstName: t.exposeString('firstName'),
226
226
  posts: t.relation('posts', {
227
227
  args: {
228
228
  limit: t.arg.int(),
@@ -231,13 +231,19 @@ builder.drizzleObject('users', {
231
231
  query: (args) => ({
232
232
  limit: args.limit ?? 10,
233
233
  offset: args.offset ?? 0,
234
- where: (post, { eq }) => eq(post.published, true),
235
- orderBy: (post, { desc }) => desc(post.updatedAt),
234
+ where: {
235
+ published: true,
236
+ },
237
+ orderBy: {
238
+ updatedAt: 'desc',
239
+ },
236
240
  }),
237
241
  }),
238
242
  drafts: t.relation('posts', {
239
243
  query: {
240
- where: (post, { eq }) => eq(post.published, false),
244
+ where: {
245
+ published: false,
246
+ },
241
247
  },
242
248
  }),
243
249
  }),
@@ -267,7 +273,9 @@ builder.queryType({
267
273
  resolve: (query, root, args, ctx) =>
268
274
  db.query.posts.findFirst(
269
275
  query({
270
- where: eq(posts.id, Number.parseInt(args.id, 10)),
276
+ where: {
277
+ id: Number.parseInt(args.id, 10),
278
+ },
271
279
  }),
272
280
  ),
273
281
  }),
@@ -298,9 +306,7 @@ done using a feature called `variants`. The `variants` API consists of 3 parts:
298
306
  // Viewer type representing the current user
299
307
  export const Viewer = builder.drizzleObject('users', {
300
308
  variant: 'Viewer',
301
- select: {
302
- columns: {},
303
- },
309
+ select: {},
304
310
  fields: (t) => ({
305
311
  id: t.exposeID('id'),
306
312
  // A reference to the normal user type so normal user fields can be queried
@@ -308,8 +314,12 @@ export const Viewer = builder.drizzleObject('users', {
308
314
  // Adding drafts to View allows a user to fetch their own drafts without exposing it for Other Users in the API
309
315
  drafts: t.relation('posts', {
310
316
  query: {
311
- where: (post, { eq }) => eq(post.published, false),
312
- orderBy: (post, ops) => ops.desc(post.updatedAt),
317
+ where: {
318
+ published: false,
319
+ },
320
+ orderBy: {
321
+ updatedAt: 'desc',
322
+ },
313
323
  },
314
324
  }),
315
325
  }),
@@ -323,7 +333,9 @@ builder.queryType({
323
333
  resolve: (query, root, args, ctx) =>
324
334
  db.query.users.findFirst(
325
335
  query({
326
- where: (user, { eq }) => eq(user.id, ctx.user.id),
336
+ where: {
337
+ id: ctx.user.id,
338
+ },
327
339
  }),
328
340
  ),
329
341
  }),
@@ -342,6 +354,79 @@ builder.drizzleNode('users', {
342
354
  });
343
355
  ```
344
356
 
357
+
358
+ ## Related field
359
+
360
+ The `t.relatedField` method allows you to define a field based on a relation that uses custom
361
+ selections, including aggregations like counts. This is useful when you want to expose derived data
362
+ from a relation without loading the full related records.
363
+
364
+ ### Count aggregations
365
+
366
+ One common use case is adding a count field that efficiently counts related records:
367
+
368
+ ```ts
369
+ import { count } from 'drizzle-orm';
370
+
371
+ builder.drizzleNode('users', {
372
+ name: 'User',
373
+ id: { column: (user) => user.id },
374
+ fields: (t) => ({
375
+ firstName: t.exposeString('firstName'),
376
+ // Add a count of related posts
377
+ postsCount: t.relatedField('posts', {
378
+ type: 'Int',
379
+ // buildFilter creates the correct WHERE clause for the relation
380
+ select: (buildFilter) => ({
381
+ extras: {
382
+ postsCount: (parent) => db.$count(posts, buildFilter(parent)),
383
+ },
384
+ }),
385
+ resolve: (user) => user.postsCount,
386
+ }),
387
+ }),
388
+ });
389
+ ```
390
+
391
+ The `buildFilter` function passed to `select` generates the appropriate SQL filter based on the
392
+ relation definition. This is no different than using `t.field`, but the `buildFilter` helper makes
393
+ it easier to filter for the related records.
394
+
395
+ ## Related count
396
+
397
+ For the common case of counting related records, there's a simpler `t.relatedCount` method that handles
398
+ all the boilerplate for you:
399
+
400
+ ```ts
401
+ builder.drizzleNode('users', {
402
+ name: 'User',
403
+ id: { column: (user) => user.id },
404
+ fields: (t) => ({
405
+ firstName: t.exposeString('firstName'),
406
+ // Simple count of all related comments
407
+ commentsCount: t.relatedCount('comments'),
408
+ // Count with a where filter
409
+ publishedPostsCount: t.relatedCount('posts', {
410
+ where: eq(posts.published, true),
411
+ }),
412
+ }),
413
+ });
414
+ ```
415
+
416
+ The `where` option accepts either a static SQL filter or a function that receives the field arguments
417
+ and context:
418
+
419
+ ```ts
420
+ publishedPostsCount: t.relatedCount('posts', {
421
+ args: {
422
+ category: t.arg.string(),
423
+ },
424
+ where: (args, ctx) => args.category
425
+ ? and(eq(posts.published, true), eq(posts.category, args.category))
426
+ : eq(posts.published, true),
427
+ }),
428
+ ```
429
+
345
430
  ## Relay integration
346
431
 
347
432
  Relay provides some very useful best practices that are useful for most GraphQL APIs. To make it
@@ -351,7 +436,7 @@ easy to comply with these best practices, the drizzle plugin has built in suppor
351
436
  ## Relay Nodes
352
437
 
353
438
  Defining relay nodes works just like defining normal `drizzleObject`s, but requires specifying a
354
- colum to use as the nodes `id` field.
439
+ column to use as the nodes `id` field.
355
440
 
356
441
  ```ts
357
442
  builder.drizzleNode('users', {
@@ -405,16 +490,61 @@ builder.drizzleNode('users', {
405
490
  fields: (t) => ({
406
491
  posts: t.relatedConnection('posts', {
407
492
  query: () => ({
408
- where: (post, { eq }) => eq(post.published, 1),
409
- orderBy: (post) => ({
410
- desc: post.id,
411
- }),
493
+ where: {
494
+ published: true,
495
+ },
496
+ orderBy: {
497
+ id: 'desc',
498
+ },
412
499
  }),
413
500
  }),
414
501
  }),
415
502
  });
416
503
  ```
417
504
 
505
+ ### Connection totalCount
506
+
507
+ You can add a `totalCount` field to your connection by setting the `totalCount` option to `true`:
508
+
509
+ ```ts
510
+ builder.drizzleNode('users', {
511
+ name: 'User',
512
+ fields: (t) => ({
513
+ posts: t.relatedConnection('posts', {
514
+ totalCount: true,
515
+ query: () => ({
516
+ where: {
517
+ published: true,
518
+ },
519
+ orderBy: {
520
+ id: 'desc',
521
+ },
522
+ }),
523
+ }),
524
+ }),
525
+ });
526
+ ```
527
+
528
+ This will automatically add a `totalCount` field to the connection type. The count query is only
529
+ executed when the `totalCount` field is actually requested in the GraphQL query, and it's included
530
+ as a subquery in the main database query for efficiency.
531
+
532
+ ```graphql
533
+ query {
534
+ user(id: "...") {
535
+ posts(first: 10) {
536
+ totalCount
537
+ edges {
538
+ node {
539
+ id
540
+ title
541
+ }
542
+ }
543
+ }
544
+ }
545
+ }
546
+ ```
547
+
418
548
  ## Drizzle connections
419
549
 
420
550
  Similar to `t.drizzleField`, `t.drizzleConnection` allows you to define a connection field that acts
@@ -428,10 +558,248 @@ builder.queryFields((t) => ({
428
558
  resolve: (query, root, args, ctx) =>
429
559
  db.query.posts.findMany(
430
560
  query({
431
- where: (post, { eq }) => eq(post.published, true),
432
- orderBy: (post) => ({ desc: post.id }),
561
+ where: {
562
+ published: true,
563
+ },
564
+ orderBy: {
565
+ id: 'desc',
566
+ },
433
567
  }),
434
568
  ),
435
569
  }),
436
570
  }));
437
571
  ```
572
+
573
+ ### drizzleConnection totalCount
574
+
575
+ You can add a `totalCount` field to a `drizzleConnection` by providing a `totalCount` callback function that returns the count:
576
+
577
+ ```ts
578
+ builder.queryFields((t) => ({
579
+ posts: t.drizzleConnection({
580
+ type: 'posts',
581
+ // Use db.$count() for a simple count query
582
+ totalCount: () => db.$count(posts, eq(posts.published, true)),
583
+ resolve: (query, root, args, ctx) =>
584
+ db.query.posts.findMany(
585
+ query({
586
+ where: {
587
+ published: true,
588
+ },
589
+ orderBy: {
590
+ id: 'desc',
591
+ },
592
+ }),
593
+ ),
594
+ }),
595
+ }));
596
+ ```
597
+
598
+ The `totalCount` callback receives the same arguments as a normal resolver (`parent`, `args`, `context`, `info`), allowing you to implement custom count logic based on the query context. The example above uses `db.$count()` for a simple count, but you can use any Drizzle query approach.
599
+
600
+ When only the `totalCount` field is requested (without `edges` or `nodes`), the main query is skipped entirely and only the count query is executed for efficiency.
601
+
602
+ ### Indirect relations as connections
603
+
604
+ In many cases, you can define many to many connections via drizzle relations, allowing the `relatedConnection` API to work across
605
+ more complex relations. In some cases you may want to define a connection for a relation not expressed directly as a relation in
606
+ your drizzle schema. For these cases, you can use the `drizzleConnectionHelpers`, which allows you to define connection with the `t.connection` API.
607
+
608
+ ```typescript
609
+ // Create a drizzle object for the node type of your connection
610
+ const Role = builder.drizzleObject('roles', {
611
+ name: 'Role',
612
+ fields: (t) => ({
613
+ id: t.exposeID('id'),
614
+ name: t.exposeString('name'),
615
+ }),
616
+ });
617
+
618
+
619
+
620
+ // Create connection helpers for the media type. This will allow you
621
+ // to use the normal t.connection with a drizzle type
622
+ const rolesConnection = drizzleConnectionHelpers(builder, 'userRoles', {
623
+ // select the data needed for the nodes
624
+ select: (nestedSelection) => ({
625
+ with: {
626
+ // use nestedSelection to create the correct selection for the node
627
+ role: nestedSelection(),
628
+ },
629
+ }),
630
+ // resolve the node from the returned list item
631
+ resolveNode: (userRole) => userRole.role,
632
+ });
633
+
634
+ builder.drizzleObjectField('User', 'rolesConnection', (t) =>
635
+ t.connection({
636
+ // The type for the Node
637
+ type: Role,
638
+ // since we are not using t.relatedConnection we need to manually
639
+ // include the selections for our connection
640
+ select: (args, ctx, nestedSelection) => ({
641
+ with: {
642
+ userRoles: rolesConnection.getQuery(args, ctx, nestedSelection),
643
+ },
644
+ }),
645
+ // This helper takes a list of nodes and formats them for the connection
646
+ resolve: (user, args, ctx) => {
647
+ return rolesConnection.resolve(user.userRoles, args, ctx, user);
648
+ },
649
+ }),
650
+ );
651
+ ```
652
+
653
+ The above example assumes that you are paginating a relation to a join table, where the pagination
654
+ args are applied based on the relation to that join table, but the nodes themselves are nested
655
+ deeper.
656
+
657
+ `drizzleConnectionHelpers` can also be used to manually create a connection where the edge and
658
+ connections share the same model, and pagination happens directly on a relation to nodes type (even
659
+ if that relation is nested).
660
+
661
+ ```ts
662
+ const commentConnectionHelpers = drizzleConnectionHelpers(builder, 'Comment');
663
+
664
+ const SelectPost = builder.drizzleObject('posts', {
665
+ fields: (t) => ({
666
+ title: t.exposeString('title'),
667
+ comments: t.connection({
668
+ type: commentConnectionHelpers.ref,
669
+ select: (args, ctx, nestedSelection) => ({
670
+ with: {
671
+ comments: commentConnectionHelpers.getQuery(args, ctx, nestedSelection),
672
+ },
673
+ }),
674
+ resolve: (parent, args, ctx) => commentConnectionHelpers.resolve(parent.comments, args, ctx),
675
+ }),
676
+ }),
677
+ });
678
+ ```
679
+
680
+ Arguments, ordering and filtering can also be defined in the helpers:
681
+
682
+ ```ts
683
+ const rolesConnection = drizzleConnectionHelpers(builder, 'userRoles', {
684
+ // define additional arguments
685
+ args: (t) => ({}),
686
+ query: (args) => ({
687
+ // define an order
688
+ orderBy: {
689
+ roleId: 'asc',
690
+ }
691
+ // define a filter
692
+ where: {
693
+ accepted: true,
694
+ }
695
+ }),
696
+ // select the data needed for the nodes
697
+ select: (nestedSelection) => ({
698
+ with: {
699
+ // use nestedSelection to create the correct selection for the node
700
+ role: nestedSelection(),
701
+ },
702
+ }),
703
+ // resolve the node from the returned list item
704
+ resolveNode: (userRole) => userRole.role,
705
+ });
706
+
707
+
708
+ builder.drizzleObjectField('User', 'rolesConnection', (t) =>
709
+ t.connection({
710
+ type: Role,
711
+ // add the args from the connection helper to the field
712
+ args: rolesConnection.getArgs(),
713
+ select: (args, ctx, nestedSelection) => ({
714
+ with: {
715
+ userRoles: rolesConnection.getQuery(args, ctx, nestedSelection),
716
+ },
717
+ }),
718
+ resolve: (user, args, ctx) => rolesConnection.resolve(user.userRoles, args, ctx, user),
719
+ }),
720
+ );
721
+ ```
722
+
723
+ ### Extending connection edges
724
+
725
+ In some cases you may want to expose some data from an indirect connection on the edge object.
726
+
727
+ ```typescript
728
+ const rolesConnection = drizzleConnectionHelpers(builder, 'userRoles', {
729
+ select: (nestedSelection) => ({
730
+ with: {
731
+ role: nestedSelection(),
732
+ },
733
+ }),
734
+ resolveNode: (userRole) => userRole.role,
735
+ });
736
+
737
+ builder.drizzleObjectFields('User', (t) => ({
738
+ rolesConnection: t.connection(
739
+ {
740
+ type: Role,
741
+ select: (args, ctx, nestedSelection) => ({
742
+ with: {
743
+ userRoles: rolesConnection.getQuery(args, ctx, nestedSelection),
744
+ },
745
+ }),
746
+ resolve: (user, args, ctx) =>
747
+ rolesConnection.resolve(
748
+ user.userRoles,
749
+ args,
750
+ ctx,
751
+ user,
752
+ ),
753
+ },
754
+ {},
755
+ // options for the edge object
756
+ {
757
+ // define the additional fields on the edge object
758
+ fields: (edge) => ({
759
+ createdAt: edge.field({
760
+ type: 'DateTime',
761
+ // the parent shape for edge fields is inferred from the connections resolve function
762
+ resolve: (role) => role.createdAt,
763
+ }),
764
+ }),
765
+ },
766
+ ),
767
+ }));
768
+ ```
769
+
770
+ ### `drizzleConnectionHelpers` for non-relation connections
771
+
772
+ You can also use `drizzleConnectionHelpers` for non-relation connections where you want a connection where your edges and nodes are not the same type.
773
+
774
+ Note that when doing this, you need to be careful to properly merge the `where` clause generated by the connection helper with any additional `where` clause you need to apply to your query
775
+
776
+ ```typescript
777
+ const rolesConnection = drizzleConnectionHelpers(builder, 'userRoles', {
778
+ select: (nestedSelection) => ({
779
+ with: {
780
+ role: nestedSelection(),
781
+ },
782
+ }),
783
+ resolveNode: (userRole) => userRole.role,
784
+ });
785
+
786
+ builder.queryFields((t) => ({
787
+ roles: t.connection({
788
+ type: Role,
789
+ args: {
790
+ userId: t.arg.int({ required: true }),
791
+ },
792
+ nodeNullable: true,
793
+ resolve: async (_, args, ctx, info) => {
794
+ const query = rolesConnection.getQuery(args, ctx, info);
795
+ const userRoles = await db.query.userRoles.findMany({
796
+ ...query,
797
+ where: {
798
+ ...query.where,
799
+ userId: args.userId,
800
+ },
801
+ });
802
+ return rolesConnection.resolve(userRoles, args, ctx);
803
+ },
804
+ }),
805
+ }));