Kysely developers build and maintain the type-safe SQL query builder layer that gives TypeScript full static visibility into database queries — inferring column types from a hand-authored or generated database schema type, catching invalid column names and type mismatches at compile time rather than at runtime, and producing queries that stay close to raw SQL structure so engineers can understand exactly what is executed against the database without learning a new abstraction model. At remote-first technology companies, they serve as the backend and full-stack TypeScript engineers who choose Kysely over ORMs when query flexibility and SQL transparency matter more than code-generation scaffolding — implementing performant, correctly-typed queries for PostgreSQL, MySQL, and SQLite without Prisma's code generation step or Drizzle's additional migration DSL.
What Kysely developers do
Kysely developers define the database type — writing interface Database { users: UsersTable; posts: PostsTable } with table interfaces that list every column as a typed field (id: Generated<number>, email: string, created_at: ColumnType<Date, string | undefined, never>) to provide the type source that Kysely uses for all query inference; instantiate the database — creating const db = new Kysely<Database>({ dialect: new PostgresDialect({ pool: new Pool({ connectionString: process.env.DATABASE_URL }) }) }) for the singleton database instance typed against the schema interface; write select queries — using db.selectFrom('users').select(['id', 'email', 'name']).where('active', '=', true).orderBy('created_at', 'desc').limit(20).execute() where TypeScript enforces that column names match the schema and the return type is inferred as { id: number; email: string; name: string }[]; write insert queries — using db.insertInto('users').values({ email: 'user@example.com', name: 'Alice' }).returningAll().executeTakeFirstOrThrow() for typed inserts that return the inserted row with database-generated fields included; write update queries — using db.updateTable('users').set({ name: 'Bob', updated_at: new Date() }).where('id', '=', userId).returning(['id', 'name']).executeTakeFirst() for partial updates with typed column set constraints; write delete queries — using db.deleteFrom('users').where('id', '=', userId).executeTakeFirst() with type-safe where clause column references; write joins — using .innerJoin('posts', 'posts.user_id', 'users.id').select(['users.email', 'posts.title', 'posts.created_at']) where Kysely infers the joined table's column types and prevents ambiguous column references without table-qualified names; write subqueries — using db.selectFrom('users').select(eb => [eb.selectFrom('posts').select(eb.fn.count('id').as('count')).whereRef('posts.user_id', '=', 'users.id').as('post_count')]).execute() for correlated subqueries expressed in the fluent API; write conditional queries — using let query = db.selectFrom('users').selectAll() and if (filter.active !== undefined) { query = query.where('active', '=', filter.active) } for type-safe dynamic query building where the final query shape is still correctly typed; implement transactions — using await db.transaction().execute(async trx => { const user = await trx.insertInto('users').values(userData).returningAll().executeTakeFirstOrThrow(); await trx.insertInto('profiles').values({ user_id: user.id }).execute() }) for multi-statement transactions; implement migrations — using @kysely/migrator with db.schema.createTable('users').addColumn('id', 'serial', col => col.primaryKey()).execute() for schema migrations using Kysely's SQL DDL builder; and generate type definitions — using kysely-codegen to introspect a live database and generate the Database interface automatically, eliminating manual schema type maintenance.
Key skills for Kysely developers
- Schema types: Database interface; table interfaces; ColumnType; Generated; Selectable; Insertable; Updateable
- Queries: selectFrom; insertInto; updateTable; deleteFrom; select; selectAll; execute; executeTakeFirst
- Where: where; orWhere; whereRef; whereBetween; whereIn; eb (ExpressionBuilder)
- Joins: innerJoin; leftJoin; rightJoin; fullJoin; crossJoin; on; onRef
- Aggregates: fn.count; fn.sum; fn.avg; fn.max; fn.min; groupBy; having
- Transactions: transaction().execute; savepoints; isolation levels; rollback on throw
- Migrations: @kysely/migrator; db.schema; createTable; alterTable; addColumn; dropColumn
- Dynamic queries: conditional where; dynamic select columns; DynamicModule; CastedExpression
- Raw SQL: sql template literal; sql.raw; sql.join; sql.table; sql.ref
- Codegen: kysely-codegen; introspection; --dialect; --url; generated Database type
Salary expectations for remote Kysely developers
Remote Kysely developers earn $90,000–$153,000 total compensation. Base salaries range from $76,000–$126,000, with equity at technology companies where database query correctness, TypeScript type coverage across the data access layer, and the elimination of runtime SQL errors directly affect application reliability and development velocity on TypeScript-first backend teams. Kysely developers with complex join query architectures for multi-table relational data models, migration strategy expertise using @kysely/migrator for zero-downtime schema changes, dynamic query builder implementations for filter-heavy API endpoints, and demonstrated replacement of ORM-generated N+1 queries with efficient Kysely join queries command the strongest premiums. Those with Kysely combined with broader TypeScript backend expertise — Hono, Fastify, or Bun runtime deployments — earn toward the top of the range.
Career progression for Kysely developers
The path from Kysely developer leads to senior backend engineer (broader scope across the full Node.js or Bun backend stack with database access as a core specialization), database engineer (deeper focus on query performance, indexing strategy, and schema design for high-throughput PostgreSQL or MySQL systems), or TypeScript platform engineer (owning the type-safe data access layer standards across a multi-service backend architecture). Some Kysely developers expand into query performance specialization, using Kysely's SQL-transparent output alongside EXPLAIN ANALYZE to diagnose and optimize slow queries in a way that ORM abstraction obscures. Others transition into database migration tooling, designing zero-downtime migration strategies for high-traffic production databases using Kysely's DDL builder alongside expand-contract migration patterns. Kysely developers who contribute to the ecosystem — writing dialect adapters, utility libraries, or codegen improvements — contribute to the TypeScript SQL ecosystem that competes with Prisma and Drizzle.
Remote work considerations for Kysely developers
Building Kysely-based data access layers for distributed engineering teams requires schema type management conventions, transaction handling standards, and raw SQL escape-hatch documentation that prevent distributed engineers from diverging the Database interface from the actual schema, bypassing Kysely's type safety with overuse of sql.raw, or mishandling transaction error propagation in nested async functions. Kysely developers at remote companies establish the schema type source-of-truth policy — either committing the Database interface as a handwritten file that all schema migrations must update in the same PR, or configuring kysely-codegen to regenerate the interface as a prebuild step and committing the generated file — because distributed engineers adding migrations without updating the type file create queries that pass TypeScript but fail at runtime when column references are stale; document the Selectable, Insertable, and Updateable helper types — showing that Selectable<UsersTable> is the correct return type for select queries (all columns present, Generated<T> resolved to T), Insertable<UsersTable> for insert values (generated columns optional), and Updateable<UsersTable> for set objects (all columns optional) — because distributed engineers manually type query return values instead of using these helpers, creating drift between schema and application types; establish the ExpressionBuilder (eb) pattern for complex conditions — showing the eb.and, eb.or, and eb.cmpr APIs for building WHERE clauses programmatically — because distributed engineers fall back to sql.raw for complex conditions that eb can express with full type safety; and document the transaction error contract — that any thrown error inside db.transaction().execute(callback) automatically rolls back the transaction, so engineers must not catch errors inside the callback unless re-throwing.
Top industries hiring remote Kysely developers
- TypeScript-first startup and scale-up organizations where Kysely's type-safe SQL model fits the team's TypeScript-everywhere ethos, and the lack of a code generation step (compared to Prisma) simplifies CI pipelines and eliminates regeneration-on-schema-change friction
- Performance-critical API backends where Kysely's SQL-transparent output makes query optimization straightforward — engineers can read the generated SQL directly, add
EXPLAIN ANALYZE, and tune indexes without decoding ORM magic - Multi-database organizations supporting both PostgreSQL and SQLite where Kysely's dialect system (PostgresDialect, MysqlDialect, SqliteDialect) and LibSQLDialect for Turso enable the same query code to run against different backends in production versus development or edge environments
- Hono and Bun ecosystem teams building lightweight API servers where Kysely's minimal runtime footprint and dialect support for Cloudflare D1, Turso, and PlanetScale's HTTP API make it the preferred query builder for edge-deployed or serverless backends
- Database migration modernization organizations replacing legacy ORM codebases where Kysely's explicit SQL model provides a migration path from Sequelize or TypeORM without introducing the full Prisma code-generation workflow
Interview preparation for Kysely developer roles
Expect schema type questions: write the Kysely Database interface for a posts table with id (auto-generated serial), title (string), user_id (number FK), published (boolean), and created_at (auto-set timestamp) — what ColumnType, Generated, Selectable, Insertable, and Updateable look like for each column. Query questions ask how you'd write a query that fetches the 10 most recent published posts with their author's email — what the selectFrom, innerJoin, where, orderBy, limit, and column select look like. Dynamic query questions ask how you'd implement a posts list endpoint that optionally filters by published status and userId based on query parameters — what the conditional .where() chain looks like while keeping return types correct. Transaction questions ask how you'd implement a post creation function that inserts a post and increments the author's post count in a single atomic operation — what db.transaction().execute() looks like. Migration questions ask how you'd write a Kysely migration that adds a tags text array column to the posts table with a NOT NULL default — what db.schema.alterTable('posts').addColumn() looks like. Raw SQL questions ask when you'd use the sql template literal and what it provides that the fluent API doesn't — what sql\extract(epoch from ${sql.ref('created_at')})`` looks like. Be ready to compare Kysely with Prisma and Drizzle — the specific trade-offs around type generation, ORM features, migration tooling, and SQL control.
Tools and technologies for Kysely developers
Core: Kysely; kysely npm package; Kysely
Global remote opportunities for Kysely developers
Kysely developer expertise is in growing global demand, with Kysely's emergence as the preferred type-safe SQL query builder for TypeScript backends — with over 700,000 weekly npm downloads, adoption by the Hono and Bun communities as the idiomatic database library, and selection by PlanetScale, Turso, and Cloudflare as the recommended query layer for their serverless database offerings — creating demand for engineers who understand both Kysely's type system and the PostgreSQL and SQLite query patterns that Kysely makes type-safe. US-based Kysely developers are in demand at TypeScript-first API teams, edge computing organizations using Cloudflare Workers or Bun, and performance-focused backends where direct SQL control is a priority. EMEA-based Kysely developers are well-positioned given the European TypeScript backend community's strong adoption of type-safe tooling — teams that moved from Sequelize to TypeORM to Prisma and are now evaluating Kysely's lower-overhead SQL-first model make up a significant share of Kysely's growing adoption. Kysely's continued development — new dialect support, improved CTE and window function APIs, and growing plugin ecosystem — ensures sustained demand as the TypeScript SQL ecosystem matures.
Frequently asked questions
How do Kysely's ColumnType, Generated, Selectable, Insertable, and Updateable utility types work together? These types model the reality that database columns often have different types depending on whether you're reading, inserting, or updating. ColumnType<Select, Insert, Update>: the three-parameter generic where Select is the type returned when selecting, Insert is the type accepted when inserting, and Update is the type accepted when updating. Generated<T>: shorthand for ColumnType<T, T | undefined, T | undefined> — a column that the database generates (auto-increment IDs, default timestamps) that you don't need to provide on insert or update. Selectable<Table>: helper that resolves the Select type from each column's ColumnType — the correct return type for a selectAll() query. Insertable<Table>: helper that resolves the Insert type — columns with Generated<T> become optional; columns marked never as insert type are excluded. Updateable<Table>: helper that resolves the Update type — all columns become optional, never columns are excluded. Usage: define table interfaces once, then use the helpers as function parameter and return types throughout the application — function getUser(id: number): Promise<Selectable<UsersTable>>, function createUser(data: Insertable<UsersTable>): Promise<Selectable<UsersTable>>.
How does Kysely handle CTEs (Common Table Expressions) and window functions for complex analytical queries? CTEs: db.with('active_users', db => db.selectFrom('users').select(['id', 'email']).where('active', '=', true)).selectFrom('active_users').selectAll().execute() — the .with() method adds a named CTE that can be referenced in the main query's .selectFrom() call. Recursive CTEs: db.withRecursive('tree', db => db.selectFrom('categories').select(['id', 'parent_id', 'name']).where('parent_id', 'is', null).unionAll(db2 => db2.selectFrom('categories').innerJoin('tree', 'tree.id', 'categories.parent_id').select(['categories.id', 'categories.parent_id', 'categories.name']))) for hierarchical data traversal. Window functions: Kysely supports window functions through the over() clause — eb.fn.rowNumber().over(ov => ov.partitionBy('department_id').orderBy('salary', 'desc')).as('rank') — producing ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC). Raw SQL for unsupported functions: sql<number>\rank() over (partition by ${sql.ref('department_id')} order by ${sql.ref('salary')} desc)`.$castTousing thesql` template literal for window function expressions Kysely doesn't yet have first-class builder support for.
How does Kysely compare with Drizzle ORM and when should teams choose one over the other? Kysely and Drizzle are both TypeScript-first, SQL-transparent query tools but differ in philosophy. Kysely: pure query builder — you define the Database interface manually (or via codegen from the live database), and Kysely never generates migration files or owns the schema. Drizzle: schema-as-code ORM — you define tables using Drizzle's schema DSL (pgTable, sqliteTable) and Drizzle generates both TypeScript types and SQL migrations from that definition, serving as both query builder and migration tool. Choose Kysely when: the team prefers handwritten or codegen-from-database types, the database schema is managed outside the application (DBA-owned, or migrations via Flyway/Liquibase), or pure query-building without ORM schema ownership is desired. Choose Drizzle when: you want schema-as-code where migrations are generated from TypeScript table definitions, you need an integrated migration runner alongside the query builder, or the team prefers a single tool for both schema definition and queries. Both support PostgreSQL, MySQL, and SQLite with comparable TypeScript type safety. For edge runtimes: both support Cloudflare D1, Turso, and PlanetScale; Drizzle has broader ORM community adoption while Kysely has stronger SQL-expert adoption.