@vritti/api-sdk 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -151,11 +151,17 @@ interface DatabaseModuleOptions {
151
151
  */
152
152
  primaryDb: PrimaryDbConfig;
153
153
  /**
154
- * Drizzle schema object containing all tables and relations
154
+ * Drizzle schema object containing all tables
155
155
  * Import your schema from db/schema/index.ts and pass it here
156
156
  * @example import * as schema from '@/db/schema'
157
157
  */
158
158
  drizzleSchema: RegisteredSchema;
159
+ /**
160
+ * Drizzle relations object from defineRelations()
161
+ * Required for relational queries (db.query.*.findFirst/findMany)
162
+ * @example import { relations } from '@/db/schema'
163
+ */
164
+ drizzleRelations?: Record<string, any>;
159
165
  /**
160
166
  * Connection cache TTL in milliseconds
161
167
  * Idle connections will be closed after this period
@@ -556,22 +562,46 @@ declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
556
562
  }
557
563
 
558
564
  /**
559
- * Type-safe wrapper for Drizzle's RelationalQueryBuilder.
565
+ * Drizzle ORM v2 object-based where filter type.
566
+ * Supports simple equality, operators, AND/OR/NOT, and RAW SQL.
567
+ *
568
+ * @example
569
+ * ```typescript
570
+ * // Simple equality
571
+ * { email: 'user@example.com' }
572
+ *
573
+ * // With operators
574
+ * { age: { gt: 18, lt: 65 } }
575
+ *
576
+ * // AND/OR combinations
577
+ * { AND: [{ status: 'ACTIVE' }, { age: { gte: 18 } }] }
578
+ *
579
+ * // RAW SQL expression
580
+ * { RAW: (table) => sql`${table.email} ILIKE '%@gmail.com'` }
581
+ * ```
582
+ */
583
+ type RelationsWhereFilter = Record<string, any>;
584
+ /**
585
+ * Type-safe wrapper for Drizzle's RelationalQueryBuilder (v2 API).
560
586
  * This interface matches the method signatures of RelationalQueryBuilder
561
587
  * but properly binds the TSelect generic for type safety.
562
588
  *
563
589
  * We use this instead of RelationalQueryBuilder directly because
564
590
  * TypeScript cannot infer TSelect from the generic base repository context.
591
+ *
592
+ * @remarks
593
+ * Drizzle ORM v2 uses object-based `where` filters instead of SQL expressions.
594
+ * See: https://orm.drizzle.team/docs/relations-v1-v2
565
595
  */
566
596
  interface TypedRelationalQueryBuilder<TSelect> {
567
597
  findFirst(config?: {
568
- where?: SQL;
598
+ where?: RelationsWhereFilter;
569
599
  with?: Record<string, unknown>;
570
600
  columns?: Record<string, boolean>;
571
601
  }): Promise<TSelect | undefined>;
572
602
  findMany(config?: {
573
- where?: SQL;
574
- orderBy?: SQL;
603
+ where?: RelationsWhereFilter;
604
+ orderBy?: Record<string, 'asc' | 'desc'>;
575
605
  limit?: number;
576
606
  offset?: number;
577
607
  with?: Record<string, unknown>;
@@ -610,17 +640,17 @@ interface TypedRelationalQueryBuilder<TSelect> {
610
640
  * super(database, users);
611
641
  * }
612
642
  *
613
- * // Use Prisma-like relational query syntax (recommended)
643
+ * // Use Drizzle v2 object-based where syntax (recommended)
614
644
  * async findByEmail(email: string): Promise<User | undefined> {
615
645
  * return this.model.findFirst({
616
- * where: eq(users.email, email),
646
+ * where: { email },
617
647
  * });
618
648
  * }
619
649
  *
620
- * // Use Prisma-like with relations
650
+ * // With relations
621
651
  * async findWithRelations(id: string): Promise<User | undefined> {
622
652
  * return this.model.findFirst({
623
- * where: eq(users.id, id),
653
+ * where: { id },
624
654
  * with: { posts: true, profile: true }
625
655
  * });
626
656
  * }
@@ -643,15 +673,15 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
643
673
  */
644
674
  protected get db(): TypedDrizzleClient;
645
675
  /**
646
- * Model query API for THIS repository's table (Prisma-like syntax)
676
+ * Model query API for THIS repository's table (Drizzle v2 relational queries)
647
677
  * Scoped to only the table this repository manages.
648
678
  * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
649
679
  *
650
680
  * @example
651
681
  * ```typescript
652
- * // Use relational queries with type safety
682
+ * // Use relational queries with v2 object-based where syntax
653
683
  * const user = await this.model.findFirst({
654
- * where: eq(users.id, id),
684
+ * where: { id },
655
685
  * with: { posts: true, profile: true }
656
686
  * });
657
687
  * ```
@@ -701,43 +731,60 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
701
731
  */
702
732
  findById(id: string): Promise<TSelect | undefined>;
703
733
  /**
704
- * Find a single record with custom where clause
734
+ * Find a single record with custom where clause (Drizzle v2 object-based syntax)
705
735
  *
706
- * @param where - SQL condition
736
+ * @param where - Object-based filter condition
707
737
  * @returns Promise resolving to the record or undefined if not found
708
738
  *
709
739
  * @example
710
740
  * ```typescript
711
- * import { eq } from 'drizzle-orm';
712
- * const user = await userRepository.findOne(eq(users.email, 'user@example.com'));
741
+ * // Simple equality
742
+ * const user = await userRepository.findOne({ email: 'user@example.com' });
743
+ *
744
+ * // With operators
745
+ * const user = await userRepository.findOne({ age: { gte: 18 } });
746
+ *
747
+ * // Multiple conditions (AND)
748
+ * const user = await userRepository.findOne({
749
+ * email: 'user@example.com',
750
+ * status: 'ACTIVE'
751
+ * });
713
752
  * ```
714
753
  */
715
- findOne(where: SQL): Promise<TSelect | undefined>;
754
+ findOne(where: RelationsWhereFilter): Promise<TSelect | undefined>;
716
755
  /**
717
- * Find multiple records
756
+ * Find multiple records (Drizzle v2 object-based syntax)
718
757
  *
719
758
  * @param options - Query options (where, orderBy, limit, offset)
720
759
  * @returns Promise resolving to an array of records
721
760
  *
722
761
  * @example
723
762
  * ```typescript
724
- * import { eq, desc } from 'drizzle-orm';
725
- *
726
763
  * // Find all users
727
764
  * const users = await userRepository.findMany();
728
765
  *
729
- * // Find with filtering and pagination
766
+ * // Find with filtering and pagination (v2 object syntax)
730
767
  * const users = await userRepository.findMany({
731
- * where: eq(users.accountStatus, 'ACTIVE'),
732
- * orderBy: desc(users.createdAt),
768
+ * where: { accountStatus: 'ACTIVE' },
769
+ * orderBy: { createdAt: 'desc' },
733
770
  * limit: 10,
734
771
  * offset: 0
735
772
  * });
773
+ *
774
+ * // Multiple conditions
775
+ * const users = await userRepository.findMany({
776
+ * where: {
777
+ * AND: [
778
+ * { status: 'ACTIVE' },
779
+ * { age: { gte: 18 } }
780
+ * ]
781
+ * }
782
+ * });
736
783
  * ```
737
784
  */
738
785
  findMany(options?: {
739
- where?: SQL;
740
- orderBy?: SQL;
786
+ where?: RelationsWhereFilter;
787
+ orderBy?: Record<string, 'asc' | 'desc'>;
741
788
  limit?: number;
742
789
  offset?: number;
743
790
  }): Promise<TSelect[]>;
package/dist/index.d.ts CHANGED
@@ -151,11 +151,17 @@ interface DatabaseModuleOptions {
151
151
  */
152
152
  primaryDb: PrimaryDbConfig;
153
153
  /**
154
- * Drizzle schema object containing all tables and relations
154
+ * Drizzle schema object containing all tables
155
155
  * Import your schema from db/schema/index.ts and pass it here
156
156
  * @example import * as schema from '@/db/schema'
157
157
  */
158
158
  drizzleSchema: RegisteredSchema;
159
+ /**
160
+ * Drizzle relations object from defineRelations()
161
+ * Required for relational queries (db.query.*.findFirst/findMany)
162
+ * @example import { relations } from '@/db/schema'
163
+ */
164
+ drizzleRelations?: Record<string, any>;
159
165
  /**
160
166
  * Connection cache TTL in milliseconds
161
167
  * Idle connections will be closed after this period
@@ -556,22 +562,46 @@ declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
556
562
  }
557
563
 
558
564
  /**
559
- * Type-safe wrapper for Drizzle's RelationalQueryBuilder.
565
+ * Drizzle ORM v2 object-based where filter type.
566
+ * Supports simple equality, operators, AND/OR/NOT, and RAW SQL.
567
+ *
568
+ * @example
569
+ * ```typescript
570
+ * // Simple equality
571
+ * { email: 'user@example.com' }
572
+ *
573
+ * // With operators
574
+ * { age: { gt: 18, lt: 65 } }
575
+ *
576
+ * // AND/OR combinations
577
+ * { AND: [{ status: 'ACTIVE' }, { age: { gte: 18 } }] }
578
+ *
579
+ * // RAW SQL expression
580
+ * { RAW: (table) => sql`${table.email} ILIKE '%@gmail.com'` }
581
+ * ```
582
+ */
583
+ type RelationsWhereFilter = Record<string, any>;
584
+ /**
585
+ * Type-safe wrapper for Drizzle's RelationalQueryBuilder (v2 API).
560
586
  * This interface matches the method signatures of RelationalQueryBuilder
561
587
  * but properly binds the TSelect generic for type safety.
562
588
  *
563
589
  * We use this instead of RelationalQueryBuilder directly because
564
590
  * TypeScript cannot infer TSelect from the generic base repository context.
591
+ *
592
+ * @remarks
593
+ * Drizzle ORM v2 uses object-based `where` filters instead of SQL expressions.
594
+ * See: https://orm.drizzle.team/docs/relations-v1-v2
565
595
  */
566
596
  interface TypedRelationalQueryBuilder<TSelect> {
567
597
  findFirst(config?: {
568
- where?: SQL;
598
+ where?: RelationsWhereFilter;
569
599
  with?: Record<string, unknown>;
570
600
  columns?: Record<string, boolean>;
571
601
  }): Promise<TSelect | undefined>;
572
602
  findMany(config?: {
573
- where?: SQL;
574
- orderBy?: SQL;
603
+ where?: RelationsWhereFilter;
604
+ orderBy?: Record<string, 'asc' | 'desc'>;
575
605
  limit?: number;
576
606
  offset?: number;
577
607
  with?: Record<string, unknown>;
@@ -610,17 +640,17 @@ interface TypedRelationalQueryBuilder<TSelect> {
610
640
  * super(database, users);
611
641
  * }
612
642
  *
613
- * // Use Prisma-like relational query syntax (recommended)
643
+ * // Use Drizzle v2 object-based where syntax (recommended)
614
644
  * async findByEmail(email: string): Promise<User | undefined> {
615
645
  * return this.model.findFirst({
616
- * where: eq(users.email, email),
646
+ * where: { email },
617
647
  * });
618
648
  * }
619
649
  *
620
- * // Use Prisma-like with relations
650
+ * // With relations
621
651
  * async findWithRelations(id: string): Promise<User | undefined> {
622
652
  * return this.model.findFirst({
623
- * where: eq(users.id, id),
653
+ * where: { id },
624
654
  * with: { posts: true, profile: true }
625
655
  * });
626
656
  * }
@@ -643,15 +673,15 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
643
673
  */
644
674
  protected get db(): TypedDrizzleClient;
645
675
  /**
646
- * Model query API for THIS repository's table (Prisma-like syntax)
676
+ * Model query API for THIS repository's table (Drizzle v2 relational queries)
647
677
  * Scoped to only the table this repository manages.
648
678
  * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
649
679
  *
650
680
  * @example
651
681
  * ```typescript
652
- * // Use relational queries with type safety
682
+ * // Use relational queries with v2 object-based where syntax
653
683
  * const user = await this.model.findFirst({
654
- * where: eq(users.id, id),
684
+ * where: { id },
655
685
  * with: { posts: true, profile: true }
656
686
  * });
657
687
  * ```
@@ -701,43 +731,60 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
701
731
  */
702
732
  findById(id: string): Promise<TSelect | undefined>;
703
733
  /**
704
- * Find a single record with custom where clause
734
+ * Find a single record with custom where clause (Drizzle v2 object-based syntax)
705
735
  *
706
- * @param where - SQL condition
736
+ * @param where - Object-based filter condition
707
737
  * @returns Promise resolving to the record or undefined if not found
708
738
  *
709
739
  * @example
710
740
  * ```typescript
711
- * import { eq } from 'drizzle-orm';
712
- * const user = await userRepository.findOne(eq(users.email, 'user@example.com'));
741
+ * // Simple equality
742
+ * const user = await userRepository.findOne({ email: 'user@example.com' });
743
+ *
744
+ * // With operators
745
+ * const user = await userRepository.findOne({ age: { gte: 18 } });
746
+ *
747
+ * // Multiple conditions (AND)
748
+ * const user = await userRepository.findOne({
749
+ * email: 'user@example.com',
750
+ * status: 'ACTIVE'
751
+ * });
713
752
  * ```
714
753
  */
715
- findOne(where: SQL): Promise<TSelect | undefined>;
754
+ findOne(where: RelationsWhereFilter): Promise<TSelect | undefined>;
716
755
  /**
717
- * Find multiple records
756
+ * Find multiple records (Drizzle v2 object-based syntax)
718
757
  *
719
758
  * @param options - Query options (where, orderBy, limit, offset)
720
759
  * @returns Promise resolving to an array of records
721
760
  *
722
761
  * @example
723
762
  * ```typescript
724
- * import { eq, desc } from 'drizzle-orm';
725
- *
726
763
  * // Find all users
727
764
  * const users = await userRepository.findMany();
728
765
  *
729
- * // Find with filtering and pagination
766
+ * // Find with filtering and pagination (v2 object syntax)
730
767
  * const users = await userRepository.findMany({
731
- * where: eq(users.accountStatus, 'ACTIVE'),
732
- * orderBy: desc(users.createdAt),
768
+ * where: { accountStatus: 'ACTIVE' },
769
+ * orderBy: { createdAt: 'desc' },
733
770
  * limit: 10,
734
771
  * offset: 0
735
772
  * });
773
+ *
774
+ * // Multiple conditions
775
+ * const users = await userRepository.findMany({
776
+ * where: {
777
+ * AND: [
778
+ * { status: 'ACTIVE' },
779
+ * { age: { gte: 18 } }
780
+ * ]
781
+ * }
782
+ * });
736
783
  * ```
737
784
  */
738
785
  findMany(options?: {
739
- where?: SQL;
740
- orderBy?: SQL;
786
+ where?: RelationsWhereFilter;
787
+ orderBy?: Record<string, 'asc' | 'desc'>;
741
788
  limit?: number;
742
789
  offset?: number;
743
790
  }): Promise<TSelect[]>;
package/dist/index.js CHANGED
@@ -201,10 +201,14 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
201
201
  connectionString: databaseUrl,
202
202
  max: this.options.maxConnections || 10
203
203
  });
204
+ this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
205
+ this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
204
206
  this.db = drizzle({
205
207
  client: this.pool,
206
- schema: this.options.drizzleSchema
208
+ schema: this.options.drizzleSchema,
209
+ relations: this.options.drizzleRelations
207
210
  });
211
+ this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
208
212
  await this.pool.query("SELECT 1");
209
213
  this.logger.log("Connected to primary database (tenant registry)");
210
214
  } catch (error) {
@@ -1247,21 +1251,28 @@ var PrimaryBaseRepository = class {
1247
1251
  return this.database.drizzleClient;
1248
1252
  }
1249
1253
  /**
1250
- * Model query API for THIS repository's table (Prisma-like syntax)
1254
+ * Model query API for THIS repository's table (Drizzle v2 relational queries)
1251
1255
  * Scoped to only the table this repository manages.
1252
1256
  * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
1253
1257
  *
1254
1258
  * @example
1255
1259
  * ```typescript
1256
- * // Use relational queries with type safety
1260
+ * // Use relational queries with v2 object-based where syntax
1257
1261
  * const user = await this.model.findFirst({
1258
- * where: eq(users.id, id),
1262
+ * where: { id },
1259
1263
  * with: { posts: true, profile: true }
1260
1264
  * });
1261
1265
  * ```
1262
1266
  */
1263
1267
  get model() {
1264
- return this.database.drizzleClient.query[this.tableName];
1268
+ const query = this.database.drizzleClient.query;
1269
+ const queryKeys = Object.keys(query || {});
1270
+ this.logger.debug(`Looking for '${this.tableName}' in query keys: [${queryKeys.join(", ")}]`);
1271
+ const model = query[this.tableName];
1272
+ if (!model) {
1273
+ this.logger.error(`Table '${this.tableName}' not found in query object. Available: [${queryKeys.join(", ")}]`);
1274
+ }
1275
+ return model;
1265
1276
  }
1266
1277
  /**
1267
1278
  * Create a new repository instance
@@ -1284,6 +1295,7 @@ var PrimaryBaseRepository = class {
1284
1295
  this.tableName = getTableName(table);
1285
1296
  this.logger = new Logger6(this.constructor.name);
1286
1297
  this.logger.debug(`Initialized ${this.constructor.name}`);
1298
+ this.logger.debug(`Table name from getTableName: '${this.tableName}'`);
1287
1299
  }
1288
1300
  /**
1289
1301
  * Create a new record
@@ -1317,21 +1329,31 @@ var PrimaryBaseRepository = class {
1317
1329
  */
1318
1330
  async findById(id) {
1319
1331
  this.logger.debug(`Finding record by ID: ${id}`);
1320
- const idColumn = this.table.id;
1321
1332
  return this.model.findFirst({
1322
- where: eq2(idColumn, id)
1333
+ where: {
1334
+ id
1335
+ }
1323
1336
  });
1324
1337
  }
1325
1338
  /**
1326
- * Find a single record with custom where clause
1339
+ * Find a single record with custom where clause (Drizzle v2 object-based syntax)
1327
1340
  *
1328
- * @param where - SQL condition
1341
+ * @param where - Object-based filter condition
1329
1342
  * @returns Promise resolving to the record or undefined if not found
1330
1343
  *
1331
1344
  * @example
1332
1345
  * ```typescript
1333
- * import { eq } from 'drizzle-orm';
1334
- * const user = await userRepository.findOne(eq(users.email, 'user@example.com'));
1346
+ * // Simple equality
1347
+ * const user = await userRepository.findOne({ email: 'user@example.com' });
1348
+ *
1349
+ * // With operators
1350
+ * const user = await userRepository.findOne({ age: { gte: 18 } });
1351
+ *
1352
+ * // Multiple conditions (AND)
1353
+ * const user = await userRepository.findOne({
1354
+ * email: 'user@example.com',
1355
+ * status: 'ACTIVE'
1356
+ * });
1335
1357
  * ```
1336
1358
  */
1337
1359
  async findOne(where) {
@@ -1341,25 +1363,33 @@ var PrimaryBaseRepository = class {
1341
1363
  });
1342
1364
  }
1343
1365
  /**
1344
- * Find multiple records
1366
+ * Find multiple records (Drizzle v2 object-based syntax)
1345
1367
  *
1346
1368
  * @param options - Query options (where, orderBy, limit, offset)
1347
1369
  * @returns Promise resolving to an array of records
1348
1370
  *
1349
1371
  * @example
1350
1372
  * ```typescript
1351
- * import { eq, desc } from 'drizzle-orm';
1352
- *
1353
1373
  * // Find all users
1354
1374
  * const users = await userRepository.findMany();
1355
1375
  *
1356
- * // Find with filtering and pagination
1376
+ * // Find with filtering and pagination (v2 object syntax)
1357
1377
  * const users = await userRepository.findMany({
1358
- * where: eq(users.accountStatus, 'ACTIVE'),
1359
- * orderBy: desc(users.createdAt),
1378
+ * where: { accountStatus: 'ACTIVE' },
1379
+ * orderBy: { createdAt: 'desc' },
1360
1380
  * limit: 10,
1361
1381
  * offset: 0
1362
1382
  * });
1383
+ *
1384
+ * // Multiple conditions
1385
+ * const users = await userRepository.findMany({
1386
+ * where: {
1387
+ * AND: [
1388
+ * { status: 'ACTIVE' },
1389
+ * { age: { gte: 18 } }
1390
+ * ]
1391
+ * }
1392
+ * });
1363
1393
  * ```
1364
1394
  */
1365
1395
  async findMany(options) {