n8n/packages/@n8n/db/AGENTS.md
Declan Carroll 6f365bf3c8
ci: Enforce migration ordering in code-health rule (no-changelog) (#30511)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 10:12:55 +00:00

2.0 KiB

AGENTS.md

Extra information specific to the @n8n/db package.

Creating Migrations

Migration files are named {TIMESTAMP}-{DescriptiveName}.ts. The timestamp must be strictly greater than every existing migration timestamp in this package (across common/, postgresdb/, and sqlite/). TypeORM runs unrecorded migrations in timestamp order, so inserting a value below the current max corrupts ordering on databases that have already executed the later migrations.

Use the generator — it picks a safe timestamp, writes the scaffold, and registers the migration in the relevant index.ts files:

pnpm --filter=@n8n/db migration:new <Name> [--folder=common|postgresdb|sqlite]

<Name> is PascalCase and describes the change (e.g. AddTracingToExecution). --folder defaults to common; use postgresdb or sqlite only for dialect-specific migrations. The generator picks Date.now() when it's greater than the current head, otherwise max + 1.

The migration-timestamp rule in @n8n/code-health enforces both invariants (strict ordering and no far-future fabrication) at lint time; the generator is the easy path, the rule is the safety net.

Migration DSL

UUID Primary Keys

Do not use autoGenerate or autoGenerate2 on UUID columns. Both cause TypeORM to emit DEFAULT uuid_generate_v4() in PostgreSQL, which requires the uuid-ossp extension in the public schema. This fails on managed Postgres services like Supabase where the extension lives in a different schema.

Instead, generate UUIDs at the application level:

Migration:

column('id').uuid.primary.notNull,

Entity:

import { randomUUID } from 'node:crypto';
import { BeforeInsert, PrimaryColumn } from '@n8n/typeorm';

@PrimaryColumn('uuid')
id: string;

@BeforeInsert()
generateId() {
  if (!this.id) {
    this.id = randomUUID();
  }
}

autoGenerate / autoGenerate2 are fine for integer columns (they use serial / GENERATED BY DEFAULT AS IDENTITY respectively).