DEV Community

Alex Spinov
Alex Spinov

Posted on

Kysely Has a Free Type-Safe SQL Query Builder — Like Knex but With Full TypeScript Autocomplete

The SQL Builder Problem

Knex: great builder, returns any. Prisma: full ORM, can't write raw SQL easily. TypeORM: decorators, magic strings. Raw SQL: no autocomplete, no type checking.

Kysely is a type-safe SQL query builder. Full TypeScript autocomplete for table names, column names, and result types. Zero runtime overhead.

What Kysely Gives You

Type-Safe Queries

import { Kysely, PostgresDialect } from 'kysely';

interface Database {
  users: {
    id: number;
    name: string;
    email: string;
    created_at: Date;
  };
  posts: {
    id: number;
    title: string;
    author_id: number;
  };
}

const db = new Kysely<Database>({ dialect: new PostgresDialect({ pool }) });

// Full autocomplete: table names, column names
const users = await db
  .selectFrom('users')
  .select(['id', 'name', 'email'])
  .where('email', 'like', '%@company.com')
  .orderBy('created_at', 'desc')
  .execute();
// users is typed as { id: number; name: string; email: string }[]
Enter fullscreen mode Exit fullscreen mode

Joins With Types

const results = await db
  .selectFrom('posts')
  .innerJoin('users', 'users.id', 'posts.author_id')
  .select(['posts.title', 'users.name as author'])
  .execute();
// results: { title: string; author: string }[]
Enter fullscreen mode Exit fullscreen mode

Column names from both tables available with autocomplete.

Insert, Update, Delete

await db.insertInto('users').values({
  name: 'John',
  email: 'john@example.com',
}).execute();

await db.updateTable('users')
  .set({ name: 'Jane' })
  .where('id', '=', 1)
  .execute();

await db.deleteFrom('users')
  .where('id', '=', 1)
  .execute();
Enter fullscreen mode Exit fullscreen mode

Subqueries

const result = await db
  .selectFrom('users')
  .select([
    'name',
    (eb) => eb.selectFrom('posts')
      .select(eb.fn.count('id').as('count'))
      .whereRef('posts.author_id', '=', 'users.id')
      .as('post_count'),
  ])
  .execute();
Enter fullscreen mode Exit fullscreen mode

Raw SQL When Needed

const result = await sql<{ count: number }>`
  SELECT COUNT(*) as count FROM users WHERE created_at > ${date}
`.execute(db);
Enter fullscreen mode Exit fullscreen mode

Migrations

export async function up(db: Kysely<any>): Promise<void> {
  await db.schema
    .createTable('users')
    .addColumn('id', 'serial', (col) => col.primaryKey())
    .addColumn('name', 'text', (col) => col.notNull())
    .addColumn('email', 'text', (col) => col.unique().notNull())
    .execute();
}
Enter fullscreen mode Exit fullscreen mode

Works With

PostgreSQL, MySQL, SQLite, MSSQL. Works on Node, Deno, Bun, Cloudflare Workers.

Quick Start

npm install kysely pg
Enter fullscreen mode Exit fullscreen mode

Why This Matters

SQL is the best query language. The problem was never SQL — it was the lack of type safety. Kysely adds TypeScript to SQL without adding ORM complexity.


Need data for your SQL databases? Check out my web scraping actors on Apify Store — structured data ready for import. For custom solutions, email spinov001@gmail.com.

Top comments (0)