@vantreeseba/drizzle-graphql 2.0.0 → 4.0.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/dist/index.d.cts CHANGED
@@ -2,7 +2,8 @@ import { Table, Column, Relation, SQL, One, Many } from 'drizzle-orm';
2
2
  import { MySqlDatabase } from 'drizzle-orm/mysql-core';
3
3
  import { PgAsyncDatabase } from 'drizzle-orm/pg-core';
4
4
  import { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';
5
- import { GraphQLFieldResolver, GraphQLResolveInfo, GraphQLSchema, GraphQLInputObjectType, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLScalarType } from 'graphql';
5
+ import { GraphQLFieldResolver, GraphQLResolveInfo, GraphQLSchema, GraphQLInputObjectType, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLScalarType, GraphQLEnumType } from 'graphql';
6
+ export { GraphQLDate, GraphQLDateTime, GraphQLJSON, GraphQLUUID } from 'graphql-scalars';
6
7
 
7
8
  type TableNamedRelations = {
8
9
  relation: Relation;
@@ -89,6 +90,23 @@ type RelationResolverFactory = (params: {
89
90
  relEntry: TableNamedRelations;
90
91
  isOne: boolean;
91
92
  }) => GraphQLFieldResolver<any, any> | undefined;
93
+ /**
94
+ * Key on the GraphQL context object under which a caller can place a Drizzle transaction
95
+ * (or any other executor: a pooled connection, a logging proxy). Every generated resolver
96
+ * reads it at resolve time and runs its statements there instead of on the database the
97
+ * schema was built from, which is what lets several mutations in one request share a
98
+ * transaction and lets a query see that transaction's uncommitted rows:
99
+ *
100
+ * ```ts
101
+ * await db.transaction(async (tx) => {
102
+ * await graphql({ schema, source, contextValue: { [drizzleExecutorKey]: tx } });
103
+ * });
104
+ * ```
105
+ *
106
+ * Registered with `Symbol.for` so the ESM and CJS builds of this package agree on it when
107
+ * both end up loaded in one process.
108
+ */
109
+ declare const drizzleExecutorKey: unique symbol;
92
110
  /**
93
111
  * Creates a RelationResolverFactory that generates field-level resolvers for each relation.
94
112
  * Each resolver:
@@ -97,9 +115,41 @@ type RelationResolverFactory = (params: {
97
115
  * 3. Otherwise batches all sibling resolver calls within the same GraphQL execution tick
98
116
  * into a single IN-clause query, eliminating N+1 database round-trips.
99
117
  */
100
- declare const createRelationResolverFactory: (db: any, tables: Record<string, Table>) => RelationResolverFactory;
118
+ declare const createRelationResolverFactory: (db: any, tables: Record<string, Table>, filterCtx?: RelationFilterBase) => RelationResolverFactory;
101
119
  declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs) => SQL[];
102
- declare const extractFilters: <TTable extends Table>(table: TTable, tableName: string, filters: Filters<TTable>) => SQL | undefined;
120
+ /**
121
+ * Everything `extractFilters` needs to turn a relation key in a `where` argument into a
122
+ * correlated subquery. Omitted by callers that don't generate relation filters, in which case
123
+ * relation keys can't appear in the input to begin with.
124
+ */
125
+ interface RelationFilterContext {
126
+ /** Every table in the schema, keyed by its schema key. */
127
+ tables: Record<string, Table>;
128
+ /** Relations keyed by table schema key, then relation name. */
129
+ relationMap: Record<string, Record<string, TableNamedRelations>>;
130
+ /**
131
+ * Schema key of the table being filtered. Not always the same as the `tableName` label
132
+ * used in error messages (relation `where` callbacks pass the relation name there).
133
+ */
134
+ tableKey: string;
135
+ /** Shared counter making every subquery alias unique within one extraction. */
136
+ aliases?: {
137
+ n: number;
138
+ };
139
+ }
140
+ /**
141
+ * The build-scoped half of {@link RelationFilterContext}. Created once per generated schema and
142
+ * handed to every resolver, which adds the table it is filtering.
143
+ */
144
+ type RelationFilterBase = Pick<RelationFilterContext, 'tables' | 'relationMap'>;
145
+ declare const extractFilters: <TTable extends Table>(table: TTable, tableName: string, filters: Filters<TTable>, relationCtx?: RelationFilterContext) => SQL | undefined;
146
+ /**
147
+ * Default for `config.onError`: keeps drizzle-graphql's own errors, which are written for
148
+ * the client, and replaces driver/database errors with a generic message. Their text names
149
+ * tables, columns, constraints and offending values, none of which belongs in a response.
150
+ * The original is preserved on `originalError` for server-side logging.
151
+ */
152
+ declare const defaultErrorMapper: (error: unknown) => unknown;
103
153
 
104
154
  type Relations<TTable extends string = string, TConfig extends Record<string, Relation> = Record<string, Relation>> = {
105
155
  table: {
@@ -144,6 +194,22 @@ type UpdateArgs<TTable extends Table> = Partial<{
144
194
  set: GetRemappedTableUpdateDataType<TTable>;
145
195
  where?: Filters<TTable>;
146
196
  }>;
197
+ /**
198
+ * The `onConflict` argument of the generated upsert mutations.
199
+ *
200
+ * `target` and `where` exist on PostgreSQL and SQLite only: MySQL's
201
+ * `ON DUPLICATE KEY UPDATE` fires on whichever unique key was violated and takes no
202
+ * predicate, so neither field is generated there.
203
+ */
204
+ type UpsertConflictArgs<TTable extends Table> = {
205
+ action?: 'UPDATE' | 'NOTHING';
206
+ target?: string[];
207
+ update?: string[];
208
+ where?: Filters<TTable>;
209
+ };
210
+ type UpsertArgs<TTable extends Table, isSingle extends boolean> = InsertArgs<TTable, isSingle> & {
211
+ onConflict?: UpsertConflictArgs<TTable>;
212
+ };
147
213
  type DeleteArgs<TTable extends Table> = {
148
214
  where?: Filters<TTable>;
149
215
  };
@@ -153,9 +219,23 @@ type SelectResolver<TTable extends Table, TTables extends Record<string, Table>,
153
219
  type SelectSingleResolver<TTable extends Table, TTables extends Record<string, Table>, TRelations extends Record<string, Relation>> = (source: any, args: Partial<QueryArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<(keyof TRelations extends infer RelKey ? RelKey extends string ? GetRemappedTableDataType<TTable> & {
154
220
  [K in RelKey]: TRelations[K] extends One<string> ? GetRemappedTableDataType<ExtractTableByName<TTables, TRelations[K]['referencedTableName']> extends infer T ? T[keyof T] : never> | null : TRelations[K] extends Many<string> ? Array<GetRemappedTableDataType<ExtractTableByName<TTables, TRelations[K]['referencedTableName']> extends infer T ? T[keyof T] : never>> : never;
155
221
  } : GetRemappedTableDataType<TTable> : GetRemappedTableDataType<TTable>) | null>;
156
- type InsertResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, false>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? Array<GetRemappedTableDataType<TTable>> : MutationReturnlessResult>;
157
- type InsertArrResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
222
+ /** Resolver for `create<Table>Single`: one row in, one row (or `undefined`) out. */
223
+ type InsertResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
224
+ /** Resolver for `create<Table>`: an array of rows in, an array of rows out. */
225
+ type InsertArrResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, false>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? Array<GetRemappedTableDataType<TTable>> : MutationReturnlessResult>;
158
226
  type UpdateResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: UpdateArgs<TTable>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
227
+ /** Resolver for `upsert<Table>Single`. */
228
+ type UpsertResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<UpsertArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
229
+ /** Resolver for `upsert<Table>`. */
230
+ type UpsertArrResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<UpsertArgs<TTable, false>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? Array<GetRemappedTableDataType<TTable>> : MutationReturnlessResult>;
231
+ /**
232
+ * Resolver for a table's generated aggregate query (`<plural>Aggregate`).
233
+ * Returns only the requested aggregations: `count` plus per-column `avg` / `sum`
234
+ * (numeric columns, as Float) and `min` / `max` (orderable columns, as the column's type).
235
+ */
236
+ type AggregateResolver<TTable extends Table> = (source: any, args: {
237
+ where?: Filters<TTable>;
238
+ }, context: any, info: GraphQLResolveInfo) => Promise<Record<string, any>>;
159
239
  type DeleteResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: DeleteArgs<TTable>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
160
240
  type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations extends Record<string, Relations>, TInputs extends Record<string, GraphQLInputObjectType>, TOutputs extends Record<string, GraphQLObjectType>> = {
161
241
  [TName in keyof TSchemaTables as TName extends string ? `${Uncapitalize<TName>}` : never]: TName extends string ? {
@@ -173,6 +253,9 @@ type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations e
173
253
  where: {
174
254
  type: TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never;
175
255
  };
256
+ distinct: {
257
+ type: GraphQLList<GraphQLNonNull<GraphQLEnumType>>;
258
+ };
176
259
  };
177
260
  resolve: SelectResolver<TSchemaTables[TName], TSchemaTables, ExtractTableRelations<TSchemaTables[TName], TSchemaRelations> extends infer R ? R[keyof R] : never>;
178
261
  } : never;
@@ -192,6 +275,16 @@ type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations e
192
275
  };
193
276
  resolve: SelectSingleResolver<TSchemaTables[TName], TSchemaTables, ExtractTableRelations<TSchemaTables[TName], TSchemaRelations> extends infer R ? R[keyof R] : never>;
194
277
  } : never;
278
+ } & {
279
+ [TName in keyof TSchemaTables as TName extends string ? `${Uncapitalize<TName>}Aggregate` : never]: TName extends string ? {
280
+ type: GraphQLNonNull<TOutputs[`${Capitalize<TName>}Aggregate`] extends GraphQLObjectType ? TOutputs[`${Capitalize<TName>}Aggregate`] : GraphQLObjectType>;
281
+ args: {
282
+ where: {
283
+ type: TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never;
284
+ };
285
+ };
286
+ resolve: AggregateResolver<TSchemaTables[TName]>;
287
+ } : never;
195
288
  };
196
289
  type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends Record<string, GraphQLInputObjectType>, TOutputs extends Record<string, GraphQLObjectType>, IsReturnless extends boolean> = {
197
290
  [TName in keyof TSchemaTables as TName extends string ? `create${Capitalize<TName>}` : never]: TName extends string ? {
@@ -213,6 +306,32 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
213
306
  };
214
307
  resolve: InsertResolver<TSchemaTables[TName], IsReturnless>;
215
308
  } : never;
309
+ } & {
310
+ [TName in keyof TSchemaTables as TName extends string ? `upsert${Capitalize<TName>}` : never]?: TName extends string ? {
311
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
312
+ args: {
313
+ values: {
314
+ type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>>>;
315
+ };
316
+ onConflict: {
317
+ type: GraphQLInputObjectType;
318
+ };
319
+ };
320
+ resolve: UpsertArrResolver<TSchemaTables[TName], IsReturnless>;
321
+ } : never;
322
+ } & {
323
+ [TName in keyof TSchemaTables as TName extends string ? `upsert${Capitalize<TName>}Single` : never]?: TName extends string ? {
324
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[`${Capitalize<TName>}Item`];
325
+ args: {
326
+ values: {
327
+ type: GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>;
328
+ };
329
+ onConflict: {
330
+ type: GraphQLInputObjectType;
331
+ };
332
+ };
333
+ resolve: UpsertResolver<TSchemaTables[TName], IsReturnless>;
334
+ } : never;
216
335
  } & {
217
336
  [TName in keyof TSchemaTables as TName extends string ? `update${Capitalize<TName>}` : never]: TName extends string ? {
218
337
  type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
@@ -248,6 +367,8 @@ type GeneratedInputs<TSchema extends Record<string, Table>> = {
248
367
  };
249
368
  type GeneratedOutputs<TSchema extends Record<string, Table>, IsReturnless extends boolean> = {
250
369
  [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}SelectItem` : never]: GraphQLObjectType;
370
+ } & {
371
+ [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}Aggregate` : never]: GraphQLObjectType;
251
372
  } & (IsReturnless extends true ? {
252
373
  MutationReturn: GraphQLObjectType;
253
374
  } : {
@@ -272,6 +393,32 @@ type GeneratedData<TDatabase extends AnyDrizzleDB<any>> = {
272
393
  schema: GraphQLSchema;
273
394
  entities: GeneratedEntities<TDatabase>;
274
395
  };
396
+ /**
397
+ * Per-feature switches for what `buildSchema` generates. Every flag defaults to `true`.
398
+ * See {@link BuildSchemaConfig.features}.
399
+ */
400
+ type SchemaFeatures = {
401
+ /** `<plural>Aggregate` root queries and the aggregate output types. @default true */
402
+ aggregates?: boolean;
403
+ /** `<relation>Aggregate` fields on object types, for to-many relations. @default true */
404
+ relationAggregates?: boolean;
405
+ /** The `distinct` argument on list queries. @default true */
406
+ distinct?: boolean;
407
+ /** `create<Table>` / `create<Table>Single` mutations. @default true */
408
+ insert?: boolean;
409
+ /** `update<Table>` mutations. @default true */
410
+ update?: boolean;
411
+ /** `delete<Table>` mutations. @default true */
412
+ delete?: boolean;
413
+ /**
414
+ * `upsert<Table>` / `upsert<Table>Single` mutations — insert, or update the row that
415
+ * already holds the same unique key. Off unless asked for, so an existing schema does
416
+ * not grow new mutations on upgrade.
417
+ *
418
+ * @default false
419
+ */
420
+ upsert?: boolean;
421
+ };
275
422
  type BuildSchemaConfig = {
276
423
  /**
277
424
  * Determines whether generated mutations will be passed to returned schema.
@@ -304,7 +451,7 @@ type BuildSchemaConfig = {
304
451
  /**
305
452
  * Customizes query name prefixes for generated GraphQL operations.
306
453
  *
307
- * @default { insert: 'create', delete: 'delete', update: 'update' }
454
+ * @default { insert: 'create', delete: 'delete', update: 'update', upsert: 'upsert' }
308
455
  */
309
456
  prefixes?: {
310
457
  /** Prefix for insert mutations (e.g., 'users' -> 'createUsers') */
@@ -313,6 +460,8 @@ type BuildSchemaConfig = {
313
460
  delete?: string;
314
461
  /** Prefix for update mutations (e.g., 'users' -> 'updateUsers') */
315
462
  update?: string;
463
+ /** Prefix for upsert mutations (e.g., 'users' -> 'upsertUsers') */
464
+ upsert?: string;
316
465
  };
317
466
  /**
318
467
  * Customizes query name suffixes for generated GraphQL operations.
@@ -328,8 +477,40 @@ type BuildSchemaConfig = {
328
477
  /**
329
478
  * When true, insert mutations will use onConflictDoNothing() to silently
330
479
  * ignore duplicate key violations. Defaults to false (conflicts throw errors).
480
+ *
481
+ * PostgreSQL and SQLite only — MySQL's insert builder has no equivalent, so the flag is
482
+ * ignored there and conflicts keep throwing.
483
+ *
484
+ * @deprecated Build-wide and unconditional: a request cannot opt out of it, and a
485
+ * swallowed insert returns `null` with no indication why. Turn on `features.upsert` and
486
+ * pass `onConflict: { action: NOTHING }` per request instead. This flag keeps working
487
+ * until the next major.
331
488
  */
332
489
  conflictDoNothing?: boolean;
490
+ /**
491
+ * Turns individual generated features off.
492
+ *
493
+ * Every flag defaults to `true` except `upsert`, which is opt-in: a build with no
494
+ * `features` block generates exactly what it generated before this option existed.
495
+ * Turning one off removes both the schema surface it adds and the work behind it.
496
+ *
497
+ * ```ts
498
+ * buildSchema(db, {
499
+ * features: {
500
+ * aggregates: false, // no `<plural>Aggregate` root queries
501
+ * relationAggregates: false, // no `<relation>Aggregate` fields on object types
502
+ * distinct: false, // no `distinct` argument on list queries
503
+ * delete: false, // no delete mutations
504
+ * upsert: true, // opt in to `upsert<Table>` mutations
505
+ * },
506
+ * });
507
+ * ```
508
+ *
509
+ * Turning off every mutation feature omits the `Mutation` type entirely, exactly as
510
+ * `mutations: false` does. Query-side features can all be off — the list and single
511
+ * queries are always generated, so `Query` is never empty.
512
+ */
513
+ features?: SchemaFeatures;
333
514
  /**
334
515
  * Optional mapper from table key to singular/plural name pair.
335
516
  * When provided for a table, overrides the default (table key) naming for GraphQL type names,
@@ -377,8 +558,40 @@ type BuildSchemaConfig = {
377
558
  * @default true
378
559
  */
379
560
  eagerLoadRelations?: boolean | ((tableName: string, relationName: string) => boolean);
561
+ /**
562
+ * Called for every error thrown by a generated resolver, before it reaches the client.
563
+ *
564
+ * Return an error to surface that one instead. Return nothing to fall through to the
565
+ * default handling, which makes this a pure logging hook:
566
+ *
567
+ * ```ts
568
+ * buildSchema(db, { onError: (error) => { logger.error(error) } })
569
+ * ```
570
+ *
571
+ * The default passes through errors drizzle-graphql raises itself (bad filter, missing
572
+ * values, invalid date, …) and replaces everything else — driver and database errors —
573
+ * with a generic `Internal server error`, keeping the original on `originalError` so it
574
+ * is still available to server-side logging. Database messages routinely name tables,
575
+ * columns, constraints and the values that violated them, which is not something a
576
+ * public API should hand back.
577
+ *
578
+ * To surface raw database errors instead (useful in development):
579
+ *
580
+ * ```ts
581
+ * buildSchema(db, { onError: (error) => error as Error })
582
+ * ```
583
+ */
584
+ onError?: (error: unknown) => unknown;
380
585
  };
381
586
 
587
+ /**
588
+ * A 64-bit integer. Always transported as a decimal string, in both directions, so no value
589
+ * is ever silently rounded by JSON's double-precision numbers. `graphql-scalars`' own
590
+ * `GraphQLBigInt` is deliberately not used: it emits numbers for safe integers and strings
591
+ * for everything else, so a client cannot know which it will get.
592
+ */
593
+ declare const GraphQLBigIntString: GraphQLScalarType<string, string>;
594
+
382
595
  declare const buildSchema: <TDbClient extends AnyDrizzleDB<any>>(db: TDbClient, config?: BuildSchemaConfig) => GeneratedData<TDbClient>;
383
596
 
384
- export { type AnyDrizzleDB, type BuildSchemaConfig, type DeleteResolver, type ExtractRelations, type ExtractTableByName, type ExtractTableRelations, type ExtractTables, type GeneratedData, type GeneratedEntities, type GeneratedInputs, type GeneratedOutputs, type InsertArrResolver, type InsertResolver, type MutationReturnlessResult, type MutationsCore, type QueriesCore, type RelationResolverFactory, type SelectResolver, type SelectSingleResolver, type TableNamedRelations, type UpdateResolver, buildSchema, createRelationResolverFactory, extractFilters, extractOrderBy, extractRelationJoinColumns };
597
+ export { type AggregateResolver, type AnyDrizzleDB, type BuildSchemaConfig, type DeleteResolver, type ExtractRelations, type ExtractTableByName, type ExtractTableRelations, type ExtractTables, type GeneratedData, type GeneratedEntities, type GeneratedInputs, type GeneratedOutputs, GraphQLBigIntString, type InsertArrResolver, type InsertResolver, type MutationReturnlessResult, type MutationsCore, type QueriesCore, type RelationResolverFactory, type SchemaFeatures, type SelectResolver, type SelectSingleResolver, type TableNamedRelations, type UpdateResolver, type UpsertArgs, type UpsertArrResolver, type UpsertConflictArgs, type UpsertResolver, buildSchema, createRelationResolverFactory, defaultErrorMapper, drizzleExecutorKey, extractFilters, extractOrderBy, extractRelationJoinColumns };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@ import { Table, Column, Relation, SQL, One, Many } from 'drizzle-orm';
2
2
  import { MySqlDatabase } from 'drizzle-orm/mysql-core';
3
3
  import { PgAsyncDatabase } from 'drizzle-orm/pg-core';
4
4
  import { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';
5
- import { GraphQLFieldResolver, GraphQLResolveInfo, GraphQLSchema, GraphQLInputObjectType, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLScalarType } from 'graphql';
5
+ import { GraphQLFieldResolver, GraphQLResolveInfo, GraphQLSchema, GraphQLInputObjectType, GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLScalarType, GraphQLEnumType } from 'graphql';
6
+ export { GraphQLDate, GraphQLDateTime, GraphQLJSON, GraphQLUUID } from 'graphql-scalars';
6
7
 
7
8
  type TableNamedRelations = {
8
9
  relation: Relation;
@@ -89,6 +90,23 @@ type RelationResolverFactory = (params: {
89
90
  relEntry: TableNamedRelations;
90
91
  isOne: boolean;
91
92
  }) => GraphQLFieldResolver<any, any> | undefined;
93
+ /**
94
+ * Key on the GraphQL context object under which a caller can place a Drizzle transaction
95
+ * (or any other executor: a pooled connection, a logging proxy). Every generated resolver
96
+ * reads it at resolve time and runs its statements there instead of on the database the
97
+ * schema was built from, which is what lets several mutations in one request share a
98
+ * transaction and lets a query see that transaction's uncommitted rows:
99
+ *
100
+ * ```ts
101
+ * await db.transaction(async (tx) => {
102
+ * await graphql({ schema, source, contextValue: { [drizzleExecutorKey]: tx } });
103
+ * });
104
+ * ```
105
+ *
106
+ * Registered with `Symbol.for` so the ESM and CJS builds of this package agree on it when
107
+ * both end up loaded in one process.
108
+ */
109
+ declare const drizzleExecutorKey: unique symbol;
92
110
  /**
93
111
  * Creates a RelationResolverFactory that generates field-level resolvers for each relation.
94
112
  * Each resolver:
@@ -97,9 +115,41 @@ type RelationResolverFactory = (params: {
97
115
  * 3. Otherwise batches all sibling resolver calls within the same GraphQL execution tick
98
116
  * into a single IN-clause query, eliminating N+1 database round-trips.
99
117
  */
100
- declare const createRelationResolverFactory: (db: any, tables: Record<string, Table>) => RelationResolverFactory;
118
+ declare const createRelationResolverFactory: (db: any, tables: Record<string, Table>, filterCtx?: RelationFilterBase) => RelationResolverFactory;
101
119
  declare const extractOrderBy: <TTable extends Table, TArgs extends OrderByArgs<any> = OrderByArgs<TTable>>(table: TTable, orderArgs: TArgs) => SQL[];
102
- declare const extractFilters: <TTable extends Table>(table: TTable, tableName: string, filters: Filters<TTable>) => SQL | undefined;
120
+ /**
121
+ * Everything `extractFilters` needs to turn a relation key in a `where` argument into a
122
+ * correlated subquery. Omitted by callers that don't generate relation filters, in which case
123
+ * relation keys can't appear in the input to begin with.
124
+ */
125
+ interface RelationFilterContext {
126
+ /** Every table in the schema, keyed by its schema key. */
127
+ tables: Record<string, Table>;
128
+ /** Relations keyed by table schema key, then relation name. */
129
+ relationMap: Record<string, Record<string, TableNamedRelations>>;
130
+ /**
131
+ * Schema key of the table being filtered. Not always the same as the `tableName` label
132
+ * used in error messages (relation `where` callbacks pass the relation name there).
133
+ */
134
+ tableKey: string;
135
+ /** Shared counter making every subquery alias unique within one extraction. */
136
+ aliases?: {
137
+ n: number;
138
+ };
139
+ }
140
+ /**
141
+ * The build-scoped half of {@link RelationFilterContext}. Created once per generated schema and
142
+ * handed to every resolver, which adds the table it is filtering.
143
+ */
144
+ type RelationFilterBase = Pick<RelationFilterContext, 'tables' | 'relationMap'>;
145
+ declare const extractFilters: <TTable extends Table>(table: TTable, tableName: string, filters: Filters<TTable>, relationCtx?: RelationFilterContext) => SQL | undefined;
146
+ /**
147
+ * Default for `config.onError`: keeps drizzle-graphql's own errors, which are written for
148
+ * the client, and replaces driver/database errors with a generic message. Their text names
149
+ * tables, columns, constraints and offending values, none of which belongs in a response.
150
+ * The original is preserved on `originalError` for server-side logging.
151
+ */
152
+ declare const defaultErrorMapper: (error: unknown) => unknown;
103
153
 
104
154
  type Relations<TTable extends string = string, TConfig extends Record<string, Relation> = Record<string, Relation>> = {
105
155
  table: {
@@ -144,6 +194,22 @@ type UpdateArgs<TTable extends Table> = Partial<{
144
194
  set: GetRemappedTableUpdateDataType<TTable>;
145
195
  where?: Filters<TTable>;
146
196
  }>;
197
+ /**
198
+ * The `onConflict` argument of the generated upsert mutations.
199
+ *
200
+ * `target` and `where` exist on PostgreSQL and SQLite only: MySQL's
201
+ * `ON DUPLICATE KEY UPDATE` fires on whichever unique key was violated and takes no
202
+ * predicate, so neither field is generated there.
203
+ */
204
+ type UpsertConflictArgs<TTable extends Table> = {
205
+ action?: 'UPDATE' | 'NOTHING';
206
+ target?: string[];
207
+ update?: string[];
208
+ where?: Filters<TTable>;
209
+ };
210
+ type UpsertArgs<TTable extends Table, isSingle extends boolean> = InsertArgs<TTable, isSingle> & {
211
+ onConflict?: UpsertConflictArgs<TTable>;
212
+ };
147
213
  type DeleteArgs<TTable extends Table> = {
148
214
  where?: Filters<TTable>;
149
215
  };
@@ -153,9 +219,23 @@ type SelectResolver<TTable extends Table, TTables extends Record<string, Table>,
153
219
  type SelectSingleResolver<TTable extends Table, TTables extends Record<string, Table>, TRelations extends Record<string, Relation>> = (source: any, args: Partial<QueryArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<(keyof TRelations extends infer RelKey ? RelKey extends string ? GetRemappedTableDataType<TTable> & {
154
220
  [K in RelKey]: TRelations[K] extends One<string> ? GetRemappedTableDataType<ExtractTableByName<TTables, TRelations[K]['referencedTableName']> extends infer T ? T[keyof T] : never> | null : TRelations[K] extends Many<string> ? Array<GetRemappedTableDataType<ExtractTableByName<TTables, TRelations[K]['referencedTableName']> extends infer T ? T[keyof T] : never>> : never;
155
221
  } : GetRemappedTableDataType<TTable> : GetRemappedTableDataType<TTable>) | null>;
156
- type InsertResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, false>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? Array<GetRemappedTableDataType<TTable>> : MutationReturnlessResult>;
157
- type InsertArrResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
222
+ /** Resolver for `create<Table>Single`: one row in, one row (or `undefined`) out. */
223
+ type InsertResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
224
+ /** Resolver for `create<Table>`: an array of rows in, an array of rows out. */
225
+ type InsertArrResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<InsertArgs<TTable, false>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? Array<GetRemappedTableDataType<TTable>> : MutationReturnlessResult>;
158
226
  type UpdateResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: UpdateArgs<TTable>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
227
+ /** Resolver for `upsert<Table>Single`. */
228
+ type UpsertResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<UpsertArgs<TTable, true>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
229
+ /** Resolver for `upsert<Table>`. */
230
+ type UpsertArrResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: Partial<UpsertArgs<TTable, false>>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? Array<GetRemappedTableDataType<TTable>> : MutationReturnlessResult>;
231
+ /**
232
+ * Resolver for a table's generated aggregate query (`<plural>Aggregate`).
233
+ * Returns only the requested aggregations: `count` plus per-column `avg` / `sum`
234
+ * (numeric columns, as Float) and `min` / `max` (orderable columns, as the column's type).
235
+ */
236
+ type AggregateResolver<TTable extends Table> = (source: any, args: {
237
+ where?: Filters<TTable>;
238
+ }, context: any, info: GraphQLResolveInfo) => Promise<Record<string, any>>;
159
239
  type DeleteResolver<TTable extends Table, IsReturnless extends boolean> = (source: any, args: DeleteArgs<TTable>, context: any, info: GraphQLResolveInfo) => Promise<IsReturnless extends false ? GetRemappedTableDataType<TTable> | undefined : MutationReturnlessResult>;
160
240
  type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations extends Record<string, Relations>, TInputs extends Record<string, GraphQLInputObjectType>, TOutputs extends Record<string, GraphQLObjectType>> = {
161
241
  [TName in keyof TSchemaTables as TName extends string ? `${Uncapitalize<TName>}` : never]: TName extends string ? {
@@ -173,6 +253,9 @@ type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations e
173
253
  where: {
174
254
  type: TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never;
175
255
  };
256
+ distinct: {
257
+ type: GraphQLList<GraphQLNonNull<GraphQLEnumType>>;
258
+ };
176
259
  };
177
260
  resolve: SelectResolver<TSchemaTables[TName], TSchemaTables, ExtractTableRelations<TSchemaTables[TName], TSchemaRelations> extends infer R ? R[keyof R] : never>;
178
261
  } : never;
@@ -192,6 +275,16 @@ type QueriesCore<TSchemaTables extends Record<string, Table>, TSchemaRelations e
192
275
  };
193
276
  resolve: SelectSingleResolver<TSchemaTables[TName], TSchemaTables, ExtractTableRelations<TSchemaTables[TName], TSchemaRelations> extends infer R ? R[keyof R] : never>;
194
277
  } : never;
278
+ } & {
279
+ [TName in keyof TSchemaTables as TName extends string ? `${Uncapitalize<TName>}Aggregate` : never]: TName extends string ? {
280
+ type: GraphQLNonNull<TOutputs[`${Capitalize<TName>}Aggregate`] extends GraphQLObjectType ? TOutputs[`${Capitalize<TName>}Aggregate`] : GraphQLObjectType>;
281
+ args: {
282
+ where: {
283
+ type: TInputs[`${Capitalize<TName>}Filters`] extends GraphQLInputObjectType ? TInputs[`${Capitalize<TName>}Filters`] : never;
284
+ };
285
+ };
286
+ resolve: AggregateResolver<TSchemaTables[TName]>;
287
+ } : never;
195
288
  };
196
289
  type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends Record<string, GraphQLInputObjectType>, TOutputs extends Record<string, GraphQLObjectType>, IsReturnless extends boolean> = {
197
290
  [TName in keyof TSchemaTables as TName extends string ? `create${Capitalize<TName>}` : never]: TName extends string ? {
@@ -213,6 +306,32 @@ type MutationsCore<TSchemaTables extends Record<string, Table>, TInputs extends
213
306
  };
214
307
  resolve: InsertResolver<TSchemaTables[TName], IsReturnless>;
215
308
  } : never;
309
+ } & {
310
+ [TName in keyof TSchemaTables as TName extends string ? `upsert${Capitalize<TName>}` : never]?: TName extends string ? {
311
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
312
+ args: {
313
+ values: {
314
+ type: GraphQLNonNull<GraphQLList<GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>>>;
315
+ };
316
+ onConflict: {
317
+ type: GraphQLInputObjectType;
318
+ };
319
+ };
320
+ resolve: UpsertArrResolver<TSchemaTables[TName], IsReturnless>;
321
+ } : never;
322
+ } & {
323
+ [TName in keyof TSchemaTables as TName extends string ? `upsert${Capitalize<TName>}Single` : never]?: TName extends string ? {
324
+ type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : TOutputs[`${Capitalize<TName>}Item`];
325
+ args: {
326
+ values: {
327
+ type: GraphQLNonNull<TInputs[`${Capitalize<TName>}InsertInput`]>;
328
+ };
329
+ onConflict: {
330
+ type: GraphQLInputObjectType;
331
+ };
332
+ };
333
+ resolve: UpsertResolver<TSchemaTables[TName], IsReturnless>;
334
+ } : never;
216
335
  } & {
217
336
  [TName in keyof TSchemaTables as TName extends string ? `update${Capitalize<TName>}` : never]: TName extends string ? {
218
337
  type: IsReturnless extends true ? TOutputs['MutationReturn'] extends GraphQLObjectType ? TOutputs['MutationReturn'] : never : GraphQLNonNull<GraphQLList<GraphQLNonNull<TOutputs[`${Capitalize<TName>}Item`]>>>;
@@ -248,6 +367,8 @@ type GeneratedInputs<TSchema extends Record<string, Table>> = {
248
367
  };
249
368
  type GeneratedOutputs<TSchema extends Record<string, Table>, IsReturnless extends boolean> = {
250
369
  [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}SelectItem` : never]: GraphQLObjectType;
370
+ } & {
371
+ [TName in keyof TSchema as TName extends string ? `${Capitalize<TName>}Aggregate` : never]: GraphQLObjectType;
251
372
  } & (IsReturnless extends true ? {
252
373
  MutationReturn: GraphQLObjectType;
253
374
  } : {
@@ -272,6 +393,32 @@ type GeneratedData<TDatabase extends AnyDrizzleDB<any>> = {
272
393
  schema: GraphQLSchema;
273
394
  entities: GeneratedEntities<TDatabase>;
274
395
  };
396
+ /**
397
+ * Per-feature switches for what `buildSchema` generates. Every flag defaults to `true`.
398
+ * See {@link BuildSchemaConfig.features}.
399
+ */
400
+ type SchemaFeatures = {
401
+ /** `<plural>Aggregate` root queries and the aggregate output types. @default true */
402
+ aggregates?: boolean;
403
+ /** `<relation>Aggregate` fields on object types, for to-many relations. @default true */
404
+ relationAggregates?: boolean;
405
+ /** The `distinct` argument on list queries. @default true */
406
+ distinct?: boolean;
407
+ /** `create<Table>` / `create<Table>Single` mutations. @default true */
408
+ insert?: boolean;
409
+ /** `update<Table>` mutations. @default true */
410
+ update?: boolean;
411
+ /** `delete<Table>` mutations. @default true */
412
+ delete?: boolean;
413
+ /**
414
+ * `upsert<Table>` / `upsert<Table>Single` mutations — insert, or update the row that
415
+ * already holds the same unique key. Off unless asked for, so an existing schema does
416
+ * not grow new mutations on upgrade.
417
+ *
418
+ * @default false
419
+ */
420
+ upsert?: boolean;
421
+ };
275
422
  type BuildSchemaConfig = {
276
423
  /**
277
424
  * Determines whether generated mutations will be passed to returned schema.
@@ -304,7 +451,7 @@ type BuildSchemaConfig = {
304
451
  /**
305
452
  * Customizes query name prefixes for generated GraphQL operations.
306
453
  *
307
- * @default { insert: 'create', delete: 'delete', update: 'update' }
454
+ * @default { insert: 'create', delete: 'delete', update: 'update', upsert: 'upsert' }
308
455
  */
309
456
  prefixes?: {
310
457
  /** Prefix for insert mutations (e.g., 'users' -> 'createUsers') */
@@ -313,6 +460,8 @@ type BuildSchemaConfig = {
313
460
  delete?: string;
314
461
  /** Prefix for update mutations (e.g., 'users' -> 'updateUsers') */
315
462
  update?: string;
463
+ /** Prefix for upsert mutations (e.g., 'users' -> 'upsertUsers') */
464
+ upsert?: string;
316
465
  };
317
466
  /**
318
467
  * Customizes query name suffixes for generated GraphQL operations.
@@ -328,8 +477,40 @@ type BuildSchemaConfig = {
328
477
  /**
329
478
  * When true, insert mutations will use onConflictDoNothing() to silently
330
479
  * ignore duplicate key violations. Defaults to false (conflicts throw errors).
480
+ *
481
+ * PostgreSQL and SQLite only — MySQL's insert builder has no equivalent, so the flag is
482
+ * ignored there and conflicts keep throwing.
483
+ *
484
+ * @deprecated Build-wide and unconditional: a request cannot opt out of it, and a
485
+ * swallowed insert returns `null` with no indication why. Turn on `features.upsert` and
486
+ * pass `onConflict: { action: NOTHING }` per request instead. This flag keeps working
487
+ * until the next major.
331
488
  */
332
489
  conflictDoNothing?: boolean;
490
+ /**
491
+ * Turns individual generated features off.
492
+ *
493
+ * Every flag defaults to `true` except `upsert`, which is opt-in: a build with no
494
+ * `features` block generates exactly what it generated before this option existed.
495
+ * Turning one off removes both the schema surface it adds and the work behind it.
496
+ *
497
+ * ```ts
498
+ * buildSchema(db, {
499
+ * features: {
500
+ * aggregates: false, // no `<plural>Aggregate` root queries
501
+ * relationAggregates: false, // no `<relation>Aggregate` fields on object types
502
+ * distinct: false, // no `distinct` argument on list queries
503
+ * delete: false, // no delete mutations
504
+ * upsert: true, // opt in to `upsert<Table>` mutations
505
+ * },
506
+ * });
507
+ * ```
508
+ *
509
+ * Turning off every mutation feature omits the `Mutation` type entirely, exactly as
510
+ * `mutations: false` does. Query-side features can all be off — the list and single
511
+ * queries are always generated, so `Query` is never empty.
512
+ */
513
+ features?: SchemaFeatures;
333
514
  /**
334
515
  * Optional mapper from table key to singular/plural name pair.
335
516
  * When provided for a table, overrides the default (table key) naming for GraphQL type names,
@@ -377,8 +558,40 @@ type BuildSchemaConfig = {
377
558
  * @default true
378
559
  */
379
560
  eagerLoadRelations?: boolean | ((tableName: string, relationName: string) => boolean);
561
+ /**
562
+ * Called for every error thrown by a generated resolver, before it reaches the client.
563
+ *
564
+ * Return an error to surface that one instead. Return nothing to fall through to the
565
+ * default handling, which makes this a pure logging hook:
566
+ *
567
+ * ```ts
568
+ * buildSchema(db, { onError: (error) => { logger.error(error) } })
569
+ * ```
570
+ *
571
+ * The default passes through errors drizzle-graphql raises itself (bad filter, missing
572
+ * values, invalid date, …) and replaces everything else — driver and database errors —
573
+ * with a generic `Internal server error`, keeping the original on `originalError` so it
574
+ * is still available to server-side logging. Database messages routinely name tables,
575
+ * columns, constraints and the values that violated them, which is not something a
576
+ * public API should hand back.
577
+ *
578
+ * To surface raw database errors instead (useful in development):
579
+ *
580
+ * ```ts
581
+ * buildSchema(db, { onError: (error) => error as Error })
582
+ * ```
583
+ */
584
+ onError?: (error: unknown) => unknown;
380
585
  };
381
586
 
587
+ /**
588
+ * A 64-bit integer. Always transported as a decimal string, in both directions, so no value
589
+ * is ever silently rounded by JSON's double-precision numbers. `graphql-scalars`' own
590
+ * `GraphQLBigInt` is deliberately not used: it emits numbers for safe integers and strings
591
+ * for everything else, so a client cannot know which it will get.
592
+ */
593
+ declare const GraphQLBigIntString: GraphQLScalarType<string, string>;
594
+
382
595
  declare const buildSchema: <TDbClient extends AnyDrizzleDB<any>>(db: TDbClient, config?: BuildSchemaConfig) => GeneratedData<TDbClient>;
383
596
 
384
- export { type AnyDrizzleDB, type BuildSchemaConfig, type DeleteResolver, type ExtractRelations, type ExtractTableByName, type ExtractTableRelations, type ExtractTables, type GeneratedData, type GeneratedEntities, type GeneratedInputs, type GeneratedOutputs, type InsertArrResolver, type InsertResolver, type MutationReturnlessResult, type MutationsCore, type QueriesCore, type RelationResolverFactory, type SelectResolver, type SelectSingleResolver, type TableNamedRelations, type UpdateResolver, buildSchema, createRelationResolverFactory, extractFilters, extractOrderBy, extractRelationJoinColumns };
597
+ export { type AggregateResolver, type AnyDrizzleDB, type BuildSchemaConfig, type DeleteResolver, type ExtractRelations, type ExtractTableByName, type ExtractTableRelations, type ExtractTables, type GeneratedData, type GeneratedEntities, type GeneratedInputs, type GeneratedOutputs, GraphQLBigIntString, type InsertArrResolver, type InsertResolver, type MutationReturnlessResult, type MutationsCore, type QueriesCore, type RelationResolverFactory, type SchemaFeatures, type SelectResolver, type SelectSingleResolver, type TableNamedRelations, type UpdateResolver, type UpsertArgs, type UpsertArrResolver, type UpsertConflictArgs, type UpsertResolver, buildSchema, createRelationResolverFactory, defaultErrorMapper, drizzleExecutorKey, extractFilters, extractOrderBy, extractRelationJoinColumns };