# Typeorm

> TypeORM entity, relation, repository, query, and configuration conventions shared across engines. Load when designing entities, relations, queries, or TypeORM setup; load the matching per-engine skill alongside it.

- Skill: `carlosferorduna/typeorm` (Agent Skill)
- Install (CLI): `npx skillmds@latest add carlosferorduna/typeorm`
- Raw SKILL.md: https://api.skillmd.com/api/skills/carlosferorduna/typeorm/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: CarlosFerOrduna (https://skillmd.com/u/carlosferorduna)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/carlosferorduna/typeorm

---


# TypeORM

Portable TypeORM mapping and API conventions. Engine-specific decisions
(identifiers, primary keys, logical-column types, index syntax) live in
dedicated per-engine skills; load the one that matches the project's database,
determined by the `type:` field of its TypeORM DataSource (`mssql` →
`typeorm-mssql`, `postgres` → `typeorm-pg`). Add a new per-engine skill the day
a project runs on that engine.

## Entities

- Every column has an explicit type as the first positional argument and an explicit `name:`: `@Column('varchar', { name: 'email', length: 50 })`.
- Logical columns are manual and live in SQL (see the engine skill for exact types). Do not introduce `@CreateDateColumn` / `@UpdateDateColumn` / `@DeleteDateColumn` where existing entities do not already use them.
- Relations are explicit: `@ManyToOne(() => UserEntity, user => user.createdProfiles)` with `@JoinColumn([{ name: 'createdBy', referencedColumnName: 'id' }])`; FK columns are named in the engine's identifier style (`<entity>Id` on MSSQL, `<entity>_id` on Postgres).
- No `cascade` or lazy loading unless the workflow requires it.
- Repeated references to the same entity get intent-named inverse properties (`createdByUser` / `updatedByUser`). Legacy entities generated by `typeorm-model-generator` keep the numbered form (`users`, `users2`); do not introduce new numbered names.
- Declare indexes exactly as the SQL does, spelling out the same `where:` condition: `@Index('UQ_player_email', ['email'], { unique: true, where: '...' })`. Constraint and index names are explicit (`PK_`, `UQ_`, `IX_`, `FK_`).
- Store TS enums as varchar with a literal default: `@Column('varchar', { name: 'kycStatus', length: 20, default: () => `'${KycStatus.NOT_STARTED}'` })`. Keep enum values synchronized across TS, SQL, and seeds. The interpolated values must be simple identifiers, never expressions or runtime values.

## Repositories

- Register entities per feature module with `TypeOrmModule.forFeature([...])` and inject them directly: `@InjectRepository(Entity)`.
- Use the project's repository layer when the project has one and keep new repository code inside that layer's conventions; `@InjectRepository(Entity)` is the default only when that layer does not exist.
- Write a custom repository when there is real query logic. Extend `Repository<Entity>` with an explicit wiring constructor and expose intent-named methods (`findResumable`, `applyTerminal`) built on the project layer, `find*` options, or the query builder.

## Queries and transactions

- Prefer the `find*` options API; reserve `createQueryBuilder` for shapes it cannot express (raw projections, engine-specific output/returning).
- Paginate with `{ take, skip }`.
- Simple transactions use `dataSource.transaction(async (manager) => { ... })`; when several repositories must join one transaction, thread an explicit `QueryRunner` through the calls and run queries through `manager`.

## Configuration

- `synchronize: false` — TypeORM never mutates the schema.
- `autoLoadEntities: true` and no `entities:` / `migrations:` globs; migrations are versioned SQL applied by the project runner (see the `database` skill).

