Skip to content

Instantly share code, notes, and snippets.

@vkartaviy
Created July 29, 2026 16:50
Show Gist options
  • Select an option

  • Save vkartaviy/3607e3da5111d8f659a50f84df6394f0 to your computer and use it in GitHub Desktop.

Select an option

Save vkartaviy/3607e3da5111d8f659a50f84df6394f0 to your computer and use it in GitHub Desktop.
MikroORM 7.1.8: migration:up rewrites snapshot with migration-owned external trigger

MikroORM external-trigger snapshot repro

Prerequisite: a disposable PostgreSQL server. Connection defaults are 127.0.0.1:5432, user/password postgres/postgres; override them with DB_HOST, DB_PORT, DB_USER, and DB_PASSWORD.

pnpm install
pnpm repro

The script creates and drops its own uniquely named database. On MikroORM 7.1.8 it reports that the migration snapshot changes from zero triggers to one after migrator.up(), then migrator.checkSchema() returns true although entity metadata did not change.

{
"name": "mikro-orm-external-trigger-snapshot-repro",
"private": true,
"scripts": {
"repro": "node repro.cjs"
},
"dependencies": {
"@mikro-orm/core": "7.1.8",
"@mikro-orm/migrations": "7.1.8",
"@mikro-orm/postgresql": "7.1.8",
"pg": "8.22.0"
}
}
const { createHash, randomBytes } = require('node:crypto');
const { mkdir, readFile, rm, writeFile } = require('node:fs/promises');
const path = require('node:path');
const { EntitySchema } = require('@mikro-orm/core');
const { Migrator } = require('@mikro-orm/migrations');
const { MikroORM } = require('@mikro-orm/postgresql');
const { Client } = require('pg');
const suffix = `${Date.now()}_${randomBytes(4).toString('hex')}`;
const databaseName = `mikro_orm_external_trigger_${suffix}`;
const outputDirectory = path.join(process.cwd(), `.repro-${suffix}`);
const snapshotPath = path.join(outputDirectory, '.snapshot-repro.json');
const connection = {
host: process.env.DB_HOST ?? '127.0.0.1',
password: process.env.DB_PASSWORD ?? 'postgres',
port: Number(process.env.DB_PORT ?? 5432),
user: process.env.DB_USER ?? 'postgres',
};
const Sample = new EntitySchema({
name: 'Sample',
tableName: 'sample',
properties: {
id: { type: 'number', primary: true },
},
});
async function main() {
await createDatabase();
await mkdir(outputDirectory);
const orm = await MikroORM.init({
...connection,
dbName: databaseName,
entities: [Sample],
extensions: [Migrator],
migrations: {
emit: 'cjs',
path: outputDirectory,
snapshot: true,
snapshotName: '.snapshot-repro',
},
});
try {
const migration = await orm.migrator.create();
const migrationPath = path.join(outputDirectory, migration.fileName);
const generatedCode = await readFile(migrationPath, 'utf8');
const migrationCode = generatedCode.replace(
'\n }\n\n async down() {',
`
this.addSql(\`
create function bump_sample_version()
returns trigger
language plpgsql
as $function$
begin
return null;
end;
$function$;
\`);
this.addSql(\`
create trigger sample_external_trigger
after insert or update or delete
on sample
for each statement
execute function bump_sample_version();
\`);
}
async down() {`
);
await writeFile(migrationPath, migrationCode);
const before = await inspectSnapshot();
await orm.migrator.up();
const after = await inspectSnapshot();
const migrationNeeded = await orm.migrator.checkSchema();
const result = { after, before, migrationNeeded };
console.log(JSON.stringify(result, undefined, 2));
if (
before.triggerCount !== 0 ||
after.triggerCount !== 1 ||
!migrationNeeded
) {
throw new Error('The expected regression was not reproduced');
}
} finally {
await orm.close(true);
await dropDatabase();
await rm(outputDirectory, { force: true, recursive: true });
}
}
async function inspectSnapshot() {
const content = await readFile(snapshotPath, 'utf8');
const snapshot = JSON.parse(content);
const triggerCount = snapshot.tables.reduce(
(count, table) => count + (table.triggers?.length ?? 0),
0
);
return {
hash: createHash('sha256').update(content).digest('hex'),
triggerCount,
};
}
async function createDatabase() {
await runAdminQuery(`create database "${databaseName}"`);
}
async function dropDatabase() {
await runAdminQuery(`drop database if exists "${databaseName}" with (force)`);
}
async function runAdminQuery(query) {
const client = new Client({ ...connection, database: 'postgres' });
await client.connect();
try {
await client.query(query);
} finally {
await client.end();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment