Skip to main content Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill kysely命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... 同仓库更多 Skills LinkedIn automation via the Linked API CLI - fetch profiles, search people and companies, send messages, manage connections, create posts, react, comment, and run Sales Navigator and custom workflows. Use when the user wants to interact with LinkedIn.
Xquik X data automation API - Use REST or MCP for tweet search, user lookup, follower exports, media downloads, monitors, webhooks, giveaway draws, and confirmation-gated X actions.
MCP (Model Context Protocol) - Build AI-native servers with tools, resources, and prompts. TypeScript/Python SDKs for Claude Desktop integration.
name kysely description Kysely type-safe SQL query builder - End-to-end type safety from schema to queries, migrations, transactions, plugins user-invocable false disable-model-invocation true version 1.0.0 category development author Claude MPM Team license MIT progressive_disclosure {"entry_point":{"summary":"Type-safe SQL query builder: full TypeScript inference, schema generation, migrations, no ORM overhead, raw SQL when needed","when_to_use":"Building type-safe database layers, migrating from ORMs, need raw SQL control with types, PostgreSQL/MySQL/SQLite apps","quick_start":"1. Define schema types 2. Create Kysely instance 3. Chain .selectFrom().where().execute() 4. Full type inference"}} context_limit 700 tags ["typescript","database","sql","query-builder","type-safe","kysely","orm-alternative"] requires_tools []
Kysely - Type-Safe SQL Query Builder
Overview
Kysely is a type-safe TypeScript SQL query builder that provides end-to-end type safety from database schema to query results. Unlike ORMs, it generates plain SQL and gives you full control while maintaining perfect TypeScript inference.
Key Features :
Complete type inference (schema → queries → results)
Zero runtime overhead (compiles to SQL)
Database-agnostic (PostgreSQL, MySQL, SQLite, MSSQL)
Migration system included
Plugin ecosystem (CTEs, JSON, geospatial)
Raw SQL integration when needed
Installation :
npm install kysely
npm install pg
npm install mysql2
npm install better-sqlite3
Quick Start
1. Define Database Schema Types
import { Generated , Selectable , Insertable , Updateable } from 'kysely' ;
interface UserTable {
id : Generated <number >;
email : string ;
name : string | null ;
created_at : Generated <Date >;
updated_at : Date ;
}
interface PostTable {
id : Generated <number >;
user_id : number ;
: ;
: ;
: < >;
: < >;
}
{
: ;
: ;
}
= < >;
= < >;
= < >;
title
string
content
string
published
Generated
boolean
created_at
Generated
Date
interface
Database
users
UserTable
posts
PostTable
type
User
Selectable
UserTable
type
NewUser
Insertable
UserTable
type
UserUpdate
Updateable
UserTable
2. Create Database Instance import { Kysely , PostgresDialect } from 'kysely' ;
import { Pool } from 'pg' ;
const db = new Kysely <Database >({
dialect : new PostgresDialect ({
pool : new Pool ({
host : process.env .DB_HOST ,
database : process.env .DB_NAME ,
user : process.env .DB_USER ,
password : process.env .DB_PASSWORD ,
max : 10 ,
}),
}),
});
3. Type-Safe Queries
const users = await db
.selectFrom ('users' )
.select (['id' , 'email' , 'name' ])
.where ('created_at' , '>' , new Date ('2024-01-01' ))
.execute ();
const newUser : NewUser = {
email : 'alice@example.com' ,
name : 'Alice' ,
updated_at : new Date (),
};
const inserted = await db
.insertInto ('users' )
.values (newUser)
.returningAll ()
.executeTakeFirstOrThrow ();
await db
.updateTable ('users' )
.set ({ name : 'Alice Updated' , updated_at : new Date () })
.where ('id' , '=' , 1 )
.execute ();
await db
.deleteFrom ('users' )
.where ('email' , 'like' , '%@spam.com' )
.execute ();
Advanced Query Patterns
Joins with Type Safety
const usersWithPosts = await db
.selectFrom ('users' )
.innerJoin ('posts' , 'posts.user_id' , 'users.id' )
.select ([
'users.id' ,
'users.name' ,
'posts.title' ,
'posts.content' ,
])
.execute ();
const usersWithOptionalPosts = await db
.selectFrom ('users' )
.leftJoin ('posts' , 'posts.user_id' , 'users.id' )
.select ([
'users.id' ,
'users.email' ,
'posts.title' ,
])
.execute ();
const complexQuery = await db
.selectFrom ('posts' )
.innerJoin ('users' , 'users.id' , 'posts.user_id' )
.leftJoin ('comments' , 'comments.post_id' , 'posts.id' )
.select ([
'posts.id as postId' ,
'posts.title' ,
'users.name as authorName' ,
'comments.id as commentId' ,
])
.execute ();
Aggregations and Grouping import { sql } from 'kysely' ;
const stats = await db
.selectFrom ('posts' )
.select ([
'user_id' ,
db.fn .count <number >('id' ).as ('post_count' ),
db.fn .avg <number >('views' ).as ('avg_views' ),
])
.groupBy ('user_id' )
.having (db.fn .count ('id' ), '>' , 5 )
.execute ();
const advanced = await db
.selectFrom ('users' )
.select ([
'users.id' ,
sql<number >`COUNT(DISTINCT posts.id)` .as ('total_posts' ),
sql<Date >`MAX(posts.created_at)` .as ('latest_post' ),
])
.leftJoin ('posts' , 'posts.user_id' , 'users.id' )
.groupBy ('users.id' )
.execute ();
Subqueries
const usersWithPostCount = await db
.selectFrom ('users' )
.select ([
'users.id' ,
'users.name' ,
(eb ) =>
eb
.selectFrom ('posts' )
.select (eb.fn .count <number >('id' ).as ('count' ))
.whereRef ('posts.user_id' , '=' , 'users.id' )
.as ('post_count' ),
])
.execute ();
const activeUsers = await db
.selectFrom ('users' )
.selectAll ()
.where ((eb ) =>
eb.exists (
eb
.selectFrom ('posts' )
.select ('id' )
.whereRef ('posts.user_id' , '=' , 'users.id' )
.where ('created_at' , '>' , new Date ('2024-01-01' ))
)
)
.execute ();
const usersInTopTier = await db
.selectFrom ('users' )
.selectAll ()
.where (
'id' ,
'in' ,
db.selectFrom ('posts' )
.select ('user_id' )
.groupBy ('user_id' )
.having (db.fn .count ('id' ), '>' , 100 )
)
.execute ();
Common Table Expressions (CTEs)
const result = await db
.with ('popular_posts' , (db ) =>
db
.selectFrom ('posts' )
.select (['id' , 'user_id' , 'title' ])
.where ('views' , '>' , 1000 )
)
.with ('active_users' , (db ) =>
db
.selectFrom ('users' )
.select (['id' , 'email' ])
.where ('last_login' , '>' , new Date ('2024-01-01' ))
)
.selectFrom ('popular_posts' )
.innerJoin ('active_users' , 'active_users.id' , 'popular_posts.user_id' )
.selectAll ()
.execute ();
interface OrgNode {
id : number ;
name : string ;
parent_id : number | null ;
level : number ;
}
const hierarchy = await db
.withRecursive ('org_tree' , (db ) =>
db
.selectFrom ('departments' )
.select (['id' , 'name' , 'parent_id' , sql<number >`0` .as ('level' )])
.where ('parent_id' , 'is' , null )
.unionAll (
db
.selectFrom ('departments' )
.innerJoin ('org_tree' , 'org_tree.id' , 'departments.parent_id' )
.select ([
'departments.id' ,
'departments.name' ,
'departments.parent_id' ,
sql<number >`org_tree.level + 1` .as ('level' ),
])
)
)
.selectFrom ('org_tree' )
.selectAll ()
.execute ();
Schema Generation from Database
Using kysely-codegen
npm install --save-dev kysely-codegen
npx kysely-codegen --url "postgresql://user:pass@localhost:5432/mydb"
import type { ColumnType , Generated } from 'kysely' ;
export interface Database {
users : UsersTable ;
posts : PostsTable ;
comments : CommentsTable ;
}
export interface UsersTable {
id : Generated <number >;
email : string ;
name : string | null ;
created_at : Generated <Date >;
}
export interface PostsTable {
id : Generated <number >;
user_id : number ;
title : string ;
content : string ;
published : Generated <boolean >;
created_at : Generated <Date >;
}
Custom Type Mapping
interface CustomTypes {
timestamp : Date ;
jsonb : unknown ;
numeric : string ;
uuid : string ;
}
interface ProductTable {
id : ColumnType <string , string | undefined , string >;
metadata : ColumnType <Record <string , unknown >, string , string >;
price : ColumnType <number , number , number | undefined >;
}
Migrations
Migration Setup import { Kysely , Migrator , FileMigrationProvider } from 'kysely' ;
import { promises as fs } from 'fs' ;
import * as path from 'path' ;
const migrator = new Migrator ({
db,
provider : new FileMigrationProvider ({
fs,
path,
migrationFolder : path.join (__dirname, 'migrations' ),
}),
});
async function migrateToLatest ( ) {
const { error, results } = await migrator.migrateToLatest ();
results?.forEach ((it ) => {
if (it.status === 'Success' ) {
console .log (`Migration "${it.migrationName} " executed successfully` );
} else if (it.status === 'Error' ) {
console .error (`Migration "${it.migrationName} " failed` );
}
});
if (error) {
console .error ('Migration failed:' , error);
process.exit (1 );
}
}
async function migrateDown ( ) {
const { error, results } = await migrator.migrateDown ();
}
Migration Files
import { Kysely , sql } from 'kysely' ;
export async function up (db : Kysely <any > ): Promise <void > {
await db.schema
.createTable ('users' )
.addColumn ('id' , 'serial' , (col ) => col.primaryKey ())
.addColumn ('email' , 'varchar(255)' , (col ) => col.notNull ().unique ())
.addColumn ('name' , 'varchar(255)' )
.addColumn ('created_at' , 'timestamp' , (col ) =>
col.defaultTo (sql`CURRENT_TIMESTAMP` ).notNull ()
)
.execute ();
await db.schema
.createIndex ('users_email_idx' )
.on ('users' )
.column ('email' )
.execute ();
}
export async function down (db : Kysely <any > ): Promise <void > {
await db.schema .dropTable ('users' ).execute ();
}
Complex Migration Examples
export async function up (db : Kysely <any > ): Promise <void > {
await db.schema
.createTable ('posts' )
.addColumn ('id' , 'serial' , (col ) => col.primaryKey ())
.addColumn ('user_id' , 'integer' , (col ) =>
col.references ('users.id' ).onDelete ('cascade' ).notNull ()
)
.addColumn ('title' , 'varchar(500)' , (col ) => col.notNull ())
.addColumn ('content' , 'text' )
.execute ();
}
export async function up (db : Kysely <any > ): Promise <void > {
await db.schema
.alterTable ('users' )
.addColumn ('bio' , 'text' )
.execute ();
await db.schema
.alterTable ('users' )
.modifyColumn ('email' , 'varchar(320)' )
.execute ();
}
export async function up (db : Kysely <any > ): Promise <void > {
await sql`CREATE TYPE user_role AS ENUM ('admin', 'user', 'guest')` .execute (db);
await db.schema
.alterTable ('users' )
.addColumn ('role' , sql`user_role` , (col ) => col.defaultTo ('user' ))
.execute ();
}
Transactions
Basic Transactions
await db.transaction ().execute (async (trx) => {
await trx
.insertInto ('users' )
.values ({ email : 'alice@example.com' , name : 'Alice' , updated_at : new Date () })
.execute ();
await trx
.insertInto ('posts' )
.values ({ user_id : 1 , title : 'First Post' , content : 'Hello' })
.execute ();
});
const trx = await db.transaction ().execute (async (trx) => {
const user = await trx
.insertInto ('users' )
.values ({ email : 'bob@example.com' , name : 'Bob' , updated_at : new Date () })
.returningAll ()
.executeTakeFirstOrThrow ();
const post = await trx
.insertInto ('posts' )
.values ({
user_id : user.id ,
title : 'Bob\'s Post' ,
content : 'Content' ,
})
.returningAll ()
.executeTakeFirstOrThrow ();
return { user, post };
});
Isolation Levels import { IsolationLevel } from 'kysely' ;
await db.transaction ()
.setIsolationLevel ('read committed' )
.execute (async (trx) => {
});
await db.transaction ()
.setIsolationLevel ('serializable' )
.execute (async (trx) => {
const balance = await trx
.selectFrom ('accounts' )
.select ('balance' )
.where ('id' , '=' , accountId)
.executeTakeFirstOrThrow ();
await trx
.updateTable ('accounts' )
.set ({ balance : balance.balance - amount })
.where ('id' , '=' , accountId)
.execute ();
});
Raw SQL Integration
Using sql Template Tag import { sql } from 'kysely' ;
const result = await db
.selectFrom ('users' )
.select ([
'id' ,
sql<string >`UPPER(name)` .as ('uppercase_name' ),
sql<number >`EXTRACT(YEAR FROM created_at)` .as ('year_created' ),
])
.execute ();
const filtered = await db
.selectFrom ('posts' )
.selectAll ()
.where (sql`LOWER(title)` , 'like' , '%typescript%' )
.execute ();
const custom = await sql<{ total : number ; avg_age : number }>`
SELECT
COUNT(*) as total,
AVG(EXTRACT(YEAR FROM age(birth_date))) as avg_age
FROM users
WHERE active = true
` .execute (db);
Full Raw Queries
const result = await sql`
WITH ranked_posts AS (
SELECT
p.*,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY views DESC) as rank
FROM posts p
)
SELECT * FROM ranked_posts WHERE rank <= 3
` .execute (db);
const email = 'alice@example.com' ;
const user = await sql<User >`
SELECT * FROM users WHERE email = ${email}
` .execute (db);
Plugin Ecosystem
JSON Operations (PostgreSQL) import { jsonBuildObject, jsonArrayFrom } from 'kysely/helpers/postgres' ;
const usersWithPosts = await db
.selectFrom ('users' )
.select ([
'users.id' ,
'users.name' ,
jsonArrayFrom (
db
.selectFrom ('posts' )
.select (['posts.id' , 'posts.title' , 'posts.content' ])
.whereRef ('posts.user_id' , '=' , 'users.id' )
).as ('posts' ),
])
.execute ();
const nested = await db
.selectFrom ('users' )
.select ([
'users.id' ,
jsonBuildObject ({
name : 'users.name' ,
email : 'users.email' ,
postCount : sql<number >`(SELECT COUNT(*) FROM posts WHERE user_id = users.id)` ,
}).as ('user_data' ),
])
.execute ();
Pagination Plugin import { SelectQueryBuilder } from 'kysely' ;
function paginate<DB , TB extends keyof DB , O>(
query : SelectQueryBuilder <DB , TB , O>,
page : number ,
pageSize : number
) {
return query.limit (pageSize).offset ((page - 1 ) * pageSize);
}
const page = 2 ;
const pageSize = 20 ;
const users = await paginate (
db.selectFrom ('users' ).selectAll (),
page,
pageSize
).execute ();
async function paginateWithCount<DB , TB extends keyof DB , O>(
query : SelectQueryBuilder <DB , TB , O>,
page : number ,
pageSize : number
) {
const [items, { count }] = await Promise .all ([
query.limit (pageSize).offset ((page - 1 ) * pageSize).execute (),
query.select (db.fn .count <number >('id' ).as ('count' )).executeTakeFirstOrThrow (),
]);
return {
items,
total : count,
page,
pageSize,
totalPages : Math .ceil (count / pageSize),
};
}
Full-Text Search (PostgreSQL)
export async function up (db : Kysely <any > ): Promise <void > {
await sql`
ALTER TABLE posts
ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
to_tsvector('english', coalesce(title, '') || ' ' || coalesce(content, ''))
) STORED
` .execute (db);
await sql`
CREATE INDEX posts_search_idx ON posts USING GIN (search_vector)
` .execute (db);
}
const searchResults = await db
.selectFrom ('posts' )
.selectAll ()
.where (
sql`search_vector` ,
'@@' ,
sql`to_tsquery('english', ${query} )`
)
.execute ();
Kysely vs Drizzle vs Prisma
Feature Comparison Feature Kysely Drizzle Prisma Type Safety Full (schema → queries) Full (schema → queries) Full (generated client) SQL Control ✅ Raw SQL friendly ✅ Raw SQL friendly ❌ Limited Bundle Size ~50kB ~30kB ~500kB+ Migration System ✅ Built-in ✅ Built-in ✅ Powerful CLI Query Performance ✅ Plain SQL ✅ Plain SQL ❌ Slower (abstraction) Schema Definition TypeScript types TypeScript schema Prisma schema Codegen Required Optional No ✅ Required ORM Features ❌ Query builder only Partial (relational) ✅ Full ORM Learning Curve Medium (SQL knowledge) Medium Easy (abstracts SQL) Best For SQL-first, complex queries Type-safe schemas Rapid prototyping
When to Choose Kysely
You know SQL and want full control
Complex queries (CTEs, window functions, subqueries)
Performance is critical (no ORM overhead)
Migrating from raw SQL
Need raw SQL escape hatch frequently
Working with existing databases
Bundle size matters (edge functions)
Want declarative TypeScript schemas
Need relational query capabilities
Prefer ORM-like ergonomics with SQL control
Working with new greenfield projects
Team unfamiliar with SQL
Rapid prototyping and iteration
Need powerful migration tooling
Want automatic relation handling
Prefer declarative schema language
Migration from Prisma
const users = await prisma.user .findMany ({
where : { createdAt : { gte : new Date ('2024-01-01' ) } },
include : { posts : true },
});
const users = await db
.selectFrom ('users' )
.select ([
'users.id' ,
'users.email' ,
jsonArrayFrom (
db.selectFrom ('posts' )
.selectAll ()
.whereRef ('posts.user_id' , '=' , 'users.id' )
).as ('posts' ),
])
.where ('created_at' , '>=' , new Date ('2024-01-01' ))
.execute ();
Best Practices
Define schema types first - Use Generated, Selectable, Insertable, Updateable
Use kysely-codegen - Generate types from existing databases
Leverage type inference - Let TypeScript infer result types
Use transactions - For multi-step operations
Raw SQL when needed - Don't fight the query builder
Paginate large results - Use LIMIT/OFFSET or cursor-based
Index frequently queried columns - Performance is your responsibility
Test migrations - Both up and down
Use CTEs for readability - Complex queries become maintainable
Connection pooling - Configure database pool appropriately
Common Pitfalls ❌ Forgetting to execute queries :
const users = db.selectFrom ('users' ).selectAll ();
const users = await db.selectFrom ('users' ).selectAll ().execute ();
❌ Not handling null from LEFT JOIN :
const result = await db
.selectFrom ('users' )
.leftJoin ('posts' , 'posts.user_id' , 'users.id' )
.select (['users.name' , 'posts.title' ])
.execute ();
❌ Missing Generated for auto-increment columns :
interface UserTable {
id : number ;
}
interface UserTable {
id : Generated <number >;
}
Resources
Related Skills When using Kysely, consider these complementary skills:
typescript-core : TypeScript type system, advanced patterns, and tsconfig optimization
database-migration : Safe schema evolution patterns for production databases
Node.js backend : Server setup, connection pooling, and database configuration
Quick TypeScript Type System Reference (Inlined for Standalone Use)
import { Kysely , Generated , ColumnType } from 'kysely' ;
interface Database {
users : {
id : Generated <number >;
email : string ;
created_at : ColumnType <Date , string | undefined , never >;
};
}
const db = new Kysely <Database >({ });
const users = await db
.selectFrom ('users' )
.select (['id' , 'email' ])
.where ('created_at' , '>' , new Date ('2025-01-01' ))
.execute ();
type SelectFields <T> = {
[K in keyof T]: T[K] extends ColumnType <infer S, any , any > ? S : T[K];
};
Quick Database Migration Patterns (Inlined for Standalone Use) Safe Migration Principles:
Backward compatible - New code works with old schema
Reversible - Can rollback migrations if needed
Zero downtime - No service interruption
Incremental - Small changes, not big-bang rewrites
Kysely Migration Example:
import { Kysely , sql } from 'kysely' ;
export async function up (db : Kysely <any > ): Promise <void > {
await db.schema
.alterTable ('users' )
.addColumn ('full_name' , 'varchar(255)' )
.execute ();
await db
.updateTable ('users' )
.set ({
full_name : sql`concat(first_name, ' ', last_name)`
})
.execute ();
}
export async function down (db : Kysely <any > ): Promise <void > {
await db.schema
.alterTable ('users' )
.dropColumn ('full_name' )
.execute ();
}
await db.schema
.createIndex ('idx_users_email' )
.on ('users' )
.column ('email' )
.execute ();
await db.schema
.alterTable ('users' )
.addColumn ('email_address' , 'varchar(255)' )
.execute ();
await db
.updateTable ('users' )
.set ({ email_address : sql`email` })
.execute ();
await db.schema
.alterTable ('products' )
.addColumn ('price_cents' , 'integer' )
.execute ();
await db
.updateTable ('products' )
.set ({ price_cents : sql`cast(price * 100 as integer)` })
.execute ();
import { Kysely , Migrator , FileMigrationProvider } from 'kysely' ;
import { promises as fs } from 'fs' ;
import path from 'path' ;
const migrator = new Migrator ({
db,
provider : new FileMigrationProvider ({
fs,
path,
migrationFolder : path.join (__dirname, 'migrations' ),
}),
});
const { error, results } = await migrator.migrateToLatest ();
await migrator.migrateUp ();
await migrator.migrateDown ();
const migrations = await migrator.getMigrations ();
[Full TypeScript patterns and migration workflows available in respective skills if deployed together]
Summary
Kysely is a type-safe SQL query builder, not an ORM
Full type inference from schema definitions to query results
Zero runtime overhead - compiles to plain SQL
Migration system included with up/down support
Raw SQL integration when query builder isn't enough
Plugin ecosystem for JSON, pagination, full-text search
Best for developers who know SQL and want type safety
Alternative to Prisma (full ORM) and Drizzle (schema-first)
Perfect for complex queries, existing databases, performance-critical apps