@vantreeseba/drizzle-graphql 7.1.0 → 7.2.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 CHANGED
@@ -124,6 +124,60 @@ const { schema } = buildSchema(db, {
124
124
  `mutations: false`
125
125
  - List and single queries are always generated, so `Query` is never empty
126
126
 
127
+ ## Excluding tables and columns
128
+
129
+ `features` decides which *kinds* of operation exist. `exclude` decides which tables and
130
+ columns exist at all — for the table holding session tokens, or the column holding a
131
+ password hash:
132
+
133
+ ```Typescript
134
+ const { schema } = buildSchema(db, {
135
+ exclude: {
136
+ tables: ['magicLinks', 'sessions'],
137
+ columns: { apiKeys: ['keyHash'] },
138
+ },
139
+ })
140
+ ```
141
+
142
+ Both lists are keyed by the **Drizzle schema key**, not the database name and not the
143
+ generated GraphQL name.
144
+
145
+ An excluded **table** generates nothing: no object type, no queries, no mutations, no
146
+ aggregates, and no relation field on any other table that points at it. `Users.sessions`
147
+ is gone, and so is `users(where: { sessions: { … } })` — the table is unreachable, not
148
+ merely unnamed.
149
+
150
+ An excluded **column** disappears from every surface at once:
151
+
152
+ | Surface | Effect |
153
+ | --- | --- |
154
+ | `<Type>` object type | field removed |
155
+ | `Create<Type>Input` / `Update<Type>Input` | field removed (nested-write payloads too) |
156
+ | `<Type>Filters` | field removed |
157
+ | `<Type>OrderBy` | field removed |
158
+ | `<Type>MinAggregate` / `<Type>MaxAggregate` / … | field removed |
159
+ | `<Type>DistinctColumn` / `<Type>GroupByColumn` | value removed |
160
+ | `<Type>ConflictColumn` (upsert targets) | value removed |
161
+
162
+ Two details worth stating plainly:
163
+
164
+ - **The exclusion is all-or-nothing on purpose.** A column you can filter on but not
165
+ select is an oracle: `where: { keyHash: { eq: '…' } }` confirms a value without ever
166
+ returning it. Ordering and grouping leak the same way, more slowly. There is no
167
+ read-only or write-only variant of `exclude` for this reason.
168
+ - **Nothing changes below the schema.** An excluded column is still written by its
169
+ default, still read by the primary-key and cursor machinery, and still usable by your
170
+ own resolvers. Only the generated schema stops mentioning it.
171
+
172
+ Names that match nothing throw at build time, so renaming a column fails the build instead
173
+ of quietly un-hiding it. Excluding a `NOT NULL` column with no default is allowed — the
174
+ table becomes readable but not insertable — and warns on the console, since that is more
175
+ often a mistake than a plan.
176
+
177
+ > `exclude` is a static decision: the column is absent for every caller. Deciding per
178
+ > request who may see what is a job for [`graphql-shield`](https://the-guild.dev/graphql/shield)
179
+ > or an equivalent layer above the schema, which has the request context this library
180
+ > does not.
127
181
  ## Naming
128
182
 
129
183
  Type and field names are derived from the Drizzle schema keys. Three options change them.
package/dist/README.md CHANGED
@@ -124,6 +124,60 @@ const { schema } = buildSchema(db, {
124
124
  `mutations: false`
125
125
  - List and single queries are always generated, so `Query` is never empty
126
126
 
127
+ ## Excluding tables and columns
128
+
129
+ `features` decides which *kinds* of operation exist. `exclude` decides which tables and
130
+ columns exist at all — for the table holding session tokens, or the column holding a
131
+ password hash:
132
+
133
+ ```Typescript
134
+ const { schema } = buildSchema(db, {
135
+ exclude: {
136
+ tables: ['magicLinks', 'sessions'],
137
+ columns: { apiKeys: ['keyHash'] },
138
+ },
139
+ })
140
+ ```
141
+
142
+ Both lists are keyed by the **Drizzle schema key**, not the database name and not the
143
+ generated GraphQL name.
144
+
145
+ An excluded **table** generates nothing: no object type, no queries, no mutations, no
146
+ aggregates, and no relation field on any other table that points at it. `Users.sessions`
147
+ is gone, and so is `users(where: { sessions: { … } })` — the table is unreachable, not
148
+ merely unnamed.
149
+
150
+ An excluded **column** disappears from every surface at once:
151
+
152
+ | Surface | Effect |
153
+ | --- | --- |
154
+ | `<Type>` object type | field removed |
155
+ | `Create<Type>Input` / `Update<Type>Input` | field removed (nested-write payloads too) |
156
+ | `<Type>Filters` | field removed |
157
+ | `<Type>OrderBy` | field removed |
158
+ | `<Type>MinAggregate` / `<Type>MaxAggregate` / … | field removed |
159
+ | `<Type>DistinctColumn` / `<Type>GroupByColumn` | value removed |
160
+ | `<Type>ConflictColumn` (upsert targets) | value removed |
161
+
162
+ Two details worth stating plainly:
163
+
164
+ - **The exclusion is all-or-nothing on purpose.** A column you can filter on but not
165
+ select is an oracle: `where: { keyHash: { eq: '…' } }` confirms a value without ever
166
+ returning it. Ordering and grouping leak the same way, more slowly. There is no
167
+ read-only or write-only variant of `exclude` for this reason.
168
+ - **Nothing changes below the schema.** An excluded column is still written by its
169
+ default, still read by the primary-key and cursor machinery, and still usable by your
170
+ own resolvers. Only the generated schema stops mentioning it.
171
+
172
+ Names that match nothing throw at build time, so renaming a column fails the build instead
173
+ of quietly un-hiding it. Excluding a `NOT NULL` column with no default is allowed — the
174
+ table becomes readable but not insertable — and warns on the console, since that is more
175
+ often a mistake than a plan.
176
+
177
+ > `exclude` is a static decision: the column is absent for every caller. Deciding per
178
+ > request who may see what is a job for [`graphql-shield`](https://the-guild.dev/graphql/shield)
179
+ > or an equivalent layer above the schema, which has the request context this library
180
+ > does not.
127
181
  ## Naming
128
182
 
129
183
  Type and field names are derived from the Drizzle schema keys. Three options change them.
package/dist/index.cjs CHANGED
@@ -1182,6 +1182,30 @@ var resolveLimitPolicy = (global, table) => {
1182
1182
  }
1183
1183
  return { defaultLimit, maxLimit, clampToMax: table?.clampToMax ?? global?.clampToMax ?? false };
1184
1184
  };
1185
+ var excludedColumnRegistry = /* @__PURE__ */ new WeakMap();
1186
+ var registerColumnExclusions = (tables, exclude) => {
1187
+ for (const [tableName, table] of Object.entries(tables)) {
1188
+ const hidden = exclude?.columns?.[tableName];
1189
+ if (hidden?.length) {
1190
+ excludedColumnRegistry.set(table, new Set(hidden));
1191
+ } else {
1192
+ excludedColumnRegistry.delete(table);
1193
+ }
1194
+ }
1195
+ };
1196
+ var hasExcludedColumns = (table) => excludedColumnRegistry.has(table);
1197
+ var excludedColumnsKey = (table) => {
1198
+ const hidden = excludedColumnRegistry.get(table);
1199
+ return hidden ? [...hidden].sort().join(",") : "";
1200
+ };
1201
+ var visibleColumns = (table) => {
1202
+ const columns = (0, import_drizzle_orm4.getColumns)(table);
1203
+ const hidden = excludedColumnRegistry.get(table);
1204
+ if (!hidden) {
1205
+ return columns;
1206
+ }
1207
+ return Object.fromEntries(Object.entries(columns).filter(([columnName]) => !hidden.has(columnName)));
1208
+ };
1185
1209
  var listFieldComplexity = (options, policy) => ({ args, childComplexity }) => {
1186
1210
  const limit = args["limit"];
1187
1211
  const requested = typeof limit === "number" && limit > 0 ? limit : void 0;
@@ -1491,17 +1515,20 @@ var generateColumnFilterValues = (column, tableName, columnName, cacheCtx) => {
1491
1515
  };
1492
1516
  var orderMap = /* @__PURE__ */ new WeakMap();
1493
1517
  var generateTableOrderCached = (table) => {
1494
- if (orderMap.has(table)) {
1518
+ const cacheable = !hasExcludedColumns(table);
1519
+ if (cacheable && orderMap.has(table)) {
1495
1520
  return orderMap.get(table);
1496
1521
  }
1497
1522
  let remapped = {};
1498
1523
  try {
1499
- const columns = (0, import_drizzle_orm4.getColumns)(table);
1524
+ const columns = visibleColumns(table);
1500
1525
  const columnEntries = Object.entries(columns);
1501
1526
  remapped = Object.fromEntries(
1502
1527
  columnEntries.map(([columnName, _columnDescription]) => [columnName, { type: innerOrder }])
1503
1528
  );
1504
- orderMap.set(table, remapped);
1529
+ if (cacheable) {
1530
+ orderMap.set(table, remapped);
1531
+ }
1505
1532
  } catch (_err) {
1506
1533
  }
1507
1534
  return remapped;
@@ -1510,7 +1537,7 @@ var generateTableFilterValuesCached = (table, tableName, cacheCtx) => {
1510
1537
  if (cacheCtx.filterFieldCache.has(table)) {
1511
1538
  return cacheCtx.filterFieldCache.get(table);
1512
1539
  }
1513
- const columns = (0, import_drizzle_orm4.getColumns)(table);
1540
+ const columns = visibleColumns(table);
1514
1541
  const columnEntries = Object.entries(columns);
1515
1542
  const remapped = Object.fromEntries(
1516
1543
  columnEntries.map(([columnName, column]) => [
@@ -1531,7 +1558,7 @@ var generateTableSelectTypeFieldsCached = (table, tableName, cacheCtx) => {
1531
1558
  if (cacheCtx.selectFieldCache.has(table)) {
1532
1559
  return cacheCtx.selectFieldCache.get(table);
1533
1560
  }
1534
- const columns = (0, import_drizzle_orm4.getColumns)(table);
1561
+ const columns = visibleColumns(table);
1535
1562
  const columnEntries = Object.entries(columns);
1536
1563
  const remapped = Object.fromEntries(
1537
1564
  columnEntries.map(([columnName, column]) => [
@@ -1889,7 +1916,7 @@ var generateTableTypes = (tableName, tables, relationMap, withReturning, relatio
1889
1916
  relationAggregateFactory
1890
1917
  );
1891
1918
  const table = tables[tableName];
1892
- const columns = (0, import_drizzle_orm4.getColumns)(table);
1919
+ const columns = visibleColumns(table);
1893
1920
  const columnEntries = Object.entries(columns);
1894
1921
  const relaxedColumns = nestedWrites?.relaxedColumns(tableName);
1895
1922
  const insertFields = Object.fromEntries(
@@ -2564,7 +2591,7 @@ var attachRowCursors = (rows, entries) => {
2564
2591
  };
2565
2592
  var rowCursorResolver = (source) => source?.[ROW_CURSOR_PROP] ?? null;
2566
2593
  var getUniqueColumnSets = (table, getTableConfig4) => {
2567
- const cols = (0, import_drizzle_orm4.getColumns)(table);
2594
+ const cols = visibleColumns(table);
2568
2595
  const propNameByColumnName = new Map(Object.entries(cols).map(([propName, col]) => [col.name, propName]));
2569
2596
  const toPropNames = (columnNames) => {
2570
2597
  const propNames = [];
@@ -2604,12 +2631,13 @@ var getUniqueColumnSets = (table, getTableConfig4) => {
2604
2631
  var DISTINCT_RN = "__drizzle_graphql_distinct_rn";
2605
2632
  var columnEnumCache = /* @__PURE__ */ new WeakMap();
2606
2633
  var generateColumnEnum = (table, enumName, description, predicate = () => true) => {
2634
+ const cacheKey = `${enumName}\0${excludedColumnsKey(table)}`;
2607
2635
  let tableCache = columnEnumCache.get(table);
2608
- const cached = tableCache?.get(enumName);
2636
+ const cached = tableCache?.get(cacheKey);
2609
2637
  if (cached) {
2610
2638
  return cached;
2611
2639
  }
2612
- const columnNames = Object.entries((0, import_drizzle_orm4.getColumns)(table)).filter(([columnName, column]) => predicate(column, columnName)).map(([columnName]) => columnName);
2640
+ const columnNames = Object.entries(visibleColumns(table)).filter(([columnName, column]) => predicate(column, columnName)).map(([columnName]) => columnName);
2613
2641
  if (!columnNames.length) {
2614
2642
  return void 0;
2615
2643
  }
@@ -2622,7 +2650,7 @@ var generateColumnEnum = (table, enumName, description, predicate = () => true)
2622
2650
  tableCache = /* @__PURE__ */ new Map();
2623
2651
  columnEnumCache.set(table, tableCache);
2624
2652
  }
2625
- tableCache.set(enumName, enumType);
2653
+ tableCache.set(cacheKey, enumType);
2626
2654
  return enumType;
2627
2655
  };
2628
2656
  var generateDistinctEnum = (table, typeName) => generateColumnEnum(table, `${typeName}DistinctColumn`, `Columns of ${typeName} that a query can be made distinct on`);
@@ -3210,7 +3238,7 @@ var classifyAggregateColumns = (table, tableName) => {
3210
3238
  const numeric = {};
3211
3239
  const orderable = {};
3212
3240
  const all = {};
3213
- for (const [columnName, column] of Object.entries((0, import_drizzle_orm5.getColumns)(table))) {
3241
+ for (const [columnName, column] of Object.entries(visibleColumns(table))) {
3214
3242
  all[columnName] = column;
3215
3243
  const converted = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, false);
3216
3244
  const gqlType = converted.type;
@@ -3454,7 +3482,7 @@ var HAVING_OPS = { eq: import_drizzle_orm5.eq, ne: import_drizzle_orm5.ne, gt: i
3454
3482
  var groupableColumns = (table, tableName) => {
3455
3483
  const { orderable } = classifyAggregateColumns(table, tableName);
3456
3484
  const groupable = { ...orderable };
3457
- for (const [columnName, column] of Object.entries((0, import_drizzle_orm5.getColumns)(table))) {
3485
+ for (const [columnName, column] of Object.entries(visibleColumns(table))) {
3458
3486
  if (groupable[columnName]) {
3459
3487
  continue;
3460
3488
  }
@@ -3922,7 +3950,8 @@ var generateSchemaData = (db, schema, relations, options) => {
3922
3950
  const { relationsDepthLimit, prefixes, suffixes, typeNameMapper, shouldEagerLoad, features, complexity, limits } = options;
3923
3951
  const rawSchema = schema;
3924
3952
  const schemaEntries = Object.entries(rawSchema);
3925
- const tableEntries = schemaEntries.filter(([_key, value]) => (0, import_drizzle_orm6.is)(value, import_mysql_core2.MySqlTable));
3953
+ const excludedTables = new Set(options.exclude?.tables ?? []);
3954
+ const tableEntries = schemaEntries.filter(([key, value]) => (0, import_drizzle_orm6.is)(value, import_mysql_core2.MySqlTable) && !excludedTables.has(key));
3926
3955
  const tables = Object.fromEntries(tableEntries);
3927
3956
  if (!tableEntries.length) {
3928
3957
  throw new Error(
@@ -3936,7 +3965,11 @@ var generateSchemaData = (db, schema, relations, options) => {
3936
3965
  }
3937
3966
  registerScalarOverrides(tables, options);
3938
3967
  registerEnumConfig(options);
3968
+ registerColumnExclusions(tables, options.exclude);
3939
3969
  const namedRelations = buildNamedRelations(relations ?? {}, tableEntries);
3970
+ for (const excluded of excludedTables) {
3971
+ delete namedRelations[excluded];
3972
+ }
3940
3973
  attachTargetPrimaryKeys(namedRelations, tables, mysqlPrimaryKeyPropNames);
3941
3974
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
3942
3975
  const filterCtx = { tables, relationMap: namedRelations };
@@ -4404,7 +4437,7 @@ var createNestedWriteTypes = (params) => {
4404
4437
  }
4405
4438
  const omitted = plan.fkSide === "child" ? plan.foreignColPropName : void 0;
4406
4439
  const fields = Object.fromEntries(
4407
- Object.entries((0, import_drizzle_orm7.getColumns)(plan.targetTable)).filter(([columnName]) => columnName !== omitted).map(([columnName, column]) => [
4440
+ Object.entries(visibleColumns(plan.targetTable)).filter(([columnName]) => columnName !== omitted).map(([columnName, column]) => [
4408
4441
  columnName,
4409
4442
  drizzleColumnToGraphQLType(column, columnName, plan.targetTableName, false, true, true)
4410
4443
  ])
@@ -5282,7 +5315,8 @@ function generateSchemaData2(db, schema, relations, options) {
5282
5315
  limits
5283
5316
  } = options;
5284
5317
  const schemaEntries = Object.entries(schema);
5285
- const tableEntries = schemaEntries.filter(([_key, value]) => (0, import_drizzle_orm8.is)(value, import_pg_core2.PgTable));
5318
+ const excludedTables = new Set(options.exclude?.tables ?? []);
5319
+ const tableEntries = schemaEntries.filter(([key, value]) => (0, import_drizzle_orm8.is)(value, import_pg_core2.PgTable) && !excludedTables.has(key));
5286
5320
  const tables = Object.fromEntries(tableEntries);
5287
5321
  if (!tableEntries.length) {
5288
5322
  throw new Error(
@@ -5291,7 +5325,11 @@ function generateSchemaData2(db, schema, relations, options) {
5291
5325
  }
5292
5326
  registerScalarOverrides(tables, options);
5293
5327
  registerEnumConfig(options);
5328
+ registerColumnExclusions(tables, options.exclude);
5294
5329
  const namedRelations = buildNamedRelations(relations ?? {}, tableEntries);
5330
+ for (const excluded of excludedTables) {
5331
+ delete namedRelations[excluded];
5332
+ }
5295
5333
  attachTargetPrimaryKeys(namedRelations, tables, pgPrimaryKeyPropNames);
5296
5334
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
5297
5335
  const filterCtx = { tables, relationMap: namedRelations };
@@ -6294,7 +6332,8 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6294
6332
  } = options;
6295
6333
  const rawSchema = schema;
6296
6334
  const schemaEntries = Object.entries(rawSchema);
6297
- const tableEntries = schemaEntries.filter(([_key, value]) => (0, import_drizzle_orm9.is)(value, import_sqlite_core2.SQLiteTable));
6335
+ const excludedTables = new Set(options.exclude?.tables ?? []);
6336
+ const tableEntries = schemaEntries.filter(([key, value]) => (0, import_drizzle_orm9.is)(value, import_sqlite_core2.SQLiteTable) && !excludedTables.has(key));
6298
6337
  const tables = Object.fromEntries(tableEntries);
6299
6338
  if (!tableEntries.length) {
6300
6339
  throw new Error(
@@ -6303,7 +6342,11 @@ var generateSchemaData3 = (db, schema, relations, options) => {
6303
6342
  }
6304
6343
  registerScalarOverrides(tables, options);
6305
6344
  registerEnumConfig(options);
6345
+ registerColumnExclusions(tables, options.exclude);
6306
6346
  const namedRelations = buildNamedRelations(relations ?? {}, tableEntries);
6347
+ for (const excluded of excludedTables) {
6348
+ delete namedRelations[excluded];
6349
+ }
6307
6350
  attachTargetPrimaryKeys(namedRelations, tables, sqlitePrimaryKeyPropNames);
6308
6351
  const eagerRelations = pruneNonEagerRelations(namedRelations, shouldEagerLoad);
6309
6352
  const filterCtx = { tables, relationMap: namedRelations };
@@ -6850,6 +6893,49 @@ var buildSchema = (db, config) => {
6850
6893
  limitPolicyCache.set(tableName, resolved);
6851
6894
  return resolved;
6852
6895
  } : void 0;
6896
+ const exclude = config?.exclude;
6897
+ if (exclude) {
6898
+ const tableNames = new Set(
6899
+ Object.entries(schema).filter(([, value]) => (0, import_drizzle_orm10.is)(value, import_drizzle_orm10.Table)).map(([key]) => key)
6900
+ );
6901
+ const excludedTables = new Set(exclude.tables ?? []);
6902
+ for (const tableName of excludedTables) {
6903
+ if (!tableNames.has(tableName)) {
6904
+ throw new Error(
6905
+ `Drizzle-GraphQL Error: config.exclude.tables names '${tableName}', which is not a table in the Drizzle schema.`
6906
+ );
6907
+ }
6908
+ }
6909
+ if (excludedTables.size >= tableNames.size) {
6910
+ throw new Error(
6911
+ "Drizzle-GraphQL Error: config.exclude.tables excludes every table in the schema, leaving nothing to generate."
6912
+ );
6913
+ }
6914
+ for (const [tableName, columnNames] of Object.entries(exclude.columns ?? {})) {
6915
+ if (!tableNames.has(tableName)) {
6916
+ throw new Error(
6917
+ `Drizzle-GraphQL Error: config.exclude.columns names table '${tableName}', which is not a table in the Drizzle schema.`
6918
+ );
6919
+ }
6920
+ if (excludedTables.has(tableName)) {
6921
+ continue;
6922
+ }
6923
+ const columns = (0, import_drizzle_orm10.getColumns)(schema[tableName]);
6924
+ for (const columnName of columnNames) {
6925
+ const column = columns[columnName];
6926
+ if (!column) {
6927
+ throw new Error(
6928
+ `Drizzle-GraphQL Error: config.exclude.columns names '${tableName}.${columnName}', which is not a column of that table.`
6929
+ );
6930
+ }
6931
+ if (column.notNull && !column.hasDefault && !column.defaultFn) {
6932
+ console.warn(
6933
+ `Drizzle-GraphQL Warning: excluded column '${tableName}.${columnName}' is NOT NULL with no default, so generated inserts for '${tableName}' can never succeed.`
6934
+ );
6935
+ }
6936
+ }
6937
+ }
6938
+ }
6853
6939
  const eagerOpt = config?.eagerLoadRelations;
6854
6940
  const shouldEagerLoad = eagerOpt === void 0 || eagerOpt === true ? () => true : eagerOpt === false ? () => false : eagerOpt;
6855
6941
  if (!typeNameMapper && suffixes.list === suffixes.single) {
@@ -6883,6 +6969,7 @@ var buildSchema = (db, config) => {
6883
6969
  enumNameMapper: config?.enumNameMapper,
6884
6970
  transactions,
6885
6971
  limits,
6972
+ exclude,
6886
6973
  docs: {
6887
6974
  describeColumn: config?.describeColumn,
6888
6975
  describeTable: config?.describeTable,