Skip to main content

api-database-knex

SQL query builder for PostgreSQL, MySQL, SQLite, and MSSQL -- fluent queries, schema builder, migrations, seeds, transactions, raw queries

معلومات المصدر

المستودع
majiayu000/claude-skill-registry-data
آخر نشاط في المصدر
٢٣ يونيو ٢٠٢٦ في ١١:٠٢
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٣
التفرعات
١٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
2 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
api-database-knex
description
SQL query builder for PostgreSQL, MySQL, SQLite, and MSSQL -- fluent queries, schema builder, migrations, seeds, transactions, raw queries
# Knex.js Patterns > **Quick Guide:** Use Knex.js (v3.x) as a SQL query builder for PostgreSQL, MySQL, SQLite, and MSSQL. Initialize the knex instance **once** per application (it creates a connection pool internally via tarn.js). Set pool `min: 0` so idle connections are released. Always use **parameterized bindings** (`?` for values, `??` for identifiers) in `knex.raw()` -- never interpolate user input. Wrap multi-table writes in `knex.transaction()` and always return or await the promise (otherwise the transaction hangs). Use `.returning()` on PostgreSQL/MSSQL for inserted/updated rows -- it is a no-op on MySQL/SQLite. Call `knex.destroy()` on graceful shutdown to drain the pool. --- <critical_requirements> ## CRITICAL: Before Using This Skill > **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants) **(You MUST initialize the knex instance ONCE per application and reuse it -- creating multiple instances leaks connection pools)** **(You MUST use parameterized bindings (`?` for values, `??` for identifiers) in ALL `knex.raw()` calls -- string interpolation causes SQL injection)** **(You MUST return or await the promise inside `knex.transaction()` handlers -- failing to do so causes the transaction connection to hang indefinitely)** **(You MUST call `knex.destroy()` on graceful shutdown -- orphaned pools prevent the Node.js process from exiting)** </critical_requirements> --- ## Examples - [Core Patterns](examples/core.md) -- Initialization, query builder, insert/update/delete, raw queries, TypeScript integration - [Schema & Migrations](examples/schema-migrations.md) -- Schema builder, createTable, alterTable, migrations, seeds - [Transactions & Advanced](examples/transactions-advanced.md) -- Transactions, batch insert, subqueries, connection pooling, multi-tenancy **Additional resources:** - [reference.md](reference.md) -- Query method cheat sheet, column types, pool options, anti-patterns, production checklist --- **Auto-detection:** Knex, knex, knexfile, knex.raw, knex.schema, knex.transaction, knex.migrate, knex.seed, batchInsert, query builder, schema builder, SQL query builder, knex.fn.now, knex.ref, knex.destroy, pg, mysql2, sqlite3, better-sqlite3 **When to use:** - Building SQL queries programmatically with a fluent API - Database schema creation and modification (createTable, alterTable) - Running and managing database migrations (up/down) - Seeding development/test databases - Wrapping multi-step database operations in transactions - Writing raw SQL with safe parameter binding - Batch inserting large datasets with chunking **Key patterns covered:** - Knex initialization with connection pool configuration - Fluent query builder (select, where, join, orderBy, groupBy, having) - Insert, update, delete with `.returning()` for PostgreSQL/MSSQL - Schema builder (createTable, alterTable, column types, indexes, foreign keys) - Migrations (knex migrate:make, up/down, transaction control) - Seeds (knex seed:make, seed:run) - Transactions with async/await and isolation levels - Raw queries with `?` value bindings and `??` identifier bindings - Subqueries as callbacks or builder instances - Batch insert with `batchInsert()` and chunking - TypeScript table type augmentation - Connection pool tuning (min, max, acquireTimeout, lifetime) **When NOT to use:** - You need a full ORM with model relationships, lifecycle hooks, and identity maps -- use your ORM solution instead - You need database-specific features Knex doesn't abstract (e.g., PostgreSQL LISTEN/NOTIFY, MySQL fulltext indexes) -- use `knex.raw()` for those - Your project already uses a different query layer or ORM and doesn't need a second one --- <philosophy> ## Philosophy Knex is a **SQL query builder**, not an ORM. The core principle: **you write SQL, Knex just makes it safer and more portable.** **Core principles:** 1. **One instance, one pool** -- Initialize knex once. The instance manages a connection pool (tarn.js). Never create multiple knex instances pointing at the same database. 2. **Parameterize everything** -- Use `?` bindings for values and `??` for identifiers. Never interpolate strings into queries. 3. **Migrations are the source of truth** -- Schema changes happen through migrations, not ad-hoc `knex.schema` calls in application code. 4. **Transactions for consistency** -- Any operation touching multiple tables or needing atomicity must be wrapped in `knex.transaction()`. 5. **Knex is dialect-aware, not dialect-hiding** -- Knex normalizes common SQL, but database-specific features (e.g., `.returning()` on PostgreSQL, `ON DUPLICATE KEY` on MySQL) must be handled per-dialect. </philosophy> --- <patterns> ## Core Patterns ### Pattern 1: Knex Initialization Initialize once per application. The knex instance manages a connection pool internally. See [examples/core.md](examples/core.md) for full examples. ```typescript // Good Example -- Proper initialization with pool tuning import knex from "knex"; const POOL_MIN = 0; const POOL_MAX = 10; const ACQUIRE_TIMEOUT_MS = 30_000; function createDatabase() { const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error("DATABASE_URL environment variable is required"); } return knex({ client: "pg", connection: connectionString, pool: { min: POOL_MIN, max: POOL_MAX }, acquireConnectionTimeout: ACQUIRE_TIMEOUT_MS, }); } export { createDatabase }; ``` **Why good:** Single instance, environment variable for connection string, pool min: 0 releases idle connections, named constants ```typescript // Bad Example -- Multiple instances, hardcoded config import knex from "knex"; function getUsers() { const db = knex({ client: "pg", connection: "postgres://localhost/mydb" }); return db("users").select("*"); // Connection pool leaked -- db.destroy() never called } ``` **Why bad:** Creates a new pool per call (leaks connections), hardcoded connection string, select("\*") fetches unnecessary columns --- ### Pattern 2: Query Builder Basics Fluent API for building SELECT queries. See [examples/core.md](examples/core.md) for joins, groupBy, having. ```typescript // Good Example -- Typed query with explicit columns const ACTIVE_STATUS = "active"; const PAGE_SIZE = 25; const users = await db<User>("users") .select("id", "name", "email") .where("status", ACTIVE_STATUS) .orderBy("created_at", "desc") .limit(PAGE_SIZE); ``` **Why good:** Explicit column selection, typed result, named constants for status and page size ```typescript // Bad Example -- select(*) with string interpolation const users = await db("users").select("*").whereRaw(`status = '${status}'`); // SQL INJECTION ``` **Why bad:** `select("*")` fetches unnecessary data, string interpolation in whereRaw creates SQL injection vulnerability --- ### Pattern 3: Insert / Update / Delete with Returning `.returning()` works on PostgreSQL, MSSQL, CockroachDB, and SQLite 3.35+. MySQL ignores it silently. See [examples/core.md](examples/core.md). ```typescript // Good Example -- Insert with returning (PostgreSQL) const [inserted] = await db("users") .insert({ name: "Alice", email: "alice@example.com" }) .returning(["id", "created_at"]); // Good Example -- Update with returning const [updated] = await db("users") .where("id", userId) .update({ name: newName, updated_at: db.fn.now() }) .returning(["id", "name", "updated_at"]); ``` **Why good:** `.returning()` avoids a separate SELECT, `db.fn.now()` uses database-native timestamp ```typescript // Bad Example -- Forgetting returning() on PostgreSQL await db("users").insert({ name: "Alice" }); // Returns [0] on PostgreSQL -- the row count, not the inserted data // Developer expects the inserted row but gets a useless number ``` **Why bad:** Without `.returning()`, PostgreSQL insert returns row count (not data), forcing an extra SELECT query --- ### Pattern 4: Raw Queries with Safe Bindings Use `?` for value bindings and `??` for identifier bindings. See [examples/core.md](examples/core.md). ```typescript // Good Example -- Parameterized raw query const MIN_ORDER_COUNT = 5; const results = await db.raw( `SELECT ??, COUNT(*) as order_count FROM ?? WHERE ?? > ? GROUP BY ?? HAVING COUNT(*) >= ?`, [ "users.id", "orders", "orders.created_at", cutoffDate, "users.id", MIN_ORDER_COUNT, ], ); ``` **Why good:** `??` for identifiers, `?` for values, all user input parameterized ```typescript // Bad Example -- String concatenation in raw query const results = await db.raw(`SELECT * FROM users WHERE name = '${name}'`); // SQL INJECTION: name = "'; DROP TABLE users; --" ``` **Why bad:** String interpolation allows SQL injection, attacker can execute arbitrary SQL --- ### Pattern 5: Transactions Wrap multi-step operations in transactions. Return or await the promise -- otherwise the connection hangs. See [examples/transactions-advanced.md](examples/transactions-advanced.md). ```typescript // Good Example -- Async/await transaction const result = await db.transaction(async (trx) => { const [order] = await trx("orders") .insert({ user_id: userId, total: amount }) .returning("id"); await trx("order_items").insert( items.map((item) => ({ order_id: order.id, ...item })), ); await trx("inventory") .whereIn( "product_id", items.map((i) => i.product_id), ) .decrement("quantity", 1); return order; }); // Transaction auto-commits on success, auto-rolls-back on thrown error ``` **Why good:** All operations atomic, auto-commit on success, auto-rollback on error, returns value from transaction ```typescript // Bad Example -- Forgetting to return/await inside transaction await db.transaction((trx) => { trx("orders").insert({ user_id: userId }); // NOT returned/awaited trx("items").insert({ order_id: 1 }); // NOT returned/awaited // Transaction handler returns undefined -- trx NEVER commits or rolls back // Connection hangs until acquireConnectionTimeout fires }); ``` **Why bad:** Without returning a promise, Knex cannot detect completion, transaction hangs indefinitely consuming a pool connection --- ### Pattern 6: Schema Builder Create and modify tables. Use in migrations, not application code. See [examples/schema-migrations.md](examples/schema-migrations.md). ```typescript // Good Example -- Migration creating a table export async function up(knex: Knex): Promise<void> { await knex.schema.createTable("orders", (table) => { table.increments("id").primary(); table .integer("user_id") .unsigned() .notNullable() .references("id") .inTable("users") .onDelete("CASCADE"); table.decimal("total", 10, 2).notNullable(); table .enum("status", ["pending", "paid", "shipped", "cancelled"]) .notNullable() .defaultTo("pending"); table.timestamps(true, true); // created_at, updated_at with defaults table.index(["user_id", "status"]); }); } export async function down(knex: Knex): Promise<void> { await knex.schema.dropTable("orders"); } ``` **Why good:** Foreign key with cascade, composite index, enum constraint, timestamps with defaults, reversible down migration </patterns> --- <decision_framework> ## Decision Framework ### Knex Method Selection ``` What kind of database operation? -- SELECT query -> db("table").select().where() -- INSERT -> db("table").insert(data).returning() -- UPDATE -> db("table").where().update(data).returning() -- DELETE -> db("table").where().del() -- Schema change -> db.schema.createTable() / .alterTable() (in migrations only) -- Complex SQL -> db.raw("SQL", bindings) -- Batch insert -> db.batchInsert("table", rows, chunkSize) -- Multi-table atomic write -> db.transaction(async (trx) => { ... }) ``` ### When to Use Raw Queries ``` Can the query builder express this? -- YES -> Use the query builder (portable, type-safe) -- NO -> Does it use database-specific syntax?
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub