Aller au contenu

Reference — TypeORM → Kysely migration

Complements api-conventions.mdc § Stack Kysely investisseur.

Foundation

Piece Location
Kysely / TypeORM tx ky-safe-pg-transaction.ts, safe-pg-transaction.postgres.ts
DB registry database.tsDatabase; add KyselyDb once if missing, then import
Parse ky_parseRows / ky_parseFirstRow / ky_parseOneRow

Do not reintroduce: InvestorDatabase, dispatch wrappers, queryRowsWithTypeorm, per-repo type KyselyDb.

Example merged PRs

Ticket Pattern
BRI-890 UUID PK; dual tx (legacy positional inserts — see skill)
BRI-886 PG enum; batch UPDATE without RETURNING
BRI-887 Serial PK; business/

Schema templates

Row = z.infer<typeof row>. UUID PK → T = Row. Serial / JSONB + generated → KyselyTableFromZodType.

JSONB + generated timestamps:

export const metadata = z.object({ source: z.enum(['admin', 'system']), recordedAt: isoDate_zod })
export const row = z.object({ id, customerId: uuid_zod, metadata, createdAt: z.date(), updatedAt: z.date() })
export const insertRow = z.object({ id, customerId: uuid_zod, metadata })
export type T = KyselyTableFromZodType<typeof row, 'createdAt' | 'updatedAt', 'metadata'>

Serial PKT = KyselyTableFromZodType<typeof row, 'id'>. PG enumz.enum + .enum.{value}. FK brandedz.string().uuid().

Repository pattern

import { getKysely, type KyselyDb } from 'src/__new/lib/kysely/database'

async findOneById(params: { id: ExamplePgSchema.Id }) { /* getKysely()… */ }
async insertOneWithKysely(params: { trx: KyselyDb; row: ExamplePgSchema.Row }) { /* trx.insertInto */ }
async insertOneWithTypeorm(params: { transac: EntityManager; row: ExamplePgSchema.Row }) { /* query */ }
async findOneByIdWithTypeorm(params: { transac: EntityManager; id: ExamplePgSchema.Id }) { /* … */ }
async updateOneWithKysely(params: { trx: KyselyDb; id; patch }) { /* updateTable */ }

trx = Kysely, transac = TypeORM. Inserts always params object.

Transactions

One stack per tx. ky_safePgTransaction*WithKysely. safePgTransaction*WithTypeorm.

Integration tests

Test-first: legacy create + read → commit → migrate → same assertions. Seeds: insertOneWithKysely({ trx: getKysely(), row }).

Teardown

Delete entity + legacy repo; remove from index.ts, forFeature, all-entities.ts. Logic → {module}/business/.

Pitfalls

Issue Fix
Local type KyselyDb per repo Import from database.ts
Positional insert (trx, row) params: { trx, row } / { transac, row }
T hand-written with Generated / Jsonb KyselyTableFromZodType
trx on TypeORM / transac on Kysely trx = Kysely only, transac = TypeORM only
Mixing stacks in one tx Match repo method to parent tx
Migration without create+read test Test legacy path first

Grep hints

KYC: customers-lemonway, investor-onboarding. Services: customers/services/investor-*. Admin: administration/services/. Scripts: scripts/.