Skip to main content

stacks-database

Use when working with databases in a Stacks application - configuring connections, running queries, migrations, seeding, SQL helpers, or using SQLite/Turso/MySQL/PostgreSQL/DynamoDB. Covers @stacksjs/database, bun-query-builder, config/database.ts, and the database/ migrations directory.

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

المستودع
stacksjs/stacks
آخر نشاط في المصدر
١٠ أكتوبر ٢٠٢٦ في ٢٠:٠٩
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٦٣٣
التفرعات
٢٠

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

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

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

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

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
stacks-database
description
Use when working with databases in a Stacks application - configuring connections, running queries, migrations, seeding, SQL helpers, or using SQLite/Turso/MySQL/PostgreSQL/DynamoDB. Covers @stacksjs/database, bun-query-builder, config/database.ts, and the database/ migrations directory.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript, SQLite >= 3.47.2
allowed-tools
Read Edit Write Bash Grep Glob
# Stacks Database ## Key Paths - Database package: `storage/framework/core/database/src/` - Configuration: `config/database.ts` - QB config: `config/query-builder.ts` - Migrations: `database/migrations/` (237 migration files, `.sql` format) - QB state: `.qb/` - ORM: `storage/framework/orm/` ## Source Files ``` database/src/ ├── database.ts # Database class + factory functions ├── driver-config.ts # Driver types, defaults, validation, env detection ├── defaults.ts # DB_HOST_DEFAULT, DB_PORTS, DB_NAMES, DB_USERS constants ├── utils.ts # Lazy `db` proxy (main query builder entry point) ├── types.ts # sql template tag, Generated/Insertable/Updateable types ├── sql-helpers.ts # Cross-dialect helpers (now/boolTrue/param/etc.) ├── migrations.ts # runDatabaseMigration, resetDatabase, generateMigrations ├── seeder.ts # seed, seedModel$, freshSeed, listSeedableModels ├── validators.ts # Column type inference from validator types ├── column.ts # Column definition helpers ├── schema.ts # Schema definition helpers ├── table.ts # Table definition helpers ├── query-parser.ts # Query parsing utilities ├── query-logger.ts # Query logging/monitoring ├── auth-tables.ts # Auth-related table migrations (OAuth, passkeys) ├── custom/ # Custom migrations (jobs.ts, errors.ts) ├── drivers/ # sqlite.ts, mysql.ts, postgres.ts, dynamodb.ts │ └── defaults/ # Default migration helpers (traits.ts, passwords.ts) └── index.ts # Re-exports everything ``` ## Database Class (database.ts) ```typescript const db = new Database(options: DatabaseOptions) db.driver // 'sqlite' | 'mysql' | 'postgres' db.connection // DatabaseConnectionConfig db.isInitialized db.query // QueryBuilder (lazy-initialized via createQueryBuilder()) db.initialize() // Calls setConfig() + createQueryBuilder() from bun-query-builder db.switchDriver(driver, connection) // Close + reinitialize with new driver db.close() // Close connection, reset state // Static factories Database.fromConfig(config, env?) // From stacks config object Database.fromEnv() // From env vars (DB_CONNECTION, DB_DATABASE, etc.) ``` ## Factory Functions (database.ts) - `createDatabase(options: DatabaseOptions): Database` - `createSqliteDatabase(database: string, options?): Database` - `createTursoDatabase(url: string, authToken?: string, options?): Database` - `createPostgresDatabase(connection: DatabaseConnectionConfig, options?): Database` - `createMysqlDatabase(connection: DatabaseConnectionConfig, options?): Database` ## Driver Configuration (driver-config.ts) - `detectDriver(): SupportedDialect` -- checks `DB_CONNECTION` env, then `DATABASE_URL` prefix, defaults to `'sqlite'` - `validateDriverConfig(driver, config): { valid: boolean, errors: string[] }` - `mergeWithDefaults(driver, config): Config` - `getConfigFromEnv(driver): Config` -- reads `DB_DATABASE`, `DB_HOST`, `DB_PORT`, `DB_USERNAME`, `DB_PASSWORD`, `DB_PREFIX`, `DB_SCHEMA` - `getConnectionString(driver, config): string` -- builds `sqlite://`, `mysql://`, `postgres://` URL ## Global `db` Instance (utils.ts) The db export is a lazy runtime facade. It initializes the query builder on use and resolves application configuration through ensureDatabaseConfigLoaded. Use @stacksjs/database/runtime for the narrow query/transaction entry; the package root additionally exports migrations, seeders and driver tooling. ```typescript import { db } from '@stacksjs/database' // db auto-initializes here const users = await db.selectFrom('users').where('active', '=', true).get() ``` `initializeDbConfig(config)` can be called to update the backing config at runtime. For reads which cannot tolerate replication lag, use `db.primary.selectFrom(...)`. It stays on the primary when automatic replica routing is enabled, and uses the active transaction connection inside a transaction. It does not mark the request as a writer or change routing for unrelated reads. Session authentication uses this handle so a revoked session cannot authenticate from a stale replica. ## SQL Template Tag (types.ts) ```typescript import { sql } from '@stacksjs/database' const query = sql`SELECT * FROM users WHERE id = ${userId}` // Returns: { sql: 'SELECT * FROM users WHERE id = ?', parameters: [userId] } sql.raw('NOW()') // Raw SQL (NOT parameterized) -- returns { raw: 'NOW()' } sql.ref('users.name') // Column reference -- returns { raw: 'users.name' } ``` How it works: template values are replaced with `?` placeholders and collected into `parameters[]`. Values wrapped in `sql.raw()` or `sql.ref()` are inlined directly into the SQL string. ## SQL Dialect Helpers (sql-helpers.ts) ```typescript import { sqlHelpers } from '@stacksjs/database' const h = sqlHelpers('sqlite') // or 'mysql' or 'postgres' h.isPostgres // false h.isMysql // false h.isSqlite // true h.now // "datetime('now')" for sqlite, 'NOW()' for mysql/postgres h.boolTrue // '1' for sqlite/mysql, 'true' for postgres h.boolFalse // '0' for sqlite/mysql, 'false' for postgres h.autoIncrement // 'INTEGER' for sqlite, 'SERIAL' for postgres h.primaryKey // 'PRIMARY KEY AUTOINCREMENT' | 'PRIMARY KEY AUTO_INCREMENT' | 'PRIMARY KEY' h.param(1) // '?' for sqlite/mysql, '$1' for postgres h.params('a', 'b') // { sql: '?, ?', values: ['a', 'b'] } or { sql: '$1, $2', values: ['a', 'b'] } ``` ## Connection Defaults (defaults.ts) ```typescript DB_HOST_DEFAULT = '127.0.0.1' DB_PORTS = { mysql: 3306, postgres: 5432, sqlite: 0 } DB_NAMES = { default: 'stacks', sqlitePath: 'database/stacks.sqlite', sqliteTestingPath: 'database/stacks_testing.sqlite' } DB_USERS = { mysql: 'root', postgres: 'postgres', sqlite: '' } REDIS_DEFAULTS = { host: 'localhost', port: 6379 } AWS_DEFAULTS = { region: 'us-east-1' } getConnectionDefaults(driver: string, envProxy?): ConnectionDefaults ``` ## DatabaseOptions Type (database.ts) ```typescript interface DatabaseOptions { driver: 'sqlite' | 'mysql' | 'postgres' connection: { database: string, host?: string, port?: number, username?: string, password?: string, url?: string } verbose?: boolean timestamps?: { createdAt?: string, updatedAt?: string, defaultOrderColumn?: string } softDeletes?: { enabled?: boolean, column?: string, defaultFilter?: boolean } hooks?: QueryBuilderConfig['hooks'] } ``` ## Connection Types (driver-config.ts) ```typescript interface SqliteConfig { database: string, prefix?: string } interface TursoConfig { url: string, authToken?: string, prefix?: string } interface MysqlConfig { name: string, host?: string, port?: number, username?: string, password?: string, prefix?: string, charset?: string, collation?: string } interface PostgresConfig { name: string, host?: string, port?: number, username?: string, password?: string, prefix?: string, schema?: string, sslMode?: 'disable' | 'require' | 'verify-ca' | 'verify-full' } interface DynamoDbConfig { key: string, secret: string, region?: string, prefix?: string, endpoint?: string, tableName?: string, singleTable?: { enabled?, pkAttribute?, skAttribute?, entityTypeAttribute?, keyDelimiter?, gsiCount? } } ``` ## Model-driven migrations Change the model, generate migrations, review the resulting SQL, then migrate. The native runner loads application config, selects the dialect corpus, handles feature-gated migration resources, and acquires the migration lock. It returns a Result; inspect failures rather than treating a resolved call as success. RunDatabaseMigration, generateMigrations and resetDatabase are exported tooling, but their CLI actions supply the application's operation policy and environment. Read stacks-migrations before using full regeneration, snapshot recovery, preprocessing or destructive fresh/reset commands. SQLite preprocessing and provider-specific DDL rules are implemented in core/database/src/migrations.ts; installed package sources are copied before their SQL is rewritten. The migration ledger and snapshot are different state, not interchangeable proof that a table exists. ## Model factories and application seeders Models opt into the factory pass with traits.useSeeder/seedable. Attribute factories receive faker and the attributes generated so far; column names are converted to snake_case, and password fields are hashed by the seeder's bcrypt path. The source defines factory fallback behavior: a failed factory can use its attribute default, so inspect the summary and warnings as well as row count. seed(options?) returns a SeedSummary; seedModel$(name, options?) targets one model; freshSeed truncates before generation; listSeedableModels inspects the available definitions; makeModelRecords produces factory data without insertion. These are public exports from @stacksjs/database. When app/Models has user models, the default seed pass uses those definitions. includeDefaults explicitly merges framework defaults; an app with no user models uses defaults as fallback. User identity overrides a default even when the app model opts out of seeding. The current model-seeder app scan is flat, unlike recursive model resolution elsewhere; verify nested models at this seam. Options also include only/except, defaultCount, fresh, append, allowProtected, attachAccounts and includeDefaults. Default non-empty tables are skipped; append is explicit. Protected auth/OAuth data and attachment to existing accounts have their own guards because fixture generation can alter live credentials or associate invented records with real people. Read buddy seed --help and inspect the target connection before requesting a destructive or bypassing mode. Application seeders default-export a class extending the native Seeder: ~~~ts import { Seeder } from '@stacksjs/database' export default class WorkspaceSeeder extends Seeder { static override order = 10 static override tags = ['deploy'] async run(): Promise<void> { // Find the application's existing workspace before creating missing state. } } ~~~ runApplicationSeeders applies order (lower first), then relative path for ties. Tag/only/except selection lets deploy bootstrap remain separate from a large demo corpus. Buddy seed and migrate:fresh --seed run the model and application passes; model dependencies determine parent-first generation rather than a hard-coded User/Team/Project order. fresh empties in reverse dependency order. Source: storage/framework/core/database/src/seeder.ts and core/buddy/src/commands/seed.ts. Retained seeder tests cover override opt-out, dependency order, protected models and non-empty/append behavior. See stacks-models for fixtures/factory declarations and stacks-testing for factories that produce test records. ## Validator Type Guards (validators.ts) `isStringValidator`, `isNumberValidator`, `enumValidator`, `isBooleanValidator`, `isDateValidator`, `isUnixValidator`, `isFloatValidator`, `isDatetimeValidator`, `isTimestampValidator`, `isTimestampTzValidator`, `isDecimalValidator`, `isSmallintValidator`, `isIntegerValidator`, `isBigintValidator`, `isBinaryValidator`, `isBlobValidator`, `isJsonValidator` - `checkValidator(validator, driver): string` -- converts validator type to SQL column type string (e.g., `'integer'`, `'text'`, `'varchar(255)'`) - SQLite uses `'text'` for all strings, `'integer'` for numbers; MySQL uses `'varchar(N)'`, native `enum()` ## DynamoDB Support (drivers/dynamodb.ts) Entity-centric API for single-table design: - `createDynamo(config)`, `dynamo` (default instance) - `EntityQueryBuilder` -- query builder for DynamoDB entities - `generateKeyPattern`, `parseKeyPattern`, `buildKey` -- key pattern utilities - `marshall`, `unmarshall` -- DynamoDB data type conversion ## Re-exports from bun-query-builder - `createQueryBuilder`, `setConfig` -- core QB functions - `QueryBuilder`, `QueryBuilderConfig`, `Seeder`, `SupportedDialect` -- types ## Compatibility Type Aliases (types.ts) - `Generated<T>`, `GeneratedAlways<T>` -- column generation markers (both alias to `T`) - `Insertable<T>`, `Selectable<T>`, `Updateable<T>` -- CRUD type utilities - `RawBuilder<T>`, `Sql` -- raw SQL expression types ## CLI Commands - `buddy migrate` -- run pending migrations - `buddy migrate:fresh` -- drop all + re-migrate (add `--seed` to also seed) - `buddy make:migration <name>` -- create migration file - `buddy seed` -- seed database - `buddy generate:migrations` -- generate migration diffs from models ## Turso / libSQL (`DB_CONNECTION=turso`, alias `libsql`) SQLite's SQL on a Turso or `sqld` server. Configure `TURSO_DATABASE_URL` (`libsql://<db>-<org>.turso.io`, or `http://127.0.0.1:8080` for `turso dev`) and `TURSO_AUTH_TOKEN` (a secret, never logged). - Everything that renders SQL or DDL treats it as `sqlite`: `dialectCapabilities('turso').wire === 'sqlite'`, `getDatabaseDialect()` returns `'sqlite'`, and it shares the SQLite migration corpus and `model-snapshot.sqlite.json`. Switching between `sqlite` and `turso` needs no regeneration. - The transport is bun-query-builder's: `setConfig({ dialect: 'sqlite', database: { url, authToken } })` with a libSQL URL selects its dependency-free Hrana-over-HTTP connection (`createLibSQLSQL`). - Code that opens the database FILE must ask `isLibsqlDriver(driver)` first - there is none. The SQLite migration preprocessor reads an in-memory replica of the server's schema instead (`loadLibsqlSchemaMirror` in migrations.ts), and the migration lock is a row in `stacks_migration_lock` (`acquireLibsqlMigrationLock`), since migrating hosts share no filesystem. - libSQL rejects SQLite's double-quoted-string fallback, so a statement naming a missing column fails instead of storing the column name as text. `tests/corpus-rebuild-sources.test.ts` guards the corpus. - Inside `transaction()`, write through the `tx` handle; model writes use their own connection. ## config/database.ts Shape ```typescript { default: env.DB_CONNECTION || 'sqlite', connections: { sqlite, turso, mysql, singlestore, vitess, postgres, dynamodb }, migrations: 'migrations', migrationLocks: 'migration_locks', queryLogging: { // Defaults on outside production and off in production. Production also // skips query hooks unless persistent history is explicitly enabled, // except one onQueryError hook on PostgreSQL and MySQL that reports a // pool broken by oven-sh/bun#42804. enabled: env.DB_QUERY_LOGGING_ENABLED ?? !['production', 'prod'].includes(env.APP_ENV || ''), captureAllTraces: false, // slow and failed queries always keep traces // Bound values in query_logs.bindings, credentials stored as `<redacted>` // (by name and shape, so not every secret); production keeps only each // value's type unless this is enabled. Env takes true/false, 1/0, yes/no, on/off.
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub