Skip to main content

Repository

Base class for creating database repositories with swappable database context.

Repository Class

import { Repository } from '@repo/db'

class OrderRepository extends Repository<typeof getDbContext> {
async getById(id: string) {
const { db, orders } = await this.getDbContext()
return db.query.orders.findFirst({ where: eq(orders.id, id) })
}

async create(values: typeof orders.$inferInsert) {
const { db, orders } = await this.getDbContext()
return db.insert(orders).values(values).returning()
}
}

const orderRepo = new OrderRepository(getDbContext)

The constructor takes a getDbContext function. All repository methods call this.getDbContext() to obtain the database instance and schema tables.

withDb

Creates a new repository instance bound to a different database context. The original instance is unchanged.

withDb(customGetDb?: Fn): this

Returns this if customGetDb is undefined (no-op). Otherwise returns a new instance of the same subclass with the overridden context.

Use Cases

Transactions -- bind a repository to a transaction:

await db.transaction(async (tx) => {
const txOrderRepo = orderRepo.withDb(() => ({ db: tx, ...schema }))
const txUserRepo = userRepo.withDb(() => ({ db: tx, ...schema }))

await txOrderRepo.create({ userId: '1', total: 100 })
await txUserRepo.updateBalance('1', -100)
})

Testing -- inject a test database:

const testRepo = orderRepo.withDb(() => ({ db: testDb, ...schema }))

Hooked database -- combine with wrapDbWithHooks:

const hookedContext = wrapDbWithHooks(getDbContext, hooks)
const hookedRepo = orderRepo.withDb(hookedContext)