name: database-skill description: Implement database-related changes in this repository, including schema changes via TypeORM entities, repository/query patterns, request-context transactions, DB tests, and explicit data migrations. Use when working on DB schema updates, repositories or *Db classes, SQL queries, transactions, data backfills, or app logic that reads or writes MySQL data.
Database Workflow
Apply this skill any time work involves database schema, queries, repositories, or migrations.
Environment
- Assume MySQL 8+ in all target environments.
- Assume Aurora MySQL in staging/production.
- Assume Docker MySQL locally.
Schema Changes
- Implement schema changes through TypeORM entities in
src/entities. - Keep entity file naming pattern:
- file name prefixed with
I, example:IMyThing.ts - class name without
Iand suffixed withEntity, example:MyThingEntity
- file name prefixed with
- Export every new entity from
src/entities/entities.ts. - Treat edits to existing entities as high risk and check for possible data loss before changing types or columns.
- Never use foreign keys.
- Prefer UUID primary keys unless sequential IDs are strictly required.
- Prefer
bigintUnix epoch milliseconds for time fields over SQLdatetime/date. - For every new
@Entity(TABLE_NAME), add or use a table constant insrc/constants/db-tables.ts. - Keep entity class/file names singular and table names plural.
Queries And Repositories
- Isolate DB access inside repository-style classes, usually
*Repositoryor*Db. - Use caller-level transactions only when work must span multiple repositories:
await sqlExecutor.executeNativeQueriesInTransaction(async (connection) => { const txCtx: RequestContext = { ...ctx, connection }; await firstRepository.doWork(txCtx); await secondRepository.doMoreWork(txCtx); }); - Make
ctx: RequestContextthe last argument of new repository functions unless the surrounding class has an established incompatible pattern. - Time new repository functions:
const timerName = `${this.constructor.name}->methodName`; try { ctx.timer?.start(timerName); // query work } finally { ctx.timer?.stop(timerName); } - Use
ctx.connectionwhen present so operations participate in caller-provided transactions:
For TypeORM transaction blocks, obtain repositories from the transaction manager passed by the transaction callback instead of the global data source.const rows = await this.db.execute<MyEntity>( `select * from ${MY_TABLE} where id = :id`, { id }, ctx.connection ? { wrappedConnection: ctx.connection } : undefined ); - Never use generated
Api*classes in repositories. - Allow callers to use entity classes and repository-defined types.
- Use constants from
src/constants/db-tables.tsinstead of hardcoded table names whenever possible. - Prefer typed queries via
execute<T>()andoneOrNull<T>(). - Always pass an explicit comparator to
Array.prototype.sort, including string sorts.
Data Migrations
- Use db-migrate only for explicit data migrations or view/one-off changes requested by the user.
- Avoid db-migrate for schema changes unless the user explicitly asks for a migration; schema/table changes should normally come from TypeORM entities and
dbMigrationsLoopsync. - Create migrations with:
npm run migrate:new migration-name - Edit files created under
migrations/. - Delete the generated
.down.sqlfile and leaveexports.downpresent as a no-op; do not implement revert logic.
Tests
Place DB/repository tests next to the file under test and name them with lowercase hyphenated words ending in .test.ts. For DB integration patterns, follow src/profiles/abusiveness-check.db.test.ts.
Validation
- Kept schema changes in entity classes unless the user explicitly requested a schema migration.
- Preserved entity naming convention and exported new entities.
- Checked entity edits for data-loss risk.
- Avoided foreign keys.
- Used UUID primary keys unless sequential ID was required.
- Used epoch-millis
bigintfor time where applicable. - Added or used table name constants in
src/constants/db-tables.ts. - Kept DB logic in repository or
*Dbclasses. - Used transaction wrapper only when spanning repositories.
- Passed
ctx: RequestContextthrough new repository APIs. - Timed repository methods and used
ctx.connectionwhen available. - Avoided generated
Api*models in repositories. - Used typed query methods and table constants in SQL.
- Used db-migrate only for explicit data/view migration work and left
exports.downas a no-op. - Added or updated focused tests for changed DB behavior.
- Ran
npm run lint.