Skip to content

kysera-dev/kysera

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

132 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kysera

Kysera

Type-safe data access toolkit for TypeScript, built on Kysely

npm version MIT License TypeScript strict Cross-runtime Databases

Repository pattern • Functional DAL • Plugin ecosystem • Not an ORM

DocumentationGetting StartedAPI ReferenceExamples


Why Kysera?

Kysera extends Kysely with production-ready patterns — without hiding the query builder or inventing a new query language. You get repositories, plugins, security, and infrastructure while keeping full control over your SQL.

  • Zero magic — explicit, traceable, debuggable
  • Zero runtime deps in core packages
  • Full type safety — strict TypeScript, no any
  • Plugin architecture — compose behavior across Repository and DAL patterns
  • Cross-runtime — Node.js, Bun, Deno
  • 4,600+ tests at 95%+ coverage

Install

npm install kysely @kysera/core @kysera/executor @kysera/repository

Quick Start

import { Kysely, PostgresDialect } from 'kysely'
import { Pool } from 'pg'
import { createExecutor } from '@kysera/executor'
import { createORM } from '@kysera/repository'
import { softDeletePlugin } from '@kysera/soft-delete'
import { timestampsPlugin } from '@kysera/timestamps'

// Define your schema
interface Database {
  users: {
    id: Generated<number>
    email: string
    name: string
    created_at: Generated<Date>
    updated_at: Generated<Date>
    deleted_at: Date | null
  }
}

// Connect
const db = new Kysely<Database>({
  dialect: new PostgresDialect({ pool: new Pool({ connectionString: process.env.DATABASE_URL }) })
})

// Create executor with plugins — they apply automatically to all queries
const executor = await createExecutor(db, [
  softDeletePlugin(),
  timestampsPlugin()
])

// Create ORM and repository
const orm = await createORM(executor, [])
const userRepo = orm.createRepository({
  tableName: 'users',
  schemas: { entity: UserSchema, create: CreateUserSchema }
})

// Query — plugins apply transparently
const users = await userRepo.findAll()           // filters deleted, includes timestamps
await userRepo.create({ email: 'a@b.com', name: 'Alice' }) // auto-sets created_at/updated_at
await userRepo.softDelete(1)                      // sets deleted_at instead of DELETE

Packages

Core

Package Description
@kysera/core Errors, pagination, types, logger
@kysera/executor Unified Execution Layer — plugin-aware Kysely wrapper (zero deps)
@kysera/repository Repository pattern with Zod validation and MongoDB-style queries
@kysera/dal Functional Data Access Layer — query functions, context, composition
@kysera/dialects Dialect-specific utilities and error parsing

Plugins

Package Description
@kysera/soft-delete Soft delete with automatic query filtering
@kysera/timestamps Auto created_at / updated_at management
@kysera/audit Audit logging with restore support
@kysera/rls Row-Level Security — declarative policies, native PostgreSQL RLS

Infrastructure

Package Description
@kysera/infra Health checks, retry, circuit breaker, graceful shutdown
@kysera/debug Query logging, profiling, SQL formatting
@kysera/testing Transaction isolation, factories, seeding
@kysera/migrations Migration system with dry-run and rollback

CLI

Package Description
@kysera/cli Migrations, codegen, health monitoring, schema management

Features

Unified Execution Layer

The executor is the foundation — plugins defined once work across Repository and DAL patterns:

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

const executor = await createExecutor(db, [
  softDeletePlugin(),
  rlsPlugin({ schema: rlsSchema })
])

// Direct query — plugins apply automatically
const users = await executor.selectFrom('users').selectAll().execute()

MongoDB-Style Query Operators

Repository find() supports expressive filtering with type-safe operators:

const users = await userRepo.find({
  where: {
    age: { $gte: 18, $lte: 65 },
    status: { $in: ['active', 'pending'] },
    email: { $contains: '@company.com' },
    name: { $startsWith: 'A' },
    score: { $between: [80, 100] }
  },
  sort: [{ column: 'created_at', direction: 'desc' }],
  limit: 20
})

Operators: $eq $ne $gt $gte $lt $lte $in $nin $like $ilike $contains $startsWith $endsWith $isNull $isNotNull $between $or $and

Functional DAL

For complex reads, use composable query functions:

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

const getUserById = createQuery((ctx, id: number) =>
  ctx.db.selectFrom('users').where('id', '=', id).executeTakeFirst()
)

const getUserPosts = createQuery((ctx, userId: number) =>
  ctx.db.selectFrom('posts').where('user_id', '=', userId).execute()
)

// Compose and execute
const ctx = createContext(executor)
const user = await getUserById(ctx, 1)

// Parallel queries
const [user, posts] = await parallel(ctx, getUserById, getUserPosts)(ctx, 1, 1)

// Transactions preserve plugins
await withTransaction(ctx, async (txCtx) => {
  const user = await getUserById(txCtx, 1)   // plugins still active
})

Row-Level Security

Declarative policies that transform queries at the executor level:

import { createRLSSchema, allow, filter } from '@kysera/rls'

const rlsSchema = createRLSSchema({
  users: {
    policies: [
      allow('select').to('admin'),
      filter('select').to('user').where((ctx) => ({
        column: 'tenant_id',
        op: '=',
        value: ctx.tenantId
      }))
    ]
  }
})

const executor = await createExecutor(db, [
  rlsPlugin({ schema: rlsSchema })
])

Error Handling

Structured, dialect-aware error parsing:

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

try {
  await userRepo.create(data)
} catch (error) {
  const dbError = parseDatabaseError(error, 'postgres')
  if (dbError instanceof UniqueConstraintError) {
    console.log(dbError.constraint, dbError.columns)
  }
}

Pagination

Offset and cursor-based pagination out of the box:

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

// Offset
const page = await paginate(query, { page: 1, limit: 20 })
// → { data, total, page, limit, totalPages, hasNext, hasPrev }

// Cursor (for infinite scroll / real-time feeds)
const feed = await paginateCursor(query, {
  orderBy: [{ column: 'created_at', direction: 'desc' }],
  limit: 20,
  after: lastCursor
})
// → { data, cursor, hasMore }

Infrastructure

Production-ready resilience and observability:

import { createHealthCheck } from '@kysera/infra/health'
import { withRetry, createCircuitBreaker } from '@kysera/infra/resilience'
import { gracefulShutdown } from '@kysera/infra/shutdown'

// Health checks
const health = createHealthCheck(db, { interval: 30_000 })

// Retry with exponential backoff
const result = await withRetry(() => fetchData(), {
  maxRetries: 3,
  backoff: 'exponential'
})

// Circuit breaker
const breaker = createCircuitBreaker({ threshold: 5, timeout: 30_000 })

// Graceful shutdown
gracefulShutdown({ db, onShutdown: () => console.log('bye') })

CLI

npm install -g @kysera/cli

# Initialize project
kysera init

# Migrations
kysera migrate create add-users-table
kysera migrate up
kysera migrate status
kysera migrate rollback --steps 1

# Code generation
kysera generate model User
kysera generate repository User --with-tests
kysera generate crud User --with-api

# Database tools
kysera db seed
kysera health check
kysera debug explain "SELECT * FROM users"

Architecture

@kysera/executor (0 deps) ← Foundation
    │
    ├── @kysera/dal ──────── Functional queries
    │
    └── @kysera/repository ─ Repository pattern
            │
            ├── @kysera/soft-delete
            ├── @kysera/timestamps
            ├── @kysera/audit
            └── @kysera/rls

@kysera/core (0 deps) ─── Shared errors, pagination, types
@kysera/dialects ───────── PostgreSQL, MySQL, SQLite, MSSQL
@kysera/infra ──────────── Health, retry, circuit breaker
@kysera/debug ──────────── Logging, profiling
@kysera/testing ────────── Factories, seeding, isolation
@kysera/migrations ─────── Schema versioning

Examples

Working examples in examples/:

  • blog-app — Posts, comments, tags with soft-delete and audit
  • e-commerce — Products, orders, payments with transactions
  • multi-tenant-saas — Tenant isolation with RLS and schema separation

Development

pnpm install          # Install dependencies
pnpm build            # Build all packages
pnpm test             # Run tests (4,600+)
pnpm test:coverage    # With coverage report
pnpm test:multi-db    # PostgreSQL + MySQL + SQLite
pnpm dev              # Watch mode
pnpm typecheck        # Type checking
pnpm lint             # ESLint

Requirements: Node.js >=20, pnpm >=10, TypeScript ^5.9

Documentation

Full docs at kysera.dev

License

MIT

Packages

 
 
 

Contributors

Languages