DEV Community

LuxQuant
LuxQuant

Posted on

A Critical Analysis of Kysera: Why This Project Exists

Preface: On Honesty

This article is not marketing material. It does not attempt to convince you that Kysera is the best solution for all tasks. Instead, it presents a critical analysis: what problems led to the creation of Kysera, what place it occupies among existing solutions, and for which cases it is actually suitable.

If after reading this you decide that Kysera is not for you — that is a normal and possibly correct conclusion.


The Landscape of Problems

Developer Pain Points in Large Projects

Working with databases in production systems comes with recurring problems that are independent of the chosen tool:

Architectural Problems:

  • Transactions distributed across multiple services or layers
  • Soft delete that needs to be applied everywhere but is easy to forget
  • Audit trail requiring uniformity across all write operations
  • Row-Level Security for multi-tenant applications
  • Graceful shutdown and connection pool management

Operational Problems:

  • Database errors that are hard to understand without knowing PostgreSQL/MySQL codes
  • Health checks and connection state monitoring
  • Slow queries that need to be found and profiled
  • Testing with transaction isolation

Typing Problems:

  • Generated<T> fields that exist on read but not on write
  • Validation schemas for create/update that differ from the entity type
  • Error types that get lost in unknown

How Different Tools Solve These Problems

Prisma

Approach: Full abstraction. Prisma hides SQL behind a declarative schema and generated client.

Strengths:

  • Excellent DX for simple CRUD operations
  • Automatic migrations from schema
  • Built-in relations and nested queries
  • Type-safe client out of the box

Weaknesses:

  • Generated code is a black box
  • Complex queries require $queryRaw, losing typing
  • Limited control over SQL
  • Soft delete requires middleware, which is not obvious
  • Query engine as a separate process (or WASM) adds overhead
  • Migrations are tied to Prisma, difficult to exit

Philosophy: "We know better how to work with databases. Trust the abstraction."

TypeORM

Approach: Classic ORM with decorators, Active Record or Data Mapper.

Strengths:

  • Familiar patterns for developers from the Java/C# world
  • Support for multiple databases
  • Entity lifecycle management
  • Query Builder for complex cases

Weaknesses:

  • Decorators and metadata are runtime magic
  • Identity Map creates non-obvious state
  • Lazy loading is a source of N+1 problems
  • Typing is often lost in complex queries
  • Migrations require synchronization with entities

Philosophy: "ORM should manage the lifecycle of entities."

Drizzle

Approach: Type-safe schema-first query builder with ORM-like API.

Strengths:

  • Excellent typing from schema
  • SQL is close to the surface
  • Lightweight (no runtime engine)
  • Relational queries for simple JOINs

Weaknesses:

  • Schema in code, not in the database (requires synchronization)
  • Smaller ecosystem/plugins than Prisma
  • Relational queries are not full-fledged relations
  • Complex queries still need raw SQL

Philosophy: "Type safety matters more than abstraction. SQL is not the enemy."

Knex

Approach: Query builder without ORM pretensions.

Strengths:

  • Simple and clear API
  • Migrations as code
  • Support for multiple dialects
  • Minimal overhead

Weaknesses:

  • Weak typing (from the pre-TypeScript-first era)
  • No validation
  • No plugin system
  • Every project writes its own utilities

Philosophy: "A query builder is all you need."

Kysely

Approach: Type-safe query builder for TypeScript.

Strengths:

  • Full query typing
  • SQL as a first-class citizen
  • Zero runtime overhead
  • Predictable SQL output

Weaknesses:

  • Only a query builder — no data access patterns
  • No validation
  • No plugin system
  • No utilities for production (health, retry, pagination)
  • Every project implements soft delete differently

Philosophy: "Type-safe SQL. Nothing more, nothing less."


Kysera's Position

What Kysera Does NOT Attempt to Do

Before talking about what Kysera does, it's important to understand what it deliberately avoids:

  1. Does not replace Kysely. Kysera is a layer on top of Kysely, not an alternative. You can always use Kysely directly.

  2. Is not an ORM. No entity mapping, Unit of Work, Identity Map, lazy loading. These patterns are a source of complexity that Kysera rejects.

  3. Does not hide SQL. Queries are written in Kysely, SQL remains visible and predictable.

  4. Does not impose architecture. Repository or DAL — the choice is yours. You can use both.

  5. Does not solve all problems. Kysera focuses on specific pain points, not on trying to be "everything for everyone."

What Kysera Does

Kysera provides a toolkit for solving recurring problems in database work:

Plugin System with Query Interception

Instead of remembering about soft delete in every query:

// Without Kysera — easy to forget
const users = await db
  .selectFrom('users')
  .selectAll()
  .where('deleted_at', 'is', null)  // Forgot? You'll get deleted users
  .execute();

// With Kysera — applied automatically
const executor = await createExecutor(db, [softDeletePlugin()]);
const users = await executor
  .selectFrom('users')
  .selectAll()
  .execute();  // WHERE deleted_at IS NULL added automatically
Enter fullscreen mode Exit fullscreen mode

This works through Proxy-based interception — the same approach used by middleware in Express or interceptors in Axios.

Dual API: Repository and Functional DAL

Repository — for structured CRUD with validation:

const userRepo = orm.createRepository(createUserRepository);
const user = await userRepo.create({
  email: 'test@example.com',
  name: 'Test'
});
// Validation, typing, plugins — all out of the box
Enter fullscreen mode Exit fullscreen mode

Functional DAL — for complex queries with composition:

const getUserWithStats = createQuery(async (ctx, id: number) => {
  const user = await ctx.db
    .selectFrom('users')
    .selectAll()
    .where('id', '=', id)
    .executeTakeFirst();

  const stats = await ctx.db
    .selectFrom('events')
    .select(sql<number>`count(*)`.as('total'))
    .where('user_id', '=', id)
    .executeTakeFirst();

  return { user, stats };
});
Enter fullscreen mode Exit fullscreen mode

Typed Error Handling

Instead of parsing error strings:

// Without Kysera
try {
  await db.insertInto('users').values(data).execute();
} catch (error: unknown) {
  // What is this? UniqueConstraint? ForeignKey? Who knows?
  if (error.message.includes('duplicate')) { /* ... */ }
}

// With Kysera
try {
  await userRepo.create(data);
} catch (error) {
  const dbError = parseDatabaseError(error, 'postgres');
  if (dbError instanceof UniqueConstraintError) {
    // Typed error with constraint name, table, columns
  }
}
Enter fullscreen mode Exit fullscreen mode

Production Utilities

Opt-in packages for production:

// Health checks
const health = await checkDatabaseHealth(db, pool);
// { status: 'healthy', latency: 12, poolMetrics: {...} }

// Retry with exponential backoff
const result = await withRetry(
  () => userRepo.findById(id),
  { maxAttempts: 3, backoff: 'exponential' }
);

// Graceful shutdown
await gracefulShutdown(db, {
  timeout: 30000,
  onClose: () => console.log('DB connections closed')
});
Enter fullscreen mode Exit fullscreen mode

Architectural Decision: Unified Execution Layer

The key architectural decision in Kysera is KyseraExecutor. It's a Proxy wrapper over Kysely that:

  1. Intercepts selectFrom, insertInto, updateTable, deleteFrom
  2. Applies plugins to each query
  3. Propagates plugins into transactions
  4. Maintains zero overhead when no plugins are present
┌─────────────────────────────────────────┐
│  Application Code                       │
│         │                               │
│         ▼                               │
│  ┌─────────────────┐                    │
│  │ KyseraExecutor  │ ← Proxy intercepts │
│  │ (selectFrom)    │   query methods    │
│  └────────┬────────┘                    │
│           │                             │
│           ▼                             │
│  ┌─────────────────┐                    │
│  │ Plugin Chain    │ ← Plugins modify   │
│  │ (interceptors)  │   query builders   │
│  └────────┬────────┘                    │
│           │                             │
│           ▼                             │
│  ┌─────────────────┐                    │
│  │     Kysely      │ ← Original QB      │
│  └────────┬────────┘                    │
│           │                             │
│           ▼                             │
│       Database                          │
└─────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

This allows plugins to work with both patterns — Repository and DAL — without code duplication.


Honest Comparison

Kysera vs Prisma

Aspect Prisma Kysera
Abstraction High (hides SQL) Low (SQL visible)
Typing Generated from schema From Kysely + DB schema
Soft delete Middleware (not obvious) Plugin (explicit)
Complex queries $queryRaw (loses types) Kysely (preserves types)
Bundle size ~2MB (with engine) ~30KB (all packages)
Lock-in High Low (can fall back to Kysely)

When Prisma is better:

  • Quick start with simple CRUD
  • Team doesn't want to think about SQL
  • Need automatic migrations from schema

When Kysera is better:

  • Complex queries make up a significant part of the project
  • SQL control is important
  • Bundle size is critical
  • Need gradual migration or exit capability

Kysera vs Drizzle

Aspect Drizzle Kysera
Schema source Code (schema.ts) Database
Plugin system No Yes
Repository pattern No Optional
Error handling Generic Typed errors
Production utils Partial Full set (opt-in)

When Drizzle is better:

  • You prefer schema-first approach
  • Relational queries cover your JOINs
  • You don't need plugins

When Kysera is better:

  • Schema in DB (database-first)
  • Need soft delete, audit, RLS as plugins
  • Production utilities are important

Kysera vs "Just Kysely"

This is the most important question: why Kysera if Kysely exists?

Problem Kysely Kysely + Kysera
Soft delete Manual WHERE in every query Plugin automatically
Audit trail Your own implementation Plugin with full history
RLS Manual filtering Plugin with AsyncLocalStorage
Error parsing try/catch + strings Typed errors
Pagination Your own implementation Offset + cursor out of the box
Health checks Your own implementation @kysera/infra
Retry logic Your own implementation @kysera/infra
Validation Your own integration Built-in (Zod, Valibot, etc.)
Testing Your own isolation Transaction rollback helpers

Conclusion: Kysera is a codification of best practices that every team otherwise writes from scratch.


A Critical Look at Kysera

Weaknesses

1. Additional Abstraction

Every layer of abstraction is:

  • A potential place for bugs
  • A need to study documentation
  • A dependency on maintainers

For simple projects, the overhead may not pay off.

2. Smaller Ecosystem Than Prisma

Prisma has a huge ecosystem: Prisma Studio, Prisma Accelerate, integrations with all frameworks. Kysera is a young project with a smaller community.

3. Two Patterns — Potential Confusion

Repository and DAL in the same project can create inconsistency if the team doesn't agree on conventions.

4. Plugin Dependencies

Plugins have execution order and dependencies. Wrong order can lead to unexpected behavior.

When NOT to Use Kysera

  1. Simple CRUD project — Prisma or even raw Kysely will be simpler.

  2. Team doesn't know SQL — Kysera doesn't hide SQL, this is a deliberate choice.

  3. Need entity lifecycle — Kysera rejects these patterns. If you need Identity Map or Unit of Work — look at TypeORM.

  4. Maximum DX matters more than control — Prisma gives better out-of-the-box experience for simple cases.

  5. You already have a working solution — Migration for migration's sake doesn't make sense.


Who is Kysera For?

The "Ideal" User Profile

Kysera is created for developers who:

  • Understand SQL and want to control it
  • Work on production systems with reliability requirements
  • Value explicitness over magic
  • Prefer composition over inheritance
  • Want opt-in functionality, not "all-inclusive"
  • Use TypeScript with strict mode

Typical Scenarios

1. Multi-tenant SaaS

RLS plugin + AsyncLocalStorage for automatic filtering by tenant_id.

2. Applications with Audit Requirements

Audit plugin for full change history with restore capability.

3. Complex Domain-Driven Applications

Functional DAL for complex queries + Repository for CRUD.

4. Microservices with Shared Database Patterns

Plugins as reusable packages between services.


The Philosophy of "Start minimal, grow as needed, stay transparent"

Start Minimal

Minimal installation — only @kysera/core (8KB):

npm install kysely @kysera/core
Enter fullscreen mode Exit fullscreen mode

You get error handling and pagination. Everything else is opt-in.

Grow as Needed

As your project grows, add what you need:

# Need soft delete?
npm install @kysera/soft-delete

# Need audit?
npm install @kysera/audit

# Need health checks?
npm install @kysera/infra
Enter fullscreen mode Exit fullscreen mode

Stay Transparent

Every operation:

  • Generates predictable SQL
  • Can be debugged via @kysera/debug
  • Has no hidden state
  • Works in transactions the same as outside them

Conclusion: An Honest Assessment

Kysera is not a revolution in data access. It is an evolutionary solution to specific problems that arise when scaling TypeScript projects with relational databases.

Strengths:

  • Solves real production problems (soft delete, audit, RLS, error handling)
  • Preserves SQL control through Kysely
  • Modular architecture — take only what you need
  • Low lock-in — can fall back to pure Kysely
  • Plugin system for reusing patterns

Weaknesses:

  • Additional abstraction over Kysely
  • Smaller ecosystem than Prisma
  • Requires understanding of SQL
  • Young project

Verdict:

If you:

  • Already use or are planning to use Kysely
  • Work on a production system
  • Want plugins for cross-cutting concerns
  • Value explicit over implicit

...then Kysera is worth considering.

If you need maximum DX with minimal SQL, or you're working on a simple project — Prisma or Drizzle might be a better fit.

Kysera doesn't try to be the best solution for everyone. It tries to be the right solution for a certain class of problems.


Appendix: Links for Further Reading

Top comments (0)