Skip to main content

Repository Factory

Factory functions for creating repositories.

createRepositoryFactory

Create a typed repository factory for a database instance.

function createRepositoryFactory<DB>(executor: Executor<DB>): RepositoryFactory<DB>

interface RepositoryFactory<DB> {
executor: Executor<DB>
create<TableName extends keyof DB, Entity, PK = number>(
config: RepositoryConfig<DB[TableName], Entity, PK>
): Repository<Entity, DB, PK>
}

Usage

const factory = createRepositoryFactory(db)

const userRepo = factory.create({
tableName: 'users',
mapRow: (row) => row,
schemas: {
create: CreateUserSchema,
update: UpdateUserSchema
}
})

createRepositoriesFactory

Create a factory that produces multiple repositories.

function createRepositoriesFactory<DB, Repos extends Record<string, any>>(
factories: RepositoryFactoryMap<DB, Repos>
): (executor: Executor<DB>) => Repos

type RepositoryFactoryMap<DB, Repos> = {
[K in keyof Repos]: (executor: Executor<DB>) => Repos[K]
}

Usage

// Define factory
const createRepos = createRepositoriesFactory({
users: createUserRepository,
posts: createPostRepository,
comments: createCommentRepository
})

// Use with database
const repos = createRepos(db)
const user = await repos.users.findById(1)

// Use in transaction
await db.transaction().execute(async (trx) => {
const repos = createRepos(trx)
await repos.users.create({ ... })
await repos.posts.create({ ... })
})

createSimpleRepository

Create a basic repository without factory pattern.

function createSimpleRepository<DB, TableName extends keyof DB, Entity, PK = number>(
executor: Executor<DB>,
tableName: TableName,
mapRow: (row: Selectable<DB[TableName]>) => Entity,
options?: {
primaryKey?: string | string[]
primaryKeyType?: 'number' | 'string' | 'uuid'
}
): SimpleRepository<Entity, PK>

Usage

const userRepo = createSimpleRepository(
db,
'users',
(row) => row,
{ primaryKey: 'id' }
)

Repository Configuration

RepositoryConfig

interface RepositoryConfig<Table, Entity, PK = number> {
// Required
tableName: string
mapRow: (row: Selectable<Table>) => Entity
schemas: {
create: z.ZodType
update?: z.ZodType
entity?: z.ZodType<Entity>
}

// Optional
primaryKey?: string | string[] // Default: 'id'
primaryKeyType?: 'number' | 'string' | 'uuid'
validationStrategy?: 'none' | 'strict' // Default: 'strict'
// Output validation controlled via KYSERA_VALIDATION_MODE or NODE_ENV
}

Row Mapping

interface UserRow {
id: Generated<number>
email: string
first_name: string
last_name: string
created_at: Generated<Date>
}

interface User {
id: number
email: string
fullName: string
createdAt: Date
}

const userRepo = factory.create({
tableName: 'users',
mapRow: (row): User => ({
id: row.id,
email: row.email,
fullName: `${row.first_name} ${row.last_name}`,
createdAt: row.created_at
}),
schemas: { create: CreateUserSchema }
})

Primary Key Configuration

// Numeric ID (default)
{ primaryKey: 'id' }

// UUID
{
primaryKey: 'uuid',
primaryKeyType: 'uuid'
}

// Custom column name
{
primaryKey: 'account_number',
primaryKeyType: 'string'
}

// Composite key
{
primaryKey: ['tenant_id', 'user_id']
}

Validation Configuration

Validation is controlled via environment variables:

# Always validate both inputs and outputs (development)
KYSERA_VALIDATION_MODE=always

# Validate inputs only (production)
KYSERA_VALIDATION_MODE=production

# Never validate outputs (testing/performance)
KYSERA_VALIDATION_MODE=never

# Default: based on NODE_ENV
KYSERA_VALIDATION_MODE=development

See the Validation API for more details.

Best Practices

1. Define Repository Functions

// user.repository.ts
export function createUserRepository(executor: Executor<Database>) {
const factory = createRepositoryFactory(executor)

return factory.create({
tableName: 'users' as const,
mapRow: mapUserRow,
schemas: {
create: CreateUserSchema,
update: UpdateUserSchema
}
})
}

2. Create Bundle Factory

// repositories.ts
export const createRepositories = createRepositoriesFactory({
users: createUserRepository,
posts: createPostRepository,
comments: createCommentRepository
})

export type Repositories = ReturnType<typeof createRepositories>

3. Use in Services

class UserService {
constructor(private repos = createRepositories(db)) {}

async createUserWithProfile(data: CreateUserInput) {
return this.repos.users.transaction(async (trx) => {
const repos = createRepositories(trx)
const user = await repos.users.create(data)
await repos.profiles.create({ userId: user.id })
return user
})
}
}