Skip to main content

API Reference

Complete API documentation for all Kysera packages.

What is Kysera?

Kysera is a type-safe data access toolkit built on Kysely, not a traditional ORM. It provides composable patterns (Repository, DAL) and plugins for common database features, while maintaining Kysely's lightweight philosophy and full SQL control.

Unified Execution Layer (v0.7+)

Kysera now features a Unified Execution Layer powered by @kysera/executor. This foundation package enables plugins to work seamlessly with both Repository and DAL patterns through query interception. Learn more about the architecture.

Architecture Overview

Kysera follows a layered architecture with @kysera/executor as the foundation:

┌─────────────────────────────────────────────────────┐
│ Application Layer (Repository / DAL Patterns) │
│ - @kysera/repository: Repository with validation │
│ - @kysera/dal: Functional query composition │
├─────────────────────────────────────────────────────┤
│ Plugin Layer (Query Interceptors & Extensions) │
│ - @kysera/soft-delete, @kysera/rls, etc. │
├─────────────────────────────────────────────────────┤
│ Unified Execution Layer │
│ - @kysera/executor: Plugin-aware Kysely wrapper │
├─────────────────────────────────────────────────────┤
│ Kysely Query Builder (peer dependency) │
└─────────────────────────────────────────────────────┘

Key Concepts:

  • @kysera/executor wraps Kysely instances with plugin interception capabilities
  • Plugins work with BOTH Repository and DAL patterns through the executor
  • Query interceptors (e.g., soft-delete filters, RLS policies) apply automatically to all queries
  • Repository extensions (e.g., repo.softDelete(), repo.restore()) work only with Repository pattern

Package Index

Core Packages

PackageDescriptionBundle Size
@kysera/coreCore utilities - errors, pagination, logger, types~8 KB
@kysera/executorFoundation: Unified plugin execution layer~8 KB
@kysera/repositoryRepository pattern with validation~12 KB
@kysera/dalFunctional Data Access Layer~7 KB

Infrastructure Packages

PackageDescriptionBundle Size
@kysera/infraHealth checks, retry, circuit breaker, shutdown~12 KB
@kysera/debugQuery logging, profiling, SQL formatting~5 KB
@kysera/testingTesting utilities and factories~6 KB
@kysera/migrationsDatabase migration system~12 KB

Plugin Packages

PackageDescriptionBundle Size
@kysera/soft-deleteSoft delete functionality~4 KB
@kysera/timestampsAutomatic timestamp management~4 KB
@kysera/auditComprehensive audit logging~8 KB
@kysera/rlsRow-Level Security for multi-tenancy~10 KB

@kysera/core

Core utilities including error handling, pagination, and logging.

ModuleDescription
ErrorsMulti-database error parsing
PaginationOffset and cursor pagination
LoggerConfigurable logging interface
import { parseDatabaseError, paginate, consoleLogger } from '@kysera/core'

Package Dependencies

Understanding the dependency hierarchy helps you choose the right packages:

@kysera/executor (foundation - 0 dependencies)

├──> @kysera/dal (depends on executor)
│ └──> Used for: Functional queries with plugin support

├──> @kysera/repository (depends on executor + dal)
│ └──> Used for: Repository pattern with validation and plugin methods

└──> Plugin packages (use executor's Plugin interface)
├──> @kysera/soft-delete (query interceptor + repository extensions)
├──> @kysera/rls (query interceptor + repository extensions)
├──> @kysera/timestamps (repository extensions only)
└──> @kysera/audit (repository extensions only)

@kysera/core (standalone - 0 dependencies)
└──> Used by: All packages for errors, pagination, logging

Plugin Capabilities:

Plugin FeatureWorks with RepositoryWorks with DAL
Query Interceptors (interceptQuery)✅ Yes✅ Yes (via executor)
Repository Extensions (extendRepository)✅ Yes❌ No

Examples:

  • @kysera/soft-delete: Automatic filtering works in both; repo.softDelete() method only in Repository
  • @kysera/rls: RLS policies work in both; validation methods only in Repository
  • @kysera/timestamps: Repository only (no query interception needed)
  • @kysera/audit: Repository only (no query interception needed)

@kysera/executor

Foundation package - Unified plugin execution layer enabling plugins to work with both Repository and DAL patterns. Full Documentation →

import { createExecutor, isKyseraExecutor, getPlugins, getRawDb } from '@kysera/executor'

Core Concept: Wraps Kysely instances with plugin interception, allowing automatic query modification (filtering, policies) before execution while maintaining full type safety.

Key Features:

  • Zero overhead when no interceptor plugins are registered
  • Automatic plugin validation (conflicts, dependencies, circular deps)
  • Transaction propagation - plugins automatically work in transactions
  • Plugin priority and dependency resolution
  • getRawDb() for bypassing interceptors when needed

Quick Example:

import { createExecutor } from '@kysera/executor'
import { softDeletePlugin } from '@kysera/soft-delete'

const executor = await createExecutor(db, [softDeletePlugin()])

// Automatic soft-delete filtering
const users = await executor.selectFrom('users').selectAll().execute()

// Works in transactions
await executor.transaction().execute(async (trx) => {
// Plugins automatically applied
const user = await trx.selectFrom('users').where('id', '=', 1).executeTakeFirst()
})

@kysera/repository

Type-safe repository pattern implementation with full plugin support. Full Documentation →

ModuleDescription
FactoryRepository factory functions
ValidationValidation utilities
TypesType definitions
import { createRepositoryFactory, createORM, withPlugins } from '@kysera/repository'

Plugin Integration: Uses @kysera/executor internally, supporting both query interceptors (interceptQuery) and repository extensions (extendRepository).

@kysera/dal

Functional Data Access Layer for composable queries with query interceptor support via KyseraExecutor. Full Documentation →

import { createQuery, withTransaction, createContext, compose, parallel } from '@kysera/dal'

Plugin Integration: Works with @kysera/executor to support query interceptor plugins (soft-delete, RLS, etc.). Repository extension plugins are not available in DAL.

@kysera/infra

Infrastructure utilities for production applications.

import {
checkDatabaseHealth,
HealthMonitor,
withRetry,
CircuitBreaker,
registerShutdownHandlers
} from '@kysera/infra'

@kysera/debug

Debug and profiling utilities.

import { withDebug, QueryProfiler, formatSQL, highlightSQL } from '@kysera/debug'

@kysera/testing

Testing utilities for Kysera applications.

import {
testInTransaction,
createFactory,
cleanDatabase,
seedDatabase
} from '@kysera/testing'

@kysera/migrations

Database migration system.

import {
createMigration,
createMigrationRunner,
runMigrations,
getMigrationStatus
} from '@kysera/migrations'

Plugin Packages

@kysera/soft-delete

Mark records as deleted without removing them.

import { softDeletePlugin } from '@kysera/soft-delete'

const orm = await createORM(db, [
softDeletePlugin({ deletedAtColumn: 'deleted_at' })
])

// Methods added: softDelete, restore, hardDelete, findWithDeleted, etc.

@kysera/timestamps

Automatic timestamp management.

import { timestampsPlugin } from '@kysera/timestamps'

const orm = await createORM(db, [
timestampsPlugin() // Zero config!
])

// created_at and updated_at are set automatically

@kysera/audit

Comprehensive audit logging.

import { auditPlugin } from '@kysera/audit'

const orm = await createORM(db, [
auditPlugin({
getUserId: () => currentUser?.id,
captureOldValues: true,
captureNewValues: true
})
])

// Methods added: getAuditHistory, getAuditLog, restoreFromAudit

@kysera/rls

Row-Level Security for multi-tenant applications.

import { rlsPlugin, defineRLSSchema, filter, allow, rlsContext } from '@kysera/rls'

const rlsSchema = defineRLSSchema({
posts: {
policies: [
filter('read', ctx => ({ tenant_id: ctx.auth.tenantId })),
allow(['update', 'delete'], ctx => ctx.auth.userId === ctx.row?.author_id)
]
}
})

const orm = await createORM(db, [rlsPlugin({ schema: rlsSchema })])

// All queries automatically filtered by tenant
await rlsContext.runAsync({ auth: { userId: 1, tenantId: 'acme', roles: [] } }, async () => {
const posts = await postRepo.findAll() // Filtered by tenant_id = 'acme'
})

Quick Reference

Creating a Repository

import { createRepositoryFactory } from '@kysera/repository'
import { z } from 'zod'

const factory = createRepositoryFactory(db)

const userRepo = factory.create({
tableName: 'users' as const,
mapRow: (row) => ({
id: row.id,
email: row.email,
name: row.name,
createdAt: row.created_at
}),
schemas: {
create: z.object({
email: z.string().email(),
name: z.string().min(1)
})
}
})

Using Plugins (Unified Approach)

With the Unified Execution Layer, create an executor with plugins that work across both Repository and DAL:

import { createExecutor } from '@kysera/executor'
import { createORM } from '@kysera/repository'
import { softDeletePlugin } from '@kysera/soft-delete'
import { timestampsPlugin } from '@kysera/timestamps'
import { auditPlugin } from '@kysera/audit'
import { rlsPlugin } from '@kysera/rls'

// Create executor with query interceptor plugins
const executor = await createExecutor(db, [
rlsPlugin({ schema: rlsSchema }), // RLS policies (query interceptor)
softDeletePlugin() // Soft-delete filter (query interceptor)
])

// createORM creates a plugin container (repository manager), not a traditional ORM
// It gets both query interceptors + extension methods
const orm = await createORM(executor, [
timestampsPlugin(), // Repository extension only
auditPlugin({ // Repository extension only
getUserId: () => currentUser?.id
})
])

// DAL pattern: Gets query interceptors only
import { createQuery } from '@kysera/dal'
const getUsers = createQuery((ctx) =>
ctx.db.selectFrom('users').selectAll().execute()
)
// RLS and soft-delete filters automatically applied!
const users = await getUsers(executor)

Key Points:

  • Query interceptor plugins (soft-delete, RLS) → Add to executor
  • Repository extension plugins (timestamps, audit) → Add to createORM (plugin container)
  • Both patterns share the same query interceptors for consistent behavior
  • createORM is a plugin container/repository manager, not a traditional ORM

Error Handling

import { parseDatabaseError, UniqueConstraintError, ForeignKeyError } from '@kysera/core'

try {
await userRepo.create({ email: 'existing@example.com', name: 'Test' })
} catch (rawError) {
const error = parseDatabaseError(rawError, 'postgres')

if (error instanceof UniqueConstraintError) {
console.log(`Duplicate value in columns: ${error.columns.join(', ')}`)
}

if (error instanceof ForeignKeyError) {
console.log(`Foreign key violation: ${error.constraint}`)
}
}

Health Checks

import { checkDatabaseHealth, HealthMonitor } from '@kysera/infra'

// One-time check
const health = await checkDatabaseHealth(db)
console.log(health.status) // 'healthy' | 'degraded' | 'unhealthy'

// Continuous monitoring
const monitor = new HealthMonitor(db, { intervalMs: 30000 })
monitor.start((result) => {
if (result.status !== 'healthy') {
alerting.send('Database health issue', result)
}
})

Pagination

import { paginate, paginateCursor } from '@kysera/core'

// Offset pagination
const page = await paginate(
db.selectFrom('posts').selectAll(),
{ page: 1, limit: 20 }
)
// { items: [...], total: 100, page: 1, limit: 20, totalPages: 5 }

// Cursor pagination
const result = await paginateCursor(
db.selectFrom('posts').selectAll(),
{
orderBy: [{ column: 'created_at', direction: 'desc' }],
limit: 20,
cursor: previousCursor
}
)
// { items: [...], nextCursor: {...}, hasMore: true }

Transactions with Plugins

Plugins automatically propagate through transactions:

import { createExecutor } from '@kysera/executor'
import { softDeletePlugin } from '@kysera/soft-delete'
import { createORM } from '@kysera/repository'
import { withTransaction, createQuery } from '@kysera/dal'

// Create executor with plugins
const executor = await createExecutor(db, [softDeletePlugin()])

// Repository pattern - plugins in transactions
const orm = await createORM(executor, [])
await orm.transaction(async (ctx) => {
const userRepo = orm.createRepository(createUserRepository)
const user = await userRepo.create({ email: 'new@example.com', name: 'New User' })
// Soft-delete filter applied within transaction
const activeUsers = await userRepo.findAll()
})

// DAL pattern - plugins in transactions
const getUsers = createQuery((ctx) =>
ctx.db.selectFrom('users').selectAll().execute()
)

await withTransaction(executor, async (ctx) => {
// Soft-delete filter automatically applied
const users = await getUsers(ctx)
// Both operations commit or roll back together
})

// Repository factory pattern
import { createRepositoriesFactory } from '@kysera/repository'

const createRepos = createRepositoriesFactory({
users: (executor) => createUserRepository(executor),
posts: (executor) => createPostRepository(executor)
})

await executor.transaction().execute(async (trx) => {
const repos = createRepos(trx)
// All queries inherit plugins from executor
const user = await repos.users.create({ email: 'new@example.com', name: 'New User' })
await repos.posts.create({ title: 'First Post', userId: user.id })
})

Version Compatibility

PackageVersionKyselyNode.jsBunDeno
@kysera/core0.6.1>=0.28.8>=20>=1.0>=1.40
@kysera/executor0.7.0>=0.28.8>=20>=1.0>=1.40
@kysera/repository0.7.0>=0.28.8>=20>=1.0>=1.40
@kysera/dal0.7.0>=0.28.8>=20>=1.0>=1.40
@kysera/infra0.6.1>=0.28.8>=20>=1.0>=1.40
@kysera/debug0.6.1>=0.28.8>=20>=1.0>=1.40
@kysera/testing0.6.1>=0.28.8>=20>=1.0>=1.40
@kysera/migrations0.6.1>=0.28.8>=20>=1.0>=1.40
@kysera/soft-delete0.7.0>=0.28.8>=20>=1.0>=1.40
@kysera/timestamps0.6.1>=0.28.8>=20>=1.0>=1.40
@kysera/audit0.6.1>=0.28.8>=20>=1.0>=1.40
@kysera/rls0.7.0>=0.28.8>=20>=1.0>=1.40

Database Support

FeaturePostgreSQLMySQLSQLite
RETURNING clauseNativeEmulatedNative
JSONB columnsNativeJSON typeTEXT
Partial indexesSupportedLimitedSupported
Row-level securityNative + AppApp-levelApp-level
Boolean typetrue/false1/01/0