Generic base repository abstract class

So, I’m trying to create a base repository class to extend based on my schema definitions, thing is I’m having trouble getting the types to work, here is my example
import type { InferSelectModel, SQL } from 'drizzle-orm';
import type { AnyPgTable } from 'drizzle-orm/pg-core';

import type { DB, DBTransaction } from './client';

export interface RepositoryOptions {
db?: DB;
tx?: DBTransaction;
}

/**
* A generic base repository class for Drizzle ORM
*
* Note: This implementation uses type assertions in some places to work
* around limitations in TypeScript's ability to fully type the Drizzle ORM.
* The public API is fully typed, but internal implementations may use 'any'.
*/
export class BaseRepository<
TTable extends AnyPgTable,
TSelect = InferSelectModel<TTable>,
> {
protected table: TTable;
protected db: DB | DBTransaction;
constructor(table: TTable, db: DB | DBTransaction) {
this.table = table;
this.db = db;
}

/**
* Find all records matching the given criteria
*/
async findAll(
options: {
limit?: number;
offset?: number;
orderBy?: SQL<unknown>;
where?: SQL<unknown>;
} = {},
): Promise<TSelect[]> {
const { limit, offset, orderBy, where } = options;

// Using any to avoid complex type issues with the query builder
let query = this.db.select().from(this.table).$dynamic();

if (where) {
query = query.where(where);
}

if (orderBy) {
query = query.orderBy(orderBy);
}

if (limit) {
query = query.limit(limit);
}

if (offset) {
query = query.offset(offset);
}

return query;
}
}
import type { InferSelectModel, SQL } from 'drizzle-orm';
import type { AnyPgTable } from 'drizzle-orm/pg-core';

import type { DB, DBTransaction } from './client';

export interface RepositoryOptions {
db?: DB;
tx?: DBTransaction;
}

/**
* A generic base repository class for Drizzle ORM
*
* Note: This implementation uses type assertions in some places to work
* around limitations in TypeScript's ability to fully type the Drizzle ORM.
* The public API is fully typed, but internal implementations may use 'any'.
*/
export class BaseRepository<
TTable extends AnyPgTable,
TSelect = InferSelectModel<TTable>,
> {
protected table: TTable;
protected db: DB | DBTransaction;
constructor(table: TTable, db: DB | DBTransaction) {
this.table = table;
this.db = db;
}

/**
* Find all records matching the given criteria
*/
async findAll(
options: {
limit?: number;
offset?: number;
orderBy?: SQL<unknown>;
where?: SQL<unknown>;
} = {},
): Promise<TSelect[]> {
const { limit, offset, orderBy, where } = options;

// Using any to avoid complex type issues with the query builder
let query = this.db.select().from(this.table).$dynamic();

if (where) {
query = query.where(where);
}

if (orderBy) {
query = query.orderBy(orderBy);
}

if (limit) {
query = query.limit(limit);
}

if (offset) {
query = query.offset(offset);
}

return query;
}
}
1 Reply
Monopolo11
Monopolo11OP5d ago
It looks like it should work but I get the following error in the this.table when creating the select
Argument of type 'TTable' is not assignable to parameter of type 'TableLikeHasEmptySelection<TTable> extends true ? DrizzleTypeError<"Cannot reference a data-modifying statement subquery if it doesn't contain a `returning` clause"> : TTable'.
Type 'PgTable<Required<{ schema: string | undefined; name: string; dialect: string; columns: Record<string, PgColumn<ColumnBaseConfig<ColumnDataType, string>, {}, {}>>; }>>' is not assignable to type 'TableLikeHasEmptySelection<TTable> extends true ? DrizzleTypeError<"Cannot reference a data-modifying statement subquery if it doesn't contain a `returning` clause"> : TTable'.
Argument of type 'TTable' is not assignable to parameter of type 'TableLikeHasEmptySelection<TTable> extends true ? DrizzleTypeError<"Cannot reference a data-modifying statement subquery if it doesn't contain a `returning` clause"> : TTable'.
Type 'PgTable<Required<{ schema: string | undefined; name: string; dialect: string; columns: Record<string, PgColumn<ColumnBaseConfig<ColumnDataType, string>, {}, {}>>; }>>' is not assignable to type 'TableLikeHasEmptySelection<TTable> extends true ? DrizzleTypeError<"Cannot reference a data-modifying statement subquery if it doesn't contain a `returning` clause"> : TTable'.
Also for reference, this is how I’m getting the DB and DBTransaction types
export const db = drizzle({
client: psClient,
schema,
casing: "snake_case",
});

export type DBTransaction = Parameters<Parameters<typeof db.transaction>[0]>[0];
export type DB = typeof db;
export const db = drizzle({
client: psClient,
schema,
casing: "snake_case",
});

export type DBTransaction = Parameters<Parameters<typeof db.transaction>[0]>[0];
export type DB = typeof db;
Any clues or examples on how to achieve this?

Did you find this page helpful?