kysely-org/kysely

A type-safe TypeScript SQL query builder

13,693 stars TypeScript 10 components

Generates type-safe SQL queries from TypeScript code with dialect-specific compilation

Kysely processes queries through three distinct phases: First, developers use fluent TypeScript APIs (SelectQueryBuilder, InsertQueryBuilder, etc.) which create an abstract syntax tree of OperationNode objects capturing query structure without database-specific syntax. Next, the DefaultQueryCompiler traverses this AST using dialect-specific visitors to generate SQL strings with parameter placeholders. Finally, the DefaultQueryExecutor acquires database connections, binds parameters, executes the SQL, and maps results back to TypeScript types based on the original schema definitions.

Under the hood, the system uses 2 feedback loops, 2 data pools, 3 control points to manage its runtime behavior.

A 10-component library. 471 files analyzed. Data flows through 4 distinct pipeline stages.

How Data Flows Through the System

Kysely processes queries through three distinct phases: First, developers use fluent TypeScript APIs (SelectQueryBuilder, InsertQueryBuilder, etc.) which create an abstract syntax tree of OperationNode objects capturing query structure without database-specific syntax. Next, the DefaultQueryCompiler traverses this AST using dialect-specific visitors to generate SQL strings with parameter placeholders. Finally, the DefaultQueryExecutor acquires database connections, binds parameters, executes the SQL, and maps results back to TypeScript types based on the original schema definitions.

  1. Query Construction — Developer chains method calls on QueryBuilder instances (db.selectFrom('users').select(['name', 'email']).where('active', '=', true)) which progressively build OperationNode trees while TypeScript tracks column types and table relationships [DatabaseSchema types → OperationNode]
  2. AST Compilation — DefaultQueryCompiler.compileQuery() walks the OperationNode tree using visitSelectQuery, visitWhereClause, etc. methods that delegate to dialect-specific implementations to generate SQL strings with parameter placeholders [OperationNode → CompiledQuery]
  3. Query Execution — DefaultQueryExecutor.executeQuery() acquires a connection from the connection provider, calls connection.executeQuery() with the compiled SQL and parameters, then processes the raw database results [CompiledQuery → QueryResult]
  4. Result Transformation — QueryResult.rows are mapped back to TypeScript types using the original schema information captured during query building, ensuring type safety end-to-end [QueryResult → typed result objects]

Data Models

The data structures that flow between stages — the contracts that hold the system together.

OperationNode src/operation-node/operation-node.ts
union type with kind discriminator — SelectQueryNode, InsertQueryNode, UpdateQueryNode etc. each containing arrays of child nodes representing clauses
Created during query building, transformed by visitors during compilation, consumed by dialect-specific code generators
CompiledQuery src/query-compiler/compiled-query.ts
object with sql: string, parameters: readonly unknown[], query: OperationNode
Generated by query compiler from operation nodes, passed to database drivers for execution
DatabaseSchema src/util/type-utils.ts
generic type mapping table names to column definitions — Record<string, Record<string, ColumnType<SelectType, InsertType, UpdateType>>>
Defined by users to describe their database structure, flows through TypeScript's type system to provide compile-time validation
QueryResult src/driver/database-connection.ts
object with rows: unknown[], numAffectedRows?: bigint | number, insertId?: bigint | number
Returned by database drivers, transformed into type-safe results based on schema definitions
DialectAdapter src/dialect/dialect-adapter.ts
interface with methods createDriver(), createQueryCompiler(), createIntrospector() returning database-specific implementations
Instantiated during Kysely initialization, used throughout query lifecycle to provide database-specific behavior

Hidden Assumptions

Things this code relies on but never validates. These are the things that cause silent failures when the system changes.

critical Resource unguarded

Connection pools maintain healthy connections indefinitely without timeouts or connection limits exhaustion — acquireConnection() waits infinitely for an available connection

If this fails: If connection pool is exhausted or connections become stale, queries hang forever without timeout or error, causing application deadlock

src/driver/default-connection-provider.ts:DefaultConnectionProvider
critical Shape unguarded

Database drivers return QueryResult.rows as unknown[] where each row matches the expected TypeScript schema types, but no runtime validation occurs between database values and TypeScript types

If this fails: When database schema changes or returns unexpected types (NULL for NOT NULL columns, strings for numbers), Kysely returns incorrectly typed data that passes TypeScript checks but fails at runtime

src/query-executor/default-query-executor.ts:DefaultQueryExecutor.executeQuery
critical Contract weakly guarded

Pool configuration from user contains valid PostgreSQL connection parameters and the pool factory function always returns a connected pg.Pool instance

If this fails: Invalid connection configs or network issues cause cryptic pg.Pool errors that bubble up without context, making it unclear whether the issue is configuration, network, or database

src/dialect/postgres/postgres-dialect.ts:PostgresDialect.createDriver
critical Ordering weakly guarded

Visitor pattern traverses OperationNode trees in dependency-correct order where referenced nodes (tables, columns) are visited before dependent nodes (expressions, conditions) that use them

If this fails: If traversal order is wrong, SQL generation produces invalid queries with undefined table aliases or column references, especially in complex joins and subqueries

src/operation-node/operation-node-visitor.ts:OperationNodeVisitor
warning Temporal unguarded

Compiled query cache entries remain valid for the lifetime of the Kysely instance — cached SQL never becomes stale due to schema changes or dialect updates

If this fails: After database schema changes (column renames, table drops), cached queries contain outdated SQL that executes against non-existent objects, causing runtime SQL errors

src/query-compiler/default-query-compiler.ts:DefaultQueryCompiler
warning Scale unguarded

TypeScript's type system can handle arbitrarily complex nested generic types from chained query operations like selectFrom().join().where().select() without hitting compiler limits

If this fails: Extremely complex queries with many joins and subqueries cause TypeScript compilation to hang or fail with 'Type instantiation is excessively deep' errors

src/query-builder/select-query-builder.ts:SelectQueryBuilder
warning Domain weakly guarded

Parameter binding uses 1-based indexing ($1, $2, $3) and parameters array indices match PostgreSQL's parameter placeholder numbering exactly

If this fails: Off-by-one errors in parameter binding cause queries to use wrong values — $1 gets parameters[1] instead of parameters[0], leading to data corruption or query failures

src/dialect/postgres/postgres-query-compiler.ts:PostgresQueryCompiler
warning Environment unguarded

Database configuration from Config contains valid PostgreSQL connection parameters and the target database exists and is accessible when Pool is created

If this fails: Missing database, wrong credentials, or network issues cause pg.Pool creation to fail with connection errors that don't distinguish between configuration vs infrastructure problems

example/src/app.ts:App.constructor
warning Contract weakly guarded

All dialect-specific DatabaseConnection implementations handle parameter binding consistently and return QueryResult with the same structure — rows, numAffectedRows, insertId have identical semantics across PostgreSQL, MySQL, SQLite

If this fails: Switching dialects breaks applications that depend on specific result formats — MySQL returns insertId as number but PostgreSQL might return bigint, causing type mismatches

src/driver/database-connection.ts:DatabaseConnection.executeQuery
warning Resource unguarded

Memory usage scales linearly with query complexity and result set size — large result sets or deeply nested OperationNode trees don't cause exponential memory growth during compilation or execution

If this fails: Queries returning millions of rows or with hundreds of joins consume excessive memory, potentially causing out-of-memory crashes in production

src/query-executor/default-query-executor.ts:DefaultQueryExecutor

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

Connection Pool (buffer)
Database connections maintained by driver-specific pools (pg.Pool, mysql.createPool) to avoid connection overhead
Query Cache (cache)
Compiled query cache that avoids re-parsing identical operation node structures

Feedback Loops

Delays

Control Points

Technology Stack

TypeScript (runtime)
Provides type safety and compile-time SQL validation through advanced type system features
Node.js (runtime)
Primary runtime environment with support for multiple database drivers
Deno (runtime)
Alternative JavaScript runtime supported through ESM and zero-dependency architecture
PostgreSQL (database)
Supported database with dialect providing Postgres-specific SQL generation and type mapping
MySQL (database)
Supported database with dialect handling MySQL syntax variations and connection pooling
SQLite (database)
Supported database for embedded use cases with file-based storage
Mocha (testing)
Test runner for comprehensive test suite covering multiple runtime environments
ESBuild (build)
Fast TypeScript compilation and bundling for distribution builds

Key Components

Package Structure

kysely (library)
The main Kysely type-safe SQL query builder library with comprehensive dialect support and schema management capabilities.
kysely-site (app)
Documentation website built with Docusaurus showcasing features, examples, and getting started guides.
kysely-cloudflare-workers-test (tooling)
Test suite verifying Kysely compatibility with Cloudflare Workers runtime environment.

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Library Repositories

Frequently Asked Questions

What is kysely used for?

Generates type-safe SQL queries from TypeScript code with dialect-specific compilation kysely-org/kysely is a 10-component library written in TypeScript. Data flows through 4 distinct pipeline stages. The codebase contains 471 files.

How is kysely architected?

kysely is organized into 5 architecture layers: Query Builder API, Operation Node System, Query Compilation, Database Dialects, and 1 more. Data flows through 4 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through kysely?

Data moves through 4 stages: Query Construction → AST Compilation → Query Execution → Result Transformation. Kysely processes queries through three distinct phases: First, developers use fluent TypeScript APIs (SelectQueryBuilder, InsertQueryBuilder, etc.) which create an abstract syntax tree of OperationNode objects capturing query structure without database-specific syntax. Next, the DefaultQueryCompiler traverses this AST using dialect-specific visitors to generate SQL strings with parameter placeholders. Finally, the DefaultQueryExecutor acquires database connections, binds parameters, executes the SQL, and maps results back to TypeScript types based on the original schema definitions. This pipeline design keeps the data transformation process straightforward.

What technologies does kysely use?

The core stack includes TypeScript (Provides type safety and compile-time SQL validation through advanced type system features), Node.js (Primary runtime environment with support for multiple database drivers), Deno (Alternative JavaScript runtime supported through ESM and zero-dependency architecture), PostgreSQL (Supported database with dialect providing Postgres-specific SQL generation and type mapping), MySQL (Supported database with dialect handling MySQL syntax variations and connection pooling), SQLite (Supported database for embedded use cases with file-based storage), and 2 more. A focused set of dependencies that keeps the build manageable.

What system dynamics does kysely have?

kysely exhibits 2 data pools (Connection Pool, Query Cache), 2 feedback loops, 3 control points, 2 delays. The feedback loops handle retry and circuit-breaker. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does kysely use?

4 design patterns detected: Fluent Builder Pattern, Visitor Pattern, Plugin Architecture, Type-Level Programming.

Analyzed on April 20, 2026 by CodeSea. Written by .