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
import { createRepositoryFactory, zodAdapter } from '@kysera/repository'
const factory = createRepositoryFactory(db)
const userRepo = factory.create({
tableName: 'users',
mapRow: row => row,
schemas: {
create: zodAdapter(CreateUserSchema),
update: zodAdapter(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. Uses nativeAdapter (no validation) by default.
function createSimpleRepository<DB, TableName extends keyof DB & string, Entity, PK = number>(
executor: Executor<DB>,
tableName: TableName,
mapRow: (row: Selectable<DB[TableName]>) => Entity,
options?: {
primaryKey?: PrimaryKeyColumn
primaryKeyType?: PrimaryKeyTypeHint
dialect?: DialectConfig
}
): Repository<Entity, DB, PK>
Usage
const userRepo = createSimpleRepository(db, 'users', row => row, { primaryKey: 'id' })
Repository Configuration
RepositoryConfig
interface RepositoryConfig<Table, Entity> {
// Required
tableName: string
mapRow: (row: Selectable<Table>) => Entity
schemas: {
create: ValidationSchema // Required input validation
update?: ValidationSchema // Optional (uses create.partial() if omitted)
entity?: ValidationSchema<Entity> // Optional result validation
}
// Optional
schema?: string // PostgreSQL schema (e.g., 'auth', 'tenant_123')
primaryKey?: PrimaryKeyColumn // Default: 'id'
primaryKeyType?: PrimaryKeyTypeHint // Default: 'number'
dialect?: DialectConfig // Database dialect config
validationStrategy?: 'none' | 'strict' // Default: 'strict'
validateDbResults?: boolean // Default: NODE_ENV === 'development'
}
ValidationSchema
The schemas property uses the ValidationSchema interface, not raw Zod types. Wrap Zod schemas with zodAdapter(), Valibot schemas with valibotAdapter(), TypeBox schemas with typeboxAdapter(), or use nativeAdapter() for no validation. See the Validation API for details.
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
})
}
}