@vantreeseba/drizzle-graphql 4.0.0 → 4.1.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 +46 -0
- package/dist/README.md +46 -0
- package/dist/index.cjs +56 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.js +56 -15
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -397,6 +397,52 @@ The build-wide `conflictDoNothing` option is deprecated in favour of this: it ap
|
|
|
397
397
|
`create*` mutation with no way for a request to opt out. `onConflict: { action: NOTHING }` is
|
|
398
398
|
the per-request replacement.
|
|
399
399
|
|
|
400
|
+
## Query cost
|
|
401
|
+
|
|
402
|
+
Generated fields carry a `complexity` hint in their GraphQL extensions, ready for
|
|
403
|
+
[`graphql-query-complexity`](https://github.com/slicknode/graphql-query-complexity)'s
|
|
404
|
+
`fieldExtensionsEstimator`. The generator knows which fields are paginated and which are
|
|
405
|
+
aggregates, so the cost tracks the rows a query can actually pull rather than the number of
|
|
406
|
+
fields it mentions:
|
|
407
|
+
|
|
408
|
+
| Field | Cost |
|
|
409
|
+
| --- | --- |
|
|
410
|
+
| List query, to-many relation | `(limit ?? defaultListSize) * childComplexity` |
|
|
411
|
+
| Aggregate query, `<relation>Aggregate` | `aggregateCost + childComplexity` |
|
|
412
|
+
| Everything else | no hint — your estimator's default applies |
|
|
413
|
+
|
|
414
|
+
So `users(limit: 20) { id posts(limit: 5) { id } }` costs `20 * (1 + 5 * 1)` = 120, while the
|
|
415
|
+
same query without limits costs `10 * (1 + 10 * 1)` = 110.
|
|
416
|
+
|
|
417
|
+
The hints do nothing until you install a complexity rule, so they are generated by default:
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
import { createComplexityRule, fieldExtensionsEstimator, simpleEstimator } from 'graphql-query-complexity';
|
|
421
|
+
|
|
422
|
+
const rule = createComplexityRule({
|
|
423
|
+
maximumComplexity: 1000,
|
|
424
|
+
estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Tune the two assumptions, or turn the hints off entirely:
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
buildSchema(db, {
|
|
432
|
+
complexity: { defaultListSize: 25, aggregateCost: 50 },
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
buildSchema(db, { complexity: false });
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Depth
|
|
439
|
+
|
|
440
|
+
Cost is not a depth bound. A cyclic relation graph (`user -> posts -> author -> posts -> …`)
|
|
441
|
+
lets a client nest as deep as it likes, and a deep query over cheap fields can stay under any
|
|
442
|
+
complexity ceiling. Put a depth limit in front of a publicly exposed schema as well — e.g.
|
|
443
|
+
[`graphql-depth-limit`](https://github.com/stems/graphql-depth-limit) — or set
|
|
444
|
+
`relationsDepthLimit: 0` to generate no relation fields at all.
|
|
445
|
+
|
|
400
446
|
## Error handling
|
|
401
447
|
|
|
402
448
|
Database drivers put a lot into an error message. Drizzle rethrows them with the full SQL
|
package/dist/README.md
CHANGED
|
@@ -397,6 +397,52 @@ The build-wide `conflictDoNothing` option is deprecated in favour of this: it ap
|
|
|
397
397
|
`create*` mutation with no way for a request to opt out. `onConflict: { action: NOTHING }` is
|
|
398
398
|
the per-request replacement.
|
|
399
399
|
|
|
400
|
+
## Query cost
|
|
401
|
+
|
|
402
|
+
Generated fields carry a `complexity` hint in their GraphQL extensions, ready for
|
|
403
|
+
[`graphql-query-complexity`](https://github.com/slicknode/graphql-query-complexity)'s
|
|
404
|
+
`fieldExtensionsEstimator`. The generator knows which fields are paginated and which are
|
|
405
|
+
aggregates, so the cost tracks the rows a query can actually pull rather than the number of
|
|
406
|
+
fields it mentions:
|
|
407
|
+
|
|
408
|
+
| Field | Cost |
|
|
409
|
+
| --- | --- |
|
|
410
|
+
| List query, to-many relation | `(limit ?? defaultListSize) * childComplexity` |
|
|
411
|
+
| Aggregate query, `<relation>Aggregate` | `aggregateCost + childComplexity` |
|
|
412
|
+
| Everything else | no hint — your estimator's default applies |
|
|
413
|
+
|
|
414
|
+
So `users(limit: 20) { id posts(limit: 5) { id } }` costs `20 * (1 + 5 * 1)` = 120, while the
|
|
415
|
+
same query without limits costs `10 * (1 + 10 * 1)` = 110.
|
|
416
|
+
|
|
417
|
+
The hints do nothing until you install a complexity rule, so they are generated by default:
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
import { createComplexityRule, fieldExtensionsEstimator, simpleEstimator } from 'graphql-query-complexity';
|
|
421
|
+
|
|
422
|
+
const rule = createComplexityRule({
|
|
423
|
+
maximumComplexity: 1000,
|
|
424
|
+
estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Tune the two assumptions, or turn the hints off entirely:
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
buildSchema(db, {
|
|
432
|
+
complexity: { defaultListSize: 25, aggregateCost: 50 },
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
buildSchema(db, { complexity: false });
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Depth
|
|
439
|
+
|
|
440
|
+
Cost is not a depth bound. A cyclic relation graph (`user -> posts -> author -> posts -> …`)
|
|
441
|
+
lets a client nest as deep as it likes, and a deep query over cheap fields can stay under any
|
|
442
|
+
complexity ceiling. Put a depth limit in front of a publicly exposed schema as well — e.g.
|
|
443
|
+
[`graphql-depth-limit`](https://github.com/stems/graphql-depth-limit) — or set
|
|
444
|
+
`relationsDepthLimit: 0` to generate no relation fields at all.
|
|
445
|
+
|
|
400
446
|
## Error handling
|
|
401
447
|
|
|
402
448
|
Database drivers put a lot into an error message. Drizzle rethrows them with the full SQL
|
package/dist/index.cjs
CHANGED
|
@@ -650,6 +650,12 @@ var createRelationResolverFactory = (db, tables, filterCtx) => ({ tableName, rel
|
|
|
650
650
|
return loader.load(localValue);
|
|
651
651
|
};
|
|
652
652
|
};
|
|
653
|
+
var listFieldComplexity = (options) => ({ args, childComplexity }) => {
|
|
654
|
+
const limit = args["limit"];
|
|
655
|
+
const rows = typeof limit === "number" && limit > 0 ? limit : options.defaultListSize;
|
|
656
|
+
return rows * Math.max(childComplexity, 1);
|
|
657
|
+
};
|
|
658
|
+
var aggregateFieldComplexity = (options) => ({ childComplexity }) => options.aggregateCost + childComplexity;
|
|
653
659
|
var AGGREGATE_FIELD_SUFFIX = "Aggregate";
|
|
654
660
|
var relationAggregateJoinColumns = (tree, table, selectionCtx) => {
|
|
655
661
|
const relations = selectionCtx?.relationMap[selectionCtx.tableName];
|
|
@@ -1071,7 +1077,8 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
|
|
|
1071
1077
|
offset: { type: import_graphql4.GraphQLInt },
|
|
1072
1078
|
limit: { type: import_graphql4.GraphQLInt }
|
|
1073
1079
|
},
|
|
1074
|
-
resolve
|
|
1080
|
+
resolve,
|
|
1081
|
+
...cacheCtx.complexity ? { extensions: { complexity: listFieldComplexity(cacheCtx.complexity) } } : {}
|
|
1075
1082
|
}
|
|
1076
1083
|
]);
|
|
1077
1084
|
const aggregateFieldName = `${relationName}Aggregate`;
|
|
@@ -1089,7 +1096,8 @@ var generateSelectFields = (tables, tableName, relationMap, fromTableName, fromR
|
|
|
1089
1096
|
args: {
|
|
1090
1097
|
where: { type: relSelectData.filters }
|
|
1091
1098
|
},
|
|
1092
|
-
resolve: relationAggregate.resolve
|
|
1099
|
+
resolve: relationAggregate.resolve,
|
|
1100
|
+
...cacheCtx.complexity ? { extensions: { complexity: aggregateFieldComplexity(cacheCtx.complexity) } } : {}
|
|
1093
1101
|
}
|
|
1094
1102
|
]);
|
|
1095
1103
|
}
|
|
@@ -2366,7 +2374,7 @@ var generateDelete = (db, tableName, table, filterArgs, fieldName, filterCtx) =>
|
|
|
2366
2374
|
};
|
|
2367
2375
|
var mysqlPrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table, import_mysql_core2.getTableConfig);
|
|
2368
2376
|
var generateSchemaData = (db, schema, relations, options) => {
|
|
2369
|
-
const { relationsDepthLimit, prefixes, suffixes, typeNameMapper, shouldEagerLoad, features } = options;
|
|
2377
|
+
const { relationsDepthLimit, prefixes, suffixes, typeNameMapper, shouldEagerLoad, features, complexity } = options;
|
|
2370
2378
|
const rawSchema = schema;
|
|
2371
2379
|
const schemaEntries = Object.entries(rawSchema);
|
|
2372
2380
|
const tableEntries = schemaEntries.filter(([_key, value]) => (0, import_drizzle_orm5.is)(value, import_mysql_core2.MySqlTable));
|
|
@@ -2390,7 +2398,8 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
2390
2398
|
orderTypeCache: /* @__PURE__ */ new WeakMap(),
|
|
2391
2399
|
filterTypeCache: /* @__PURE__ */ new WeakMap(),
|
|
2392
2400
|
listRelationFilterCache: /* @__PURE__ */ new Map(),
|
|
2393
|
-
aggregateTypeCache: /* @__PURE__ */ new Map()
|
|
2401
|
+
aggregateTypeCache: /* @__PURE__ */ new Map(),
|
|
2402
|
+
complexity
|
|
2394
2403
|
};
|
|
2395
2404
|
const relationAggregateFactory = features.relationAggregates ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
|
|
2396
2405
|
const queries = {};
|
|
@@ -2500,7 +2509,8 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
2500
2509
|
queries[selectArrGenerated.name] = {
|
|
2501
2510
|
type: selectArrOutput,
|
|
2502
2511
|
args: selectArrGenerated.args,
|
|
2503
|
-
resolve: selectArrGenerated.resolver
|
|
2512
|
+
resolve: selectArrGenerated.resolver,
|
|
2513
|
+
...complexity ? { extensions: { complexity: listFieldComplexity(complexity) } } : {}
|
|
2504
2514
|
};
|
|
2505
2515
|
queries[selectSingleGenerated.name] = {
|
|
2506
2516
|
type: selectSingleOutput,
|
|
@@ -2511,7 +2521,8 @@ var generateSchemaData = (db, schema, relations, options) => {
|
|
|
2511
2521
|
queries[aggregateGenerated.name] = {
|
|
2512
2522
|
type: new import_graphql6.GraphQLNonNull(aggregateType),
|
|
2513
2523
|
args: aggregateGenerated.args,
|
|
2514
|
-
resolve: aggregateGenerated.resolver
|
|
2524
|
+
resolve: aggregateGenerated.resolver,
|
|
2525
|
+
...complexity ? { extensions: { complexity: aggregateFieldComplexity(complexity) } } : {}
|
|
2515
2526
|
};
|
|
2516
2527
|
}
|
|
2517
2528
|
for (const generated of [
|
|
@@ -2938,7 +2949,16 @@ var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, fi
|
|
|
2938
2949
|
};
|
|
2939
2950
|
};
|
|
2940
2951
|
function generateSchemaData2(db, schema, relations, options) {
|
|
2941
|
-
const {
|
|
2952
|
+
const {
|
|
2953
|
+
relationsDepthLimit,
|
|
2954
|
+
prefixes,
|
|
2955
|
+
suffixes,
|
|
2956
|
+
conflictDoNothing,
|
|
2957
|
+
typeNameMapper,
|
|
2958
|
+
shouldEagerLoad,
|
|
2959
|
+
features,
|
|
2960
|
+
complexity
|
|
2961
|
+
} = options;
|
|
2942
2962
|
const schemaEntries = Object.entries(schema);
|
|
2943
2963
|
const tableEntries = schemaEntries.filter(([_key, value]) => (0, import_drizzle_orm6.is)(value, import_pg_core2.PgTable));
|
|
2944
2964
|
const tables = Object.fromEntries(tableEntries);
|
|
@@ -2961,7 +2981,8 @@ function generateSchemaData2(db, schema, relations, options) {
|
|
|
2961
2981
|
orderTypeCache: /* @__PURE__ */ new WeakMap(),
|
|
2962
2982
|
filterTypeCache: /* @__PURE__ */ new WeakMap(),
|
|
2963
2983
|
listRelationFilterCache: /* @__PURE__ */ new Map(),
|
|
2964
|
-
aggregateTypeCache: /* @__PURE__ */ new Map()
|
|
2984
|
+
aggregateTypeCache: /* @__PURE__ */ new Map(),
|
|
2985
|
+
complexity
|
|
2965
2986
|
};
|
|
2966
2987
|
const relationAggregateFactory = features.relationAggregates ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
|
|
2967
2988
|
const queries = {};
|
|
@@ -3124,7 +3145,8 @@ function generateSchemaData2(db, schema, relations, options) {
|
|
|
3124
3145
|
queries[selectArrGenerated.name] = {
|
|
3125
3146
|
type: selectArrOutput,
|
|
3126
3147
|
args: selectArrGenerated.args,
|
|
3127
|
-
resolve: selectArrGenerated.resolver
|
|
3148
|
+
resolve: selectArrGenerated.resolver,
|
|
3149
|
+
...complexity ? { extensions: { complexity: listFieldComplexity(complexity) } } : {}
|
|
3128
3150
|
};
|
|
3129
3151
|
queries[selectSingleGenerated.name] = {
|
|
3130
3152
|
type: selectSingleOutput,
|
|
@@ -3135,7 +3157,8 @@ function generateSchemaData2(db, schema, relations, options) {
|
|
|
3135
3157
|
queries[aggregateGenerated.name] = {
|
|
3136
3158
|
type: new import_graphql7.GraphQLNonNull(aggregateType),
|
|
3137
3159
|
args: aggregateGenerated.args,
|
|
3138
|
-
resolve: aggregateGenerated.resolver
|
|
3160
|
+
resolve: aggregateGenerated.resolver,
|
|
3161
|
+
...complexity ? { extensions: { complexity: aggregateFieldComplexity(complexity) } } : {}
|
|
3139
3162
|
};
|
|
3140
3163
|
}
|
|
3141
3164
|
if (insertArrGenerated) {
|
|
@@ -3527,7 +3550,16 @@ var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, fi
|
|
|
3527
3550
|
};
|
|
3528
3551
|
};
|
|
3529
3552
|
var generateSchemaData3 = (db, schema, relations, options) => {
|
|
3530
|
-
const {
|
|
3553
|
+
const {
|
|
3554
|
+
relationsDepthLimit,
|
|
3555
|
+
prefixes,
|
|
3556
|
+
suffixes,
|
|
3557
|
+
conflictDoNothing,
|
|
3558
|
+
typeNameMapper,
|
|
3559
|
+
shouldEagerLoad,
|
|
3560
|
+
features,
|
|
3561
|
+
complexity
|
|
3562
|
+
} = options;
|
|
3531
3563
|
const rawSchema = schema;
|
|
3532
3564
|
const schemaEntries = Object.entries(rawSchema);
|
|
3533
3565
|
const tableEntries = schemaEntries.filter(([_key, value]) => (0, import_drizzle_orm7.is)(value, import_sqlite_core2.SQLiteTable));
|
|
@@ -3551,7 +3583,8 @@ var generateSchemaData3 = (db, schema, relations, options) => {
|
|
|
3551
3583
|
orderTypeCache: /* @__PURE__ */ new WeakMap(),
|
|
3552
3584
|
filterTypeCache: /* @__PURE__ */ new WeakMap(),
|
|
3553
3585
|
listRelationFilterCache: /* @__PURE__ */ new Map(),
|
|
3554
|
-
aggregateTypeCache: /* @__PURE__ */ new Map()
|
|
3586
|
+
aggregateTypeCache: /* @__PURE__ */ new Map(),
|
|
3587
|
+
complexity
|
|
3555
3588
|
};
|
|
3556
3589
|
const relationAggregateFactory = features.relationAggregates ? createRelationAggregateFactory(db, tables, cacheCtx, typeNameMapper, filterCtx) : void 0;
|
|
3557
3590
|
const queries = {};
|
|
@@ -3714,7 +3747,8 @@ var generateSchemaData3 = (db, schema, relations, options) => {
|
|
|
3714
3747
|
queries[selectArrGenerated.name] = {
|
|
3715
3748
|
type: selectArrOutput,
|
|
3716
3749
|
args: selectArrGenerated.args,
|
|
3717
|
-
resolve: selectArrGenerated.resolver
|
|
3750
|
+
resolve: selectArrGenerated.resolver,
|
|
3751
|
+
...complexity ? { extensions: { complexity: listFieldComplexity(complexity) } } : {}
|
|
3718
3752
|
};
|
|
3719
3753
|
queries[selectSingleGenerated.name] = {
|
|
3720
3754
|
type: selectSingleOutput,
|
|
@@ -3725,7 +3759,8 @@ var generateSchemaData3 = (db, schema, relations, options) => {
|
|
|
3725
3759
|
queries[aggregateGenerated.name] = {
|
|
3726
3760
|
type: new import_graphql8.GraphQLNonNull(aggregateType),
|
|
3727
3761
|
args: aggregateGenerated.args,
|
|
3728
|
-
resolve: aggregateGenerated.resolver
|
|
3762
|
+
resolve: aggregateGenerated.resolver,
|
|
3763
|
+
...complexity ? { extensions: { complexity: aggregateFieldComplexity(complexity) } } : {}
|
|
3729
3764
|
};
|
|
3730
3765
|
}
|
|
3731
3766
|
if (insertArrGenerated) {
|
|
@@ -3835,6 +3870,11 @@ var buildSchema = (db, config) => {
|
|
|
3835
3870
|
delete: config?.features?.delete ?? true,
|
|
3836
3871
|
upsert: config?.features?.upsert ?? false
|
|
3837
3872
|
};
|
|
3873
|
+
const complexityConfig = config?.complexity ?? true;
|
|
3874
|
+
const complexity = complexityConfig === false ? void 0 : {
|
|
3875
|
+
defaultListSize: (complexityConfig === true ? void 0 : complexityConfig.defaultListSize) ?? 10,
|
|
3876
|
+
aggregateCost: (complexityConfig === true ? void 0 : complexityConfig.aggregateCost) ?? 10
|
|
3877
|
+
};
|
|
3838
3878
|
const eagerOpt = config?.eagerLoadRelations;
|
|
3839
3879
|
const shouldEagerLoad = eagerOpt === void 0 || eagerOpt === true ? () => true : eagerOpt === false ? () => false : eagerOpt;
|
|
3840
3880
|
if (!typeNameMapper && suffixes.list === suffixes.single) {
|
|
@@ -3861,7 +3901,8 @@ var buildSchema = (db, config) => {
|
|
|
3861
3901
|
conflictDoNothing: config?.conflictDoNothing ?? false,
|
|
3862
3902
|
typeNameMapper,
|
|
3863
3903
|
shouldEagerLoad,
|
|
3864
|
-
features
|
|
3904
|
+
features,
|
|
3905
|
+
complexity
|
|
3865
3906
|
};
|
|
3866
3907
|
let generatorOutput;
|
|
3867
3908
|
if ((0, import_drizzle_orm8.is)(db, import_mysql_core3.MySqlDatabase)) {
|