kysely

Type-safe SQL query builder assistant for Kysely. Use this skill whenever the user is writing database queries with Kysely, setting up a Kysely database instance, defining Database types, writing migrations, or asking how to do SQL operations (SELECT, INSERT, UPDATE, DELETE, JOIN, CTE, MERGE, transactions) in Kysely. Also trigger when the user mentions kysely in their code, imports from 'kysely', has a Database interface with table types, or asks about type-safe SQL in TypeScript. Also trigger on common misspellings like "kaisley" and "kysley". Activate even if they just say "query builder" or "how do I do X in kysely" or paste code that uses selectFrom/insertInto/updateTable/deleteFrom patterns. This skill covers relations (jsonArrayFrom/jsonObjectFrom), expression builders, raw SQL via the sql tag, conditional queries, reusable helpers, schema management, migrations, execution pipeline/debugging, plugins, and all Kysely patterns.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "kysely" with this command: npx skills add zackbart/skills/zackbart-skills-kysely

Kysely Query Builder

You are an expert at writing type-safe SQL queries with Kysely. Kysely is a type-safe TypeScript SQL query builder — it is NOT an ORM. It builds SQL based on what the developer writes and provides compile-time type checking.

Core Principles

  1. Type safety first — Always define proper Database interfaces. Use Generated<T> for auto-increment/default columns, ColumnType<S, I, U> for columns with different select/insert/update types.
  2. Kysely is not an ORM — There are no model classes, no lazy loading, no relation definitions. Relations are handled via joins or JSON helper functions.
  3. Everything is an expressionExpression<T> is the fundamental building block. Helpers should accept and return expressions for composability.
  4. The query builder is immutable — Each method call returns a new builder. When building queries conditionally, reassign: query = query.where(...).
  5. Dialect awareness — Some features are dialect-specific (e.g., returning on PostgreSQL, insertId on MySQL/SQLite, distinctOn on PostgreSQL, mergeInto varies by dialect). Always consider which database the user targets.

How to Use This Skill

When the user needs Kysely help, consult the reference files for detailed patterns:

  • references/examples.md — Complete code examples for SELECT, WHERE, JOIN, INSERT, UPDATE, DELETE, MERGE, transactions, and CTEs
  • references/recipes.md — Advanced patterns: relations, reusable helpers, data types, raw SQL, conditional selects, expressions, schemas, plugins, extending Kysely, introspection, and logging
  • references/migrations.md — Migrator setup and production-safe migration flows (migrateToLatest, directional up/down, target migration, rollback all, error handling)
  • references/execution.md — Query lifecycle internals and practical debugging/performance patterns (compile, executeQuery, stream, explain, plugins)

Read the relevant reference file section before generating code. The examples there are drawn directly from the official Kysely documentation and represent the canonical way to use each feature.

Quick Reference

Database Type Setup

import { Kysely, Generated, ColumnType, Selectable, Insertable, Updateable } from 'kysely'

interface Database {
  person: PersonTable
  pet: PetTable
}

interface PersonTable {
  id: Generated<number>           // auto-increment, not needed on insert
  first_name: string
  last_name: string | null        // nullable column
  age: number
  created_at: ColumnType<Date, string | undefined, never>  // different types for select/insert/update
}

// Utility types for function signatures
type Person = Selectable<PersonTable>
type NewPerson = Insertable<PersonTable>
type PersonUpdate = Updateable<PersonTable>

Common Operations at a Glance

OperationPattern
Selectdb.selectFrom('table').select([...]).where(...).execute()
Insertdb.insertInto('table').values({...}).executeTakeFirst()
Updatedb.updateTable('table').set({...}).where(...).executeTakeFirst()
Deletedb.deleteFrom('table').where(...).executeTakeFirst()
Joindb.selectFrom('a').innerJoin('b', 'b.a_id', 'a.id').select([...])
Transactiondb.transaction().execute(async (trx) => { ... })
CTEdb.with('name', (qb) => qb.selectFrom(...).select([...])).selectFrom('name')
Nested arrayjsonArrayFrom(eb.selectFrom('child').select([...]).whereRef(...)).as('children')
Nested objectjsonObjectFrom(eb.selectFrom('related').select([...]).whereRef(...)).as('item')
Raw SQLsql<Type>`expression with ${parameterized} values`
Conditionalif (condition) { query = query.where(...) }

Expression Builder

The expression builder (eb) is available via callbacks in most methods. It provides:

  • eb('column', 'op', value) — binary comparison
  • eb.and([...]) / eb.or([...]) — logical combinations
  • eb.not(expr) / eb.exists(subquery) — negation and existence
  • eb.selectFrom(...) — subqueries
  • eb.ref('column') — column references
  • eb.val(value) — parameterized values
  • eb.lit(value) — literal values (embedded in SQL)
  • eb.fn.count(...) / eb.fn.avg(...) / eb.fn.max(...) — aggregate functions
  • eb.fn('name', [...]) — call any database function

Import Paths

// Core
import { Kysely, sql, expressionBuilder } from 'kysely'

// Dialect-specific relation helpers
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres'   // or /mysql or /sqlite

// Plugins
import { ParseJSONResultsPlugin, CamelCasePlugin, DeduplicateJoinsPlugin } from 'kysely'

Common Mistakes to Catch

  • Calling .select() before .innerJoin() — The joined table's columns won't be available. Always join first, then select.
  • Forgetting .as() on complex selections — Any expression that isn't a simple column reference needs a name via .as('alias').
  • Using returning on MySQL — MySQL doesn't support RETURNING. Use insertId from the result instead.
  • Expecting relations like an ORM — There are no eager/lazy loading. Use jsonArrayFrom/jsonObjectFrom or explicit joins.
  • Mutating queries instead of reassigning — The builder is immutable. query.where(...) returns a new query; it doesn't modify the existing one.
  • Not importing dialect-specific helpersjsonArrayFrom and jsonObjectFrom come from kysely/helpers/postgres (or /mysql//sqlite), not from kysely directly.
  • JSON parsing issues — If the database driver returns JSON columns as strings instead of objects, add ParseJSONResultsPlugin to the Kysely instance.
  • Using app DB types in migrations — Migration callbacks should accept Kysely<any>, not your runtime app Database type.
  • Assuming migrations throw by default — Migrator methods return a result set with error; always check it and fail explicitly.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

kysely

No summary provided by upstream source.

Repository SourceNeeds Review
General

optimize-prompt

No summary provided by upstream source.

Repository SourceNeeds Review
General

kysely

No summary provided by upstream source.

Repository SourceNeeds Review
General

second-opinion

No summary provided by upstream source.

Repository SourceNeeds Review