@constructive-io/graphql-codegen 2.24.0 → 2.26.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.
- package/README.md +403 -279
- package/cli/codegen/babel-ast.d.ts +7 -0
- package/cli/codegen/babel-ast.js +15 -0
- package/cli/codegen/barrel.js +43 -14
- package/cli/codegen/custom-mutations.js +4 -4
- package/cli/codegen/custom-queries.js +12 -22
- package/cli/codegen/gql-ast.js +22 -1
- package/cli/codegen/index.js +1 -0
- package/cli/codegen/mutations.d.ts +2 -0
- package/cli/codegen/mutations.js +26 -13
- package/cli/codegen/orm/client-generator.js +475 -136
- package/cli/codegen/orm/custom-ops-generator.js +8 -3
- package/cli/codegen/orm/input-types-generator.js +22 -0
- package/cli/codegen/orm/model-generator.js +18 -5
- package/cli/codegen/orm/select-types.d.ts +33 -0
- package/cli/codegen/queries.d.ts +1 -1
- package/cli/codegen/queries.js +112 -35
- package/cli/codegen/utils.d.ts +6 -0
- package/cli/codegen/utils.js +19 -0
- package/cli/commands/generate-orm.d.ts +14 -0
- package/cli/commands/generate-orm.js +160 -44
- package/cli/commands/generate.d.ts +22 -0
- package/cli/commands/generate.js +195 -55
- package/cli/commands/init.js +29 -9
- package/cli/index.js +133 -28
- package/cli/watch/orchestrator.d.ts +4 -0
- package/cli/watch/orchestrator.js +4 -0
- package/esm/cli/codegen/babel-ast.d.ts +7 -0
- package/esm/cli/codegen/babel-ast.js +14 -0
- package/esm/cli/codegen/barrel.js +44 -15
- package/esm/cli/codegen/custom-mutations.js +5 -5
- package/esm/cli/codegen/custom-queries.js +13 -23
- package/esm/cli/codegen/gql-ast.js +23 -2
- package/esm/cli/codegen/index.js +1 -0
- package/esm/cli/codegen/mutations.d.ts +2 -0
- package/esm/cli/codegen/mutations.js +27 -14
- package/esm/cli/codegen/orm/client-generator.js +475 -136
- package/esm/cli/codegen/orm/custom-ops-generator.js +8 -3
- package/esm/cli/codegen/orm/input-types-generator.js +22 -0
- package/esm/cli/codegen/orm/model-generator.js +18 -5
- package/esm/cli/codegen/orm/select-types.d.ts +33 -0
- package/esm/cli/codegen/queries.d.ts +1 -1
- package/esm/cli/codegen/queries.js +114 -37
- package/esm/cli/codegen/utils.d.ts +6 -0
- package/esm/cli/codegen/utils.js +18 -0
- package/esm/cli/commands/generate-orm.d.ts +14 -0
- package/esm/cli/commands/generate-orm.js +161 -45
- package/esm/cli/commands/generate.d.ts +22 -0
- package/esm/cli/commands/generate.js +195 -56
- package/esm/cli/commands/init.js +29 -9
- package/esm/cli/index.js +134 -29
- package/esm/cli/watch/orchestrator.d.ts +4 -0
- package/esm/cli/watch/orchestrator.js +5 -1
- package/esm/types/config.d.ts +39 -2
- package/esm/types/config.js +88 -4
- package/esm/types/index.d.ts +2 -2
- package/esm/types/index.js +1 -1
- package/package.json +10 -7
- package/types/config.d.ts +39 -2
- package/types/config.js +91 -4
- package/types/index.d.ts +2 -2
- package/types/index.js +2 -1
- package/cli/codegen/orm/query-builder.d.ts +0 -161
- package/cli/codegen/orm/query-builder.js +0 -366
- package/esm/cli/codegen/orm/query-builder.d.ts +0 -161
- package/esm/cli/codegen/orm/query-builder.js +0 -353
package/README.md
CHANGED
|
@@ -117,6 +117,7 @@ Generate React Query hooks from a PostGraphile endpoint.
|
|
|
117
117
|
```bash
|
|
118
118
|
Options:
|
|
119
119
|
-e, --endpoint <url> GraphQL endpoint URL (overrides config)
|
|
120
|
+
-t, --target <name> Target name in config file
|
|
120
121
|
-o, --output <dir> Output directory (default: ./generated/graphql)
|
|
121
122
|
-c, --config <path> Path to config file
|
|
122
123
|
-a, --authorization <token> Authorization header value
|
|
@@ -132,6 +133,7 @@ Generate Prisma-like ORM client from a PostGraphile endpoint.
|
|
|
132
133
|
```bash
|
|
133
134
|
Options:
|
|
134
135
|
-e, --endpoint <url> GraphQL endpoint URL
|
|
136
|
+
-t, --target <name> Target name in config file
|
|
135
137
|
-o, --output <dir> Output directory (default: ./generated/orm)
|
|
136
138
|
-c, --config <path> Path to config file
|
|
137
139
|
-a, --authorization <token> Authorization header value
|
|
@@ -164,9 +166,10 @@ Options:
|
|
|
164
166
|
## Configuration
|
|
165
167
|
|
|
166
168
|
```typescript
|
|
167
|
-
interface
|
|
168
|
-
// Required
|
|
169
|
-
endpoint
|
|
169
|
+
interface GraphQLSDKConfigTarget {
|
|
170
|
+
// Required (choose one)
|
|
171
|
+
endpoint?: string;
|
|
172
|
+
schema?: string;
|
|
170
173
|
|
|
171
174
|
// Output
|
|
172
175
|
output?: string; // default: './generated/graphql'
|
|
@@ -194,25 +197,62 @@ interface GraphQLSDKConfig {
|
|
|
194
197
|
|
|
195
198
|
// Code generation options
|
|
196
199
|
codegen?: {
|
|
197
|
-
maxFieldDepth?: number;
|
|
198
|
-
skipQueryField?: boolean;
|
|
200
|
+
maxFieldDepth?: number; // default: 2
|
|
201
|
+
skipQueryField?: boolean; // default: true
|
|
199
202
|
};
|
|
200
203
|
|
|
201
204
|
// ORM-specific config
|
|
202
205
|
orm?: {
|
|
203
|
-
output?: string;
|
|
204
|
-
useSharedTypes?: boolean;
|
|
206
|
+
output?: string; // default: './generated/orm'
|
|
207
|
+
useSharedTypes?: boolean; // default: true
|
|
205
208
|
};
|
|
206
209
|
}
|
|
210
|
+
|
|
211
|
+
interface GraphQLSDKMultiConfig {
|
|
212
|
+
defaults?: GraphQLSDKConfigTarget;
|
|
213
|
+
targets: Record<string, GraphQLSDKConfigTarget>;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
type GraphQLSDKConfig = GraphQLSDKConfigTarget | GraphQLSDKMultiConfig;
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Multi-target Configuration
|
|
220
|
+
|
|
221
|
+
Configure multiple schema sources and outputs in one file:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
export default defineConfig({
|
|
225
|
+
defaults: {
|
|
226
|
+
headers: { Authorization: 'Bearer <token>' },
|
|
227
|
+
},
|
|
228
|
+
targets: {
|
|
229
|
+
public: {
|
|
230
|
+
endpoint: 'https://api.example.com/graphql',
|
|
231
|
+
output: './generated/public',
|
|
232
|
+
},
|
|
233
|
+
admin: {
|
|
234
|
+
schema: './admin.schema.graphql',
|
|
235
|
+
output: './generated/admin',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
});
|
|
207
239
|
```
|
|
208
240
|
|
|
241
|
+
CLI behavior:
|
|
242
|
+
|
|
243
|
+
- `graphql-codegen generate` runs all targets
|
|
244
|
+
- `graphql-codegen generate --target admin` runs a single target
|
|
245
|
+
- `--output` requires `--target` when multiple targets exist
|
|
246
|
+
|
|
209
247
|
### Glob Patterns
|
|
210
248
|
|
|
211
249
|
Filter patterns support wildcards:
|
|
250
|
+
|
|
212
251
|
- `*` - matches any string
|
|
213
252
|
- `?` - matches single character
|
|
214
253
|
|
|
215
254
|
Examples:
|
|
255
|
+
|
|
216
256
|
```typescript
|
|
217
257
|
{
|
|
218
258
|
tables: {
|
|
@@ -308,30 +348,25 @@ Fetches multiple records with pagination, filtering, and ordering:
|
|
|
308
348
|
import { useCarsQuery } from './generated/hooks';
|
|
309
349
|
|
|
310
350
|
function CarList() {
|
|
311
|
-
const {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
// Ordering
|
|
333
|
-
orderBy: ['CREATED_AT_DESC', 'NAME_ASC'],
|
|
334
|
-
});
|
|
351
|
+
const { data, isLoading, isError, error, refetch, isFetching } = useCarsQuery(
|
|
352
|
+
{
|
|
353
|
+
// Pagination
|
|
354
|
+
first: 10, // First N records
|
|
355
|
+
// last: 10, // Last N records
|
|
356
|
+
// after: 'cursor', // Cursor-based pagination
|
|
357
|
+
// before: 'cursor',
|
|
358
|
+
// offset: 20, // Offset pagination
|
|
359
|
+
|
|
360
|
+
// Filtering
|
|
361
|
+
filter: {
|
|
362
|
+
brand: { equalTo: 'Tesla' },
|
|
363
|
+
price: { greaterThan: 50000 },
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
// Ordering
|
|
367
|
+
orderBy: ['CREATED_AT_DESC', 'NAME_ASC'],
|
|
368
|
+
}
|
|
369
|
+
);
|
|
335
370
|
|
|
336
371
|
if (isLoading) return <div>Loading...</div>;
|
|
337
372
|
if (isError) return <div>Error: {error.message}</div>;
|
|
@@ -340,11 +375,13 @@ function CarList() {
|
|
|
340
375
|
<div>
|
|
341
376
|
<p>Total: {data?.cars.totalCount}</p>
|
|
342
377
|
<ul>
|
|
343
|
-
{data?.cars.nodes.map(car => (
|
|
344
|
-
<li key={car.id}>
|
|
378
|
+
{data?.cars.nodes.map((car) => (
|
|
379
|
+
<li key={car.id}>
|
|
380
|
+
{car.brand} - ${car.price}
|
|
381
|
+
</li>
|
|
345
382
|
))}
|
|
346
383
|
</ul>
|
|
347
|
-
|
|
384
|
+
|
|
348
385
|
{/* Pagination info */}
|
|
349
386
|
{data?.cars.pageInfo.hasNextPage && (
|
|
350
387
|
<button onClick={() => refetch()}>Load More</button>
|
|
@@ -411,7 +448,12 @@ function CreateCarForm() {
|
|
|
411
448
|
};
|
|
412
449
|
|
|
413
450
|
return (
|
|
414
|
-
<form
|
|
451
|
+
<form
|
|
452
|
+
onSubmit={(e) => {
|
|
453
|
+
e.preventDefault();
|
|
454
|
+
handleSubmit({ brand: 'Tesla', price: 80000 });
|
|
455
|
+
}}
|
|
456
|
+
>
|
|
415
457
|
{/* form fields */}
|
|
416
458
|
<button type="submit" disabled={createCar.isPending}>
|
|
417
459
|
{createCar.isPending ? 'Creating...' : 'Create Car'}
|
|
@@ -427,7 +469,13 @@ function CreateCarForm() {
|
|
|
427
469
|
```tsx
|
|
428
470
|
import { useUpdateCarMutation } from './generated/hooks';
|
|
429
471
|
|
|
430
|
-
function EditCarForm({
|
|
472
|
+
function EditCarForm({
|
|
473
|
+
carId,
|
|
474
|
+
currentBrand,
|
|
475
|
+
}: {
|
|
476
|
+
carId: string;
|
|
477
|
+
currentBrand: string;
|
|
478
|
+
}) {
|
|
431
479
|
const updateCar = useUpdateCarMutation({
|
|
432
480
|
onSuccess: (data) => {
|
|
433
481
|
console.log('Updated car:', data.updateCar.car.brand);
|
|
@@ -446,7 +494,7 @@ function EditCarForm({ carId, currentBrand }: { carId: string; currentBrand: str
|
|
|
446
494
|
};
|
|
447
495
|
|
|
448
496
|
return (
|
|
449
|
-
<button
|
|
497
|
+
<button
|
|
450
498
|
onClick={() => handleUpdate('Updated Brand')}
|
|
451
499
|
disabled={updateCar.isPending}
|
|
452
500
|
>
|
|
@@ -470,7 +518,7 @@ function DeleteCarButton({ carId }: { carId: string }) {
|
|
|
470
518
|
});
|
|
471
519
|
|
|
472
520
|
return (
|
|
473
|
-
<button
|
|
521
|
+
<button
|
|
474
522
|
onClick={() => deleteCar.mutate({ input: { id: carId } })}
|
|
475
523
|
disabled={deleteCar.isPending}
|
|
476
524
|
>
|
|
@@ -517,9 +565,9 @@ function NodeViewer({ nodeId }: { nodeId: string }) {
|
|
|
517
565
|
Custom mutations (like `login`, `register`, `logout`) get dedicated hooks:
|
|
518
566
|
|
|
519
567
|
```tsx
|
|
520
|
-
import {
|
|
521
|
-
useLoginMutation,
|
|
522
|
-
useRegisterMutation,
|
|
568
|
+
import {
|
|
569
|
+
useLoginMutation,
|
|
570
|
+
useRegisterMutation,
|
|
523
571
|
useLogoutMutation,
|
|
524
572
|
useForgotPasswordMutation,
|
|
525
573
|
} from './generated/hooks';
|
|
@@ -550,7 +598,12 @@ function LoginForm() {
|
|
|
550
598
|
};
|
|
551
599
|
|
|
552
600
|
return (
|
|
553
|
-
<form
|
|
601
|
+
<form
|
|
602
|
+
onSubmit={(e) => {
|
|
603
|
+
e.preventDefault();
|
|
604
|
+
handleLogin('user@example.com', 'password');
|
|
605
|
+
}}
|
|
606
|
+
>
|
|
554
607
|
{/* email and password inputs */}
|
|
555
608
|
<button disabled={login.isPending}>
|
|
556
609
|
{login.isPending ? 'Logging in...' : 'Login'}
|
|
@@ -567,7 +620,11 @@ function RegisterForm() {
|
|
|
567
620
|
},
|
|
568
621
|
});
|
|
569
622
|
|
|
570
|
-
const handleRegister = (data: {
|
|
623
|
+
const handleRegister = (data: {
|
|
624
|
+
email: string;
|
|
625
|
+
password: string;
|
|
626
|
+
username: string;
|
|
627
|
+
}) => {
|
|
571
628
|
register.mutate({
|
|
572
629
|
input: {
|
|
573
630
|
email: data.email,
|
|
@@ -578,7 +635,15 @@ function RegisterForm() {
|
|
|
578
635
|
};
|
|
579
636
|
|
|
580
637
|
return (
|
|
581
|
-
<button
|
|
638
|
+
<button
|
|
639
|
+
onClick={() =>
|
|
640
|
+
handleRegister({
|
|
641
|
+
email: 'new@example.com',
|
|
642
|
+
password: 'secret',
|
|
643
|
+
username: 'newuser',
|
|
644
|
+
})
|
|
645
|
+
}
|
|
646
|
+
>
|
|
582
647
|
Register
|
|
583
648
|
</button>
|
|
584
649
|
);
|
|
@@ -593,11 +658,7 @@ function LogoutButton() {
|
|
|
593
658
|
},
|
|
594
659
|
});
|
|
595
660
|
|
|
596
|
-
return (
|
|
597
|
-
<button onClick={() => logout.mutate({ input: {} })}>
|
|
598
|
-
Logout
|
|
599
|
-
</button>
|
|
600
|
-
);
|
|
661
|
+
return <button onClick={() => logout.mutate({ input: {} })}>Logout</button>;
|
|
601
662
|
}
|
|
602
663
|
|
|
603
664
|
// Forgot Password
|
|
@@ -609,7 +670,11 @@ function ForgotPasswordForm() {
|
|
|
609
670
|
});
|
|
610
671
|
|
|
611
672
|
return (
|
|
612
|
-
<button
|
|
673
|
+
<button
|
|
674
|
+
onClick={() =>
|
|
675
|
+
forgotPassword.mutate({ input: { email: 'user@example.com' } })
|
|
676
|
+
}
|
|
677
|
+
>
|
|
613
678
|
Reset Password
|
|
614
679
|
</button>
|
|
615
680
|
);
|
|
@@ -629,10 +694,10 @@ useCarsQuery({
|
|
|
629
694
|
notEqualTo: 'Ford',
|
|
630
695
|
in: ['Tesla', 'BMW', 'Mercedes'],
|
|
631
696
|
notIn: ['Unknown'],
|
|
632
|
-
contains: 'es',
|
|
633
|
-
startsWith: 'Tes',
|
|
634
|
-
endsWith: 'la',
|
|
635
|
-
includesInsensitive: 'TESLA',
|
|
697
|
+
contains: 'es', // LIKE '%es%'
|
|
698
|
+
startsWith: 'Tes', // LIKE 'Tes%'
|
|
699
|
+
endsWith: 'la', // LIKE '%la'
|
|
700
|
+
includesInsensitive: 'TESLA', // Case-insensitive
|
|
636
701
|
},
|
|
637
702
|
},
|
|
638
703
|
});
|
|
@@ -671,7 +736,7 @@ useOrdersQuery({
|
|
|
671
736
|
// Null checks
|
|
672
737
|
useUsersQuery({
|
|
673
738
|
filter: {
|
|
674
|
-
deletedAt: { isNull: true },
|
|
739
|
+
deletedAt: { isNull: true }, // Only non-deleted
|
|
675
740
|
},
|
|
676
741
|
});
|
|
677
742
|
|
|
@@ -687,10 +752,7 @@ useUsersQuery({
|
|
|
687
752
|
useUsersQuery({
|
|
688
753
|
filter: {
|
|
689
754
|
// OR
|
|
690
|
-
or: [
|
|
691
|
-
{ role: { equalTo: 'ADMIN' } },
|
|
692
|
-
{ role: { equalTo: 'MODERATOR' } },
|
|
693
|
-
],
|
|
755
|
+
or: [{ role: { equalTo: 'ADMIN' } }, { role: { equalTo: 'MODERATOR' } }],
|
|
694
756
|
},
|
|
695
757
|
});
|
|
696
758
|
|
|
@@ -746,12 +808,12 @@ useCarsQuery({ first: 10 });
|
|
|
746
808
|
useCarsQuery({ last: 10 });
|
|
747
809
|
|
|
748
810
|
// Offset pagination
|
|
749
|
-
useCarsQuery({ first: 10, offset: 20 });
|
|
811
|
+
useCarsQuery({ first: 10, offset: 20 }); // Skip 20, take 10
|
|
750
812
|
|
|
751
813
|
// Cursor-based pagination
|
|
752
814
|
function PaginatedList() {
|
|
753
815
|
const [cursor, setCursor] = useState<string | null>(null);
|
|
754
|
-
|
|
816
|
+
|
|
755
817
|
const { data } = useCarsQuery({
|
|
756
818
|
first: 10,
|
|
757
819
|
after: cursor,
|
|
@@ -759,8 +821,10 @@ function PaginatedList() {
|
|
|
759
821
|
|
|
760
822
|
return (
|
|
761
823
|
<div>
|
|
762
|
-
{data?.cars.nodes.map(car =>
|
|
763
|
-
|
|
824
|
+
{data?.cars.nodes.map((car) => (
|
|
825
|
+
<div key={car.id}>{car.brand}</div>
|
|
826
|
+
))}
|
|
827
|
+
|
|
764
828
|
{data?.cars.pageInfo.hasNextPage && (
|
|
765
829
|
<button onClick={() => setCursor(data.cars.pageInfo.endCursor)}>
|
|
766
830
|
Load More
|
|
@@ -786,18 +850,18 @@ All hooks accept standard React Query options:
|
|
|
786
850
|
```tsx
|
|
787
851
|
// Query hooks
|
|
788
852
|
useCarsQuery(
|
|
789
|
-
{ first: 10 },
|
|
853
|
+
{ first: 10 }, // Variables
|
|
790
854
|
{
|
|
791
855
|
// React Query options
|
|
792
|
-
enabled: isAuthenticated,
|
|
793
|
-
refetchInterval: 30000,
|
|
794
|
-
refetchOnWindowFocus: true,
|
|
795
|
-
staleTime: 5 * 60 * 1000,
|
|
796
|
-
gcTime: 30 * 60 * 1000,
|
|
797
|
-
retry: 3,
|
|
856
|
+
enabled: isAuthenticated, // Conditional fetching
|
|
857
|
+
refetchInterval: 30000, // Poll every 30s
|
|
858
|
+
refetchOnWindowFocus: true, // Refetch on tab focus
|
|
859
|
+
staleTime: 5 * 60 * 1000, // Consider fresh for 5 min
|
|
860
|
+
gcTime: 30 * 60 * 1000, // Keep in cache for 30 min
|
|
861
|
+
retry: 3, // Retry failed requests
|
|
798
862
|
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
799
|
-
placeholderData: previousData,
|
|
800
|
-
select: (data) => data.cars.nodes,
|
|
863
|
+
placeholderData: previousData, // Show previous data while loading
|
|
864
|
+
select: (data) => data.cars.nodes, // Transform data
|
|
801
865
|
}
|
|
802
866
|
);
|
|
803
867
|
|
|
@@ -858,11 +922,11 @@ The codegen generates a centralized query key factory following the [lukemorales
|
|
|
858
922
|
|
|
859
923
|
#### Generated Files
|
|
860
924
|
|
|
861
|
-
| File
|
|
862
|
-
|
|
863
|
-
| `query-keys.ts`
|
|
925
|
+
| File | Purpose |
|
|
926
|
+
| ------------------ | ------------------------------------------------------- |
|
|
927
|
+
| `query-keys.ts` | Query key factories for all entities |
|
|
864
928
|
| `mutation-keys.ts` | Mutation key factories for tracking in-flight mutations |
|
|
865
|
-
| `invalidation.ts`
|
|
929
|
+
| `invalidation.ts` | Type-safe cache invalidation helpers |
|
|
866
930
|
|
|
867
931
|
#### Using Query Keys
|
|
868
932
|
|
|
@@ -871,11 +935,11 @@ import { userKeys, invalidate } from './generated/hooks';
|
|
|
871
935
|
import { useQueryClient } from '@tanstack/react-query';
|
|
872
936
|
|
|
873
937
|
// Query key structure
|
|
874
|
-
userKeys.all
|
|
875
|
-
userKeys.lists()
|
|
876
|
-
userKeys.list({ first: 10 })
|
|
877
|
-
userKeys.details()
|
|
878
|
-
userKeys.detail('user-123')
|
|
938
|
+
userKeys.all; // ['user']
|
|
939
|
+
userKeys.lists(); // ['user', 'list']
|
|
940
|
+
userKeys.list({ first: 10 }); // ['user', 'list', { first: 10 }]
|
|
941
|
+
userKeys.details(); // ['user', 'detail']
|
|
942
|
+
userKeys.detail('user-123'); // ['user', 'detail', 'user-123']
|
|
879
943
|
|
|
880
944
|
// Granular cache invalidation
|
|
881
945
|
const queryClient = useQueryClient();
|
|
@@ -920,7 +984,7 @@ function UserList() {
|
|
|
920
984
|
|
|
921
985
|
// Check if a specific user is being deleted
|
|
922
986
|
const isDeleting = useIsMutating({
|
|
923
|
-
mutationKey: userMutationKeys.delete(userId)
|
|
987
|
+
mutationKey: userMutationKeys.delete(userId),
|
|
924
988
|
});
|
|
925
989
|
|
|
926
990
|
return (
|
|
@@ -950,7 +1014,7 @@ const createUser = useCreateUserMutation({
|
|
|
950
1014
|
...old,
|
|
951
1015
|
users: {
|
|
952
1016
|
...old.users,
|
|
953
|
-
nodes: [...old.users.nodes, { id: 'temp', ...newUser.input.user }]
|
|
1017
|
+
nodes: [...old.users.nodes, { id: 'temp', ...newUser.input.user }],
|
|
954
1018
|
},
|
|
955
1019
|
}));
|
|
956
1020
|
|
|
@@ -1032,7 +1096,7 @@ import type {
|
|
|
1032
1096
|
User,
|
|
1033
1097
|
Product,
|
|
1034
1098
|
Order,
|
|
1035
|
-
|
|
1099
|
+
|
|
1036
1100
|
// Filter types
|
|
1037
1101
|
CarFilter,
|
|
1038
1102
|
UserFilter,
|
|
@@ -1040,17 +1104,17 @@ import type {
|
|
|
1040
1104
|
IntFilter,
|
|
1041
1105
|
UUIDFilter,
|
|
1042
1106
|
DatetimeFilter,
|
|
1043
|
-
|
|
1107
|
+
|
|
1044
1108
|
// OrderBy types
|
|
1045
1109
|
CarsOrderBy,
|
|
1046
1110
|
UsersOrderBy,
|
|
1047
|
-
|
|
1111
|
+
|
|
1048
1112
|
// Input types
|
|
1049
1113
|
CreateCarInput,
|
|
1050
1114
|
UpdateCarInput,
|
|
1051
1115
|
CarPatch,
|
|
1052
1116
|
LoginInput,
|
|
1053
|
-
|
|
1117
|
+
|
|
1054
1118
|
// Payload types
|
|
1055
1119
|
LoginPayload,
|
|
1056
1120
|
CreateCarPayload,
|
|
@@ -1131,7 +1195,7 @@ type UseQueryResult<TData> = {
|
|
|
1131
1195
|
type UseMutationResult<TData, TVariables> = {
|
|
1132
1196
|
data: TData | undefined;
|
|
1133
1197
|
error: Error | null;
|
|
1134
|
-
isLoading: boolean;
|
|
1198
|
+
isLoading: boolean; // deprecated, use isPending
|
|
1135
1199
|
isPending: boolean;
|
|
1136
1200
|
isError: boolean;
|
|
1137
1201
|
isSuccess: boolean;
|
|
@@ -1196,10 +1260,12 @@ const db = createClient({
|
|
|
1196
1260
|
});
|
|
1197
1261
|
|
|
1198
1262
|
// Query users
|
|
1199
|
-
const result = await db.user
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1263
|
+
const result = await db.user
|
|
1264
|
+
.findMany({
|
|
1265
|
+
select: { id: true, username: true, email: true },
|
|
1266
|
+
first: 20,
|
|
1267
|
+
})
|
|
1268
|
+
.execute();
|
|
1203
1269
|
|
|
1204
1270
|
if (result.ok) {
|
|
1205
1271
|
console.log(result.data.users.nodes);
|
|
@@ -1208,28 +1274,36 @@ if (result.ok) {
|
|
|
1208
1274
|
}
|
|
1209
1275
|
|
|
1210
1276
|
// Find first matching user
|
|
1211
|
-
const user = await db.user
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1277
|
+
const user = await db.user
|
|
1278
|
+
.findFirst({
|
|
1279
|
+
select: { id: true, username: true },
|
|
1280
|
+
where: { username: { equalTo: 'john' } },
|
|
1281
|
+
})
|
|
1282
|
+
.execute();
|
|
1215
1283
|
|
|
1216
1284
|
// Create a user
|
|
1217
|
-
const newUser = await db.user
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
}
|
|
1285
|
+
const newUser = await db.user
|
|
1286
|
+
.create({
|
|
1287
|
+
data: { username: 'john', email: 'john@example.com' },
|
|
1288
|
+
select: { id: true, username: true },
|
|
1289
|
+
})
|
|
1290
|
+
.execute();
|
|
1221
1291
|
|
|
1222
1292
|
// Update a user
|
|
1223
|
-
const updated = await db.user
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}
|
|
1293
|
+
const updated = await db.user
|
|
1294
|
+
.update({
|
|
1295
|
+
where: { id: 'user-id' },
|
|
1296
|
+
data: { displayName: 'John Doe' },
|
|
1297
|
+
select: { id: true, displayName: true },
|
|
1298
|
+
})
|
|
1299
|
+
.execute();
|
|
1228
1300
|
|
|
1229
1301
|
// Delete a user
|
|
1230
|
-
const deleted = await db.user
|
|
1231
|
-
|
|
1232
|
-
}
|
|
1302
|
+
const deleted = await db.user
|
|
1303
|
+
.delete({
|
|
1304
|
+
where: { id: 'user-id' },
|
|
1305
|
+
})
|
|
1306
|
+
.execute();
|
|
1233
1307
|
```
|
|
1234
1308
|
|
|
1235
1309
|
### Select & Type Inference
|
|
@@ -1238,9 +1312,11 @@ The ORM uses **const generics** to infer return types based on your select claus
|
|
|
1238
1312
|
|
|
1239
1313
|
```typescript
|
|
1240
1314
|
// Select specific fields - return type is narrowed
|
|
1241
|
-
const users = await db.user
|
|
1242
|
-
|
|
1243
|
-
}
|
|
1315
|
+
const users = await db.user
|
|
1316
|
+
.findMany({
|
|
1317
|
+
select: { id: true, username: true }, // Only id and username
|
|
1318
|
+
})
|
|
1319
|
+
.unwrap();
|
|
1244
1320
|
|
|
1245
1321
|
// TypeScript knows the exact shape:
|
|
1246
1322
|
// users.users.nodes[0] is { id: string; username: string | null }
|
|
@@ -1261,16 +1337,18 @@ Relations are fully typed in Select types. The ORM supports all PostGraphile rel
|
|
|
1261
1337
|
|
|
1262
1338
|
```typescript
|
|
1263
1339
|
// Order.customer is a belongsTo relation to User
|
|
1264
|
-
const orders = await db.order
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
}
|
|
1340
|
+
const orders = await db.order
|
|
1341
|
+
.findMany({
|
|
1342
|
+
select: {
|
|
1343
|
+
id: true,
|
|
1344
|
+
orderNumber: true,
|
|
1345
|
+
// Nested select for belongsTo relation
|
|
1346
|
+
customer: {
|
|
1347
|
+
select: { id: true, username: true, displayName: true },
|
|
1348
|
+
},
|
|
1349
|
+
},
|
|
1350
|
+
})
|
|
1351
|
+
.unwrap();
|
|
1274
1352
|
|
|
1275
1353
|
// TypeScript knows:
|
|
1276
1354
|
// orders.orders.nodes[0].customer is { id: string; username: string | null; displayName: string | null }
|
|
@@ -1280,18 +1358,20 @@ const orders = await db.order.findMany({
|
|
|
1280
1358
|
|
|
1281
1359
|
```typescript
|
|
1282
1360
|
// Order.orderItems is a hasMany relation to OrderItem
|
|
1283
|
-
const orders = await db.order
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
}
|
|
1361
|
+
const orders = await db.order
|
|
1362
|
+
.findMany({
|
|
1363
|
+
select: {
|
|
1364
|
+
id: true,
|
|
1365
|
+
// HasMany with pagination and filtering
|
|
1366
|
+
orderItems: {
|
|
1367
|
+
select: { id: true, quantity: true, price: true },
|
|
1368
|
+
first: 10, // Pagination
|
|
1369
|
+
filter: { quantity: { greaterThan: 0 } }, // Filtering
|
|
1370
|
+
orderBy: ['QUANTITY_DESC'], // Ordering
|
|
1371
|
+
},
|
|
1372
|
+
},
|
|
1373
|
+
})
|
|
1374
|
+
.unwrap();
|
|
1295
1375
|
|
|
1296
1376
|
// orders.orders.nodes[0].orderItems is a connection:
|
|
1297
1377
|
// { nodes: Array<{ id: string; quantity: number | null; price: number | null }>, totalCount: number, pageInfo: PageInfo }
|
|
@@ -1301,49 +1381,53 @@ const orders = await db.order.findMany({
|
|
|
1301
1381
|
|
|
1302
1382
|
```typescript
|
|
1303
1383
|
// Order.productsByOrderItemOrderIdAndProductId is a manyToMany through OrderItem
|
|
1304
|
-
const orders = await db.order
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
}
|
|
1384
|
+
const orders = await db.order
|
|
1385
|
+
.findMany({
|
|
1386
|
+
select: {
|
|
1387
|
+
id: true,
|
|
1388
|
+
productsByOrderItemOrderIdAndProductId: {
|
|
1389
|
+
select: { id: true, name: true, price: true },
|
|
1390
|
+
first: 5,
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
})
|
|
1394
|
+
.unwrap();
|
|
1313
1395
|
```
|
|
1314
1396
|
|
|
1315
1397
|
#### Deeply Nested Relations
|
|
1316
1398
|
|
|
1317
1399
|
```typescript
|
|
1318
1400
|
// Multiple levels of nesting
|
|
1319
|
-
const products = await db.product
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
// BelongsTo: Product -> Category
|
|
1332
|
-
category: {
|
|
1333
|
-
select: { id: true, name: true }
|
|
1334
|
-
},
|
|
1335
|
-
// HasMany: Product -> Review
|
|
1336
|
-
reviews: {
|
|
1337
|
-
select: {
|
|
1338
|
-
id: true,
|
|
1339
|
-
rating: true,
|
|
1340
|
-
comment: true
|
|
1401
|
+
const products = await db.product
|
|
1402
|
+
.findMany({
|
|
1403
|
+
select: {
|
|
1404
|
+
id: true,
|
|
1405
|
+
name: true,
|
|
1406
|
+
// BelongsTo: Product -> User (seller)
|
|
1407
|
+
seller: {
|
|
1408
|
+
select: {
|
|
1409
|
+
id: true,
|
|
1410
|
+
username: true,
|
|
1411
|
+
// Even deeper nesting if needed
|
|
1412
|
+
},
|
|
1341
1413
|
},
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1414
|
+
// BelongsTo: Product -> Category
|
|
1415
|
+
category: {
|
|
1416
|
+
select: { id: true, name: true },
|
|
1417
|
+
},
|
|
1418
|
+
// HasMany: Product -> Review
|
|
1419
|
+
reviews: {
|
|
1420
|
+
select: {
|
|
1421
|
+
id: true,
|
|
1422
|
+
rating: true,
|
|
1423
|
+
comment: true,
|
|
1424
|
+
},
|
|
1425
|
+
first: 5,
|
|
1426
|
+
orderBy: ['CREATED_AT_DESC'],
|
|
1427
|
+
},
|
|
1428
|
+
},
|
|
1429
|
+
})
|
|
1430
|
+
.unwrap();
|
|
1347
1431
|
```
|
|
1348
1432
|
|
|
1349
1433
|
### Filtering & Ordering
|
|
@@ -1462,13 +1546,15 @@ where: {
|
|
|
1462
1546
|
#### Ordering
|
|
1463
1547
|
|
|
1464
1548
|
```typescript
|
|
1465
|
-
const users = await db.user
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1549
|
+
const users = await db.user
|
|
1550
|
+
.findMany({
|
|
1551
|
+
select: { id: true, username: true, createdAt: true },
|
|
1552
|
+
orderBy: [
|
|
1553
|
+
'CREATED_AT_DESC', // Newest first
|
|
1554
|
+
'USERNAME_ASC', // Then alphabetical
|
|
1555
|
+
],
|
|
1556
|
+
})
|
|
1557
|
+
.unwrap();
|
|
1472
1558
|
|
|
1473
1559
|
// Available OrderBy values (generated per entity):
|
|
1474
1560
|
// - PRIMARY_KEY_ASC / PRIMARY_KEY_DESC
|
|
@@ -1482,37 +1568,47 @@ The ORM supports cursor-based and offset pagination:
|
|
|
1482
1568
|
|
|
1483
1569
|
```typescript
|
|
1484
1570
|
// First N records
|
|
1485
|
-
const first10 = await db.user
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1571
|
+
const first10 = await db.user
|
|
1572
|
+
.findMany({
|
|
1573
|
+
select: { id: true },
|
|
1574
|
+
first: 10,
|
|
1575
|
+
})
|
|
1576
|
+
.unwrap();
|
|
1489
1577
|
|
|
1490
1578
|
// Last N records
|
|
1491
|
-
const last10 = await db.user
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1579
|
+
const last10 = await db.user
|
|
1580
|
+
.findMany({
|
|
1581
|
+
select: { id: true },
|
|
1582
|
+
last: 10,
|
|
1583
|
+
})
|
|
1584
|
+
.unwrap();
|
|
1495
1585
|
|
|
1496
1586
|
// Cursor-based pagination (after/before)
|
|
1497
|
-
const page1 = await db.user
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1587
|
+
const page1 = await db.user
|
|
1588
|
+
.findMany({
|
|
1589
|
+
select: { id: true },
|
|
1590
|
+
first: 10,
|
|
1591
|
+
})
|
|
1592
|
+
.unwrap();
|
|
1501
1593
|
|
|
1502
1594
|
const endCursor = page1.users.pageInfo.endCursor;
|
|
1503
1595
|
|
|
1504
|
-
const page2 = await db.user
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1596
|
+
const page2 = await db.user
|
|
1597
|
+
.findMany({
|
|
1598
|
+
select: { id: true },
|
|
1599
|
+
first: 10,
|
|
1600
|
+
after: endCursor, // Get records after this cursor
|
|
1601
|
+
})
|
|
1602
|
+
.unwrap();
|
|
1509
1603
|
|
|
1510
1604
|
// Offset pagination
|
|
1511
|
-
const page3 = await db.user
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1605
|
+
const page3 = await db.user
|
|
1606
|
+
.findMany({
|
|
1607
|
+
select: { id: true },
|
|
1608
|
+
first: 10,
|
|
1609
|
+
offset: 20, // Skip first 20 records
|
|
1610
|
+
})
|
|
1611
|
+
.unwrap();
|
|
1516
1612
|
|
|
1517
1613
|
// PageInfo structure
|
|
1518
1614
|
// {
|
|
@@ -1523,7 +1619,7 @@ const page3 = await db.user.findMany({
|
|
|
1523
1619
|
// }
|
|
1524
1620
|
|
|
1525
1621
|
// Total count is always included
|
|
1526
|
-
console.log(page1.users.totalCount);
|
|
1622
|
+
console.log(page1.users.totalCount); // Total matching records
|
|
1527
1623
|
```
|
|
1528
1624
|
|
|
1529
1625
|
### Error Handling
|
|
@@ -1533,9 +1629,11 @@ The ORM provides multiple ways to handle errors:
|
|
|
1533
1629
|
#### Discriminated Union (Recommended)
|
|
1534
1630
|
|
|
1535
1631
|
```typescript
|
|
1536
|
-
const result = await db.user
|
|
1537
|
-
|
|
1538
|
-
}
|
|
1632
|
+
const result = await db.user
|
|
1633
|
+
.findMany({
|
|
1634
|
+
select: { id: true },
|
|
1635
|
+
})
|
|
1636
|
+
.execute();
|
|
1539
1637
|
|
|
1540
1638
|
if (result.ok) {
|
|
1541
1639
|
// TypeScript knows result.data is non-null
|
|
@@ -1555,10 +1653,12 @@ import { GraphQLRequestError } from './generated/orm';
|
|
|
1555
1653
|
|
|
1556
1654
|
try {
|
|
1557
1655
|
// Throws GraphQLRequestError if query fails
|
|
1558
|
-
const data = await db.user
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1656
|
+
const data = await db.user
|
|
1657
|
+
.findMany({
|
|
1658
|
+
select: { id: true },
|
|
1659
|
+
})
|
|
1660
|
+
.unwrap();
|
|
1661
|
+
|
|
1562
1662
|
console.log(data.users.nodes);
|
|
1563
1663
|
} catch (error) {
|
|
1564
1664
|
if (error instanceof GraphQLRequestError) {
|
|
@@ -1572,15 +1672,17 @@ try {
|
|
|
1572
1672
|
|
|
1573
1673
|
```typescript
|
|
1574
1674
|
// Returns default value if query fails (no throwing)
|
|
1575
|
-
const data = await db.user
|
|
1576
|
-
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
}
|
|
1675
|
+
const data = await db.user
|
|
1676
|
+
.findMany({
|
|
1677
|
+
select: { id: true },
|
|
1678
|
+
})
|
|
1679
|
+
.unwrapOr({
|
|
1680
|
+
users: {
|
|
1681
|
+
nodes: [],
|
|
1682
|
+
totalCount: 0,
|
|
1683
|
+
pageInfo: { hasNextPage: false, hasPreviousPage: false },
|
|
1684
|
+
},
|
|
1685
|
+
});
|
|
1584
1686
|
|
|
1585
1687
|
// Always returns data (either real or default)
|
|
1586
1688
|
console.log(data.users.nodes);
|
|
@@ -1590,21 +1692,23 @@ console.log(data.users.nodes);
|
|
|
1590
1692
|
|
|
1591
1693
|
```typescript
|
|
1592
1694
|
// Call a function to handle errors and return fallback
|
|
1593
|
-
const data = await db.user
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
}
|
|
1695
|
+
const data = await db.user
|
|
1696
|
+
.findMany({
|
|
1697
|
+
select: { id: true },
|
|
1698
|
+
})
|
|
1699
|
+
.unwrapOrElse((errors) => {
|
|
1700
|
+
// Log errors, send to monitoring, etc.
|
|
1701
|
+
console.error('Query failed:', errors.map((e) => e.message).join(', '));
|
|
1702
|
+
|
|
1703
|
+
// Return fallback data
|
|
1704
|
+
return {
|
|
1705
|
+
users: {
|
|
1706
|
+
nodes: [],
|
|
1707
|
+
totalCount: 0,
|
|
1708
|
+
pageInfo: { hasNextPage: false, hasPreviousPage: false },
|
|
1709
|
+
},
|
|
1710
|
+
};
|
|
1711
|
+
});
|
|
1608
1712
|
```
|
|
1609
1713
|
|
|
1610
1714
|
#### Error Types
|
|
@@ -1619,7 +1723,7 @@ interface GraphQLError {
|
|
|
1619
1723
|
|
|
1620
1724
|
class GraphQLRequestError extends Error {
|
|
1621
1725
|
readonly errors: GraphQLError[];
|
|
1622
|
-
readonly data: unknown;
|
|
1726
|
+
readonly data: unknown; // Partial data if available
|
|
1623
1727
|
}
|
|
1624
1728
|
|
|
1625
1729
|
type QueryResult<T> =
|
|
@@ -1635,57 +1739,73 @@ Custom queries and mutations (like `login`, `currentUser`, etc.) are available o
|
|
|
1635
1739
|
|
|
1636
1740
|
```typescript
|
|
1637
1741
|
// Query with select
|
|
1638
|
-
const currentUser = await db.query
|
|
1639
|
-
|
|
1640
|
-
}
|
|
1742
|
+
const currentUser = await db.query
|
|
1743
|
+
.currentUser({
|
|
1744
|
+
select: { id: true, username: true, email: true },
|
|
1745
|
+
})
|
|
1746
|
+
.unwrap();
|
|
1641
1747
|
|
|
1642
1748
|
// Query without select (returns full type)
|
|
1643
1749
|
const me = await db.query.currentUser({}).unwrap();
|
|
1644
1750
|
|
|
1645
1751
|
// Query with arguments
|
|
1646
|
-
const node = await db.query
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
}
|
|
1752
|
+
const node = await db.query
|
|
1753
|
+
.nodeById(
|
|
1754
|
+
{
|
|
1755
|
+
id: 'some-node-id',
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
select: { id: true },
|
|
1759
|
+
}
|
|
1760
|
+
)
|
|
1761
|
+
.unwrap();
|
|
1651
1762
|
```
|
|
1652
1763
|
|
|
1653
1764
|
#### Custom Mutations
|
|
1654
1765
|
|
|
1655
1766
|
```typescript
|
|
1656
1767
|
// Login mutation with typed select
|
|
1657
|
-
const login = await db.mutation
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1768
|
+
const login = await db.mutation
|
|
1769
|
+
.login(
|
|
1770
|
+
{
|
|
1771
|
+
input: {
|
|
1772
|
+
email: 'user@example.com',
|
|
1773
|
+
password: 'secret123',
|
|
1774
|
+
},
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1666
1777
|
select: {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1778
|
+
clientMutationId: true,
|
|
1779
|
+
apiToken: {
|
|
1780
|
+
select: {
|
|
1781
|
+
accessToken: true,
|
|
1782
|
+
accessTokenExpiresAt: true,
|
|
1783
|
+
},
|
|
1784
|
+
},
|
|
1785
|
+
},
|
|
1670
1786
|
}
|
|
1671
|
-
|
|
1672
|
-
|
|
1787
|
+
)
|
|
1788
|
+
.unwrap();
|
|
1673
1789
|
|
|
1674
1790
|
console.log(login.login.apiToken?.accessToken);
|
|
1675
1791
|
|
|
1676
1792
|
// Register mutation
|
|
1677
|
-
const register = await db.mutation
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
}
|
|
1793
|
+
const register = await db.mutation
|
|
1794
|
+
.register({
|
|
1795
|
+
input: {
|
|
1796
|
+
email: 'new@example.com',
|
|
1797
|
+
password: 'secret123',
|
|
1798
|
+
username: 'newuser',
|
|
1799
|
+
},
|
|
1800
|
+
})
|
|
1801
|
+
.unwrap();
|
|
1684
1802
|
|
|
1685
1803
|
// Logout mutation
|
|
1686
|
-
await db.mutation
|
|
1687
|
-
|
|
1688
|
-
}
|
|
1804
|
+
await db.mutation
|
|
1805
|
+
.logout({
|
|
1806
|
+
input: { clientMutationId: 'optional-id' },
|
|
1807
|
+
})
|
|
1808
|
+
.execute();
|
|
1689
1809
|
```
|
|
1690
1810
|
|
|
1691
1811
|
### Query Builder API
|
|
@@ -1696,7 +1816,7 @@ Every operation returns a `QueryBuilder` that can be inspected before execution:
|
|
|
1696
1816
|
const query = db.user.findMany({
|
|
1697
1817
|
select: { id: true, username: true },
|
|
1698
1818
|
where: { isActive: { equalTo: true } },
|
|
1699
|
-
first: 10
|
|
1819
|
+
first: 10,
|
|
1700
1820
|
});
|
|
1701
1821
|
|
|
1702
1822
|
// Inspect the generated GraphQL
|
|
@@ -1838,25 +1958,29 @@ export type OrderSelect = {
|
|
|
1838
1958
|
id?: boolean;
|
|
1839
1959
|
orderNumber?: boolean;
|
|
1840
1960
|
status?: boolean;
|
|
1841
|
-
|
|
1961
|
+
|
|
1842
1962
|
// BelongsTo relation
|
|
1843
1963
|
customer?: boolean | { select?: UserSelect };
|
|
1844
|
-
|
|
1964
|
+
|
|
1845
1965
|
// HasMany relation
|
|
1846
|
-
orderItems?:
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1966
|
+
orderItems?:
|
|
1967
|
+
| boolean
|
|
1968
|
+
| {
|
|
1969
|
+
select?: OrderItemSelect;
|
|
1970
|
+
first?: number;
|
|
1971
|
+
filter?: OrderItemFilter;
|
|
1972
|
+
orderBy?: OrderItemsOrderBy[];
|
|
1973
|
+
};
|
|
1974
|
+
|
|
1853
1975
|
// ManyToMany relation
|
|
1854
|
-
productsByOrderItemOrderIdAndProductId?:
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1976
|
+
productsByOrderItemOrderIdAndProductId?:
|
|
1977
|
+
| boolean
|
|
1978
|
+
| {
|
|
1979
|
+
select?: ProductSelect;
|
|
1980
|
+
first?: number;
|
|
1981
|
+
filter?: ProductFilter;
|
|
1982
|
+
orderBy?: ProductsOrderBy[];
|
|
1983
|
+
};
|
|
1860
1984
|
};
|
|
1861
1985
|
```
|
|
1862
1986
|
|