Skip to content

trymoto/nestjs-neo4j-migrations

Repository files navigation

Neo4j migrations manager for NestJs

This package is inspired by TypeORM style of migration management. It tracks migrations directly inside neo4j database and runs them in order.

Disclaimers

  • WIP, lacks tests and not ready for production use
  • Beware that Neo4j migrations cannot have both schema and data changes in the same migration!
  • CLI for reverting is lacking but functional
  • No generation of migrations yet

Shoutouts

Installation

npm i nestjs-neo4j-migrations

Usage

In static modules

import { Global, Module } from '@nestjs/common';
import { Neo4jDriverModule } from 'nestjs-neo4j-migrations';

import { migrations } from './migrations';

@Global()
@Module({
  imports: [
    Neo4jDriverModule.forRoot({
      uri: process.env.NEO4J_URI,
      username: process.env.NEO4J_USERNAME,
      password: process.env.NEO4J_PASSWORD,
      migrations,
    }),
  ],
})
export class Neo4jModule {}

In dynamic modules

import { Global, Module } from '@nestjs/common';
import { Neo4jDriverModule } from 'nestjs-neo4j-migrations';

import { Config } from './config';

import { migrations } from './migrations';

@Module({
  imports: [
    Neo4jDriverModule.forRootAsync({
      inject: [Config],
      useFactory: (config: Config) => ({
        uri: config.neo4jUri,
        username: config.neo4jUser,
        password: config.neo4jPassword,
        migrations,
      }),
    }),
  ],
})
export class Neo4jModule {}

Migrations file

This acts as datasource options file, it should export an array of migrations (see example of one below), uri, username and password.

// migrations/index.ts

import dotenv from 'dotenv';
import { Neo4jMigrationList } from 'nestjs-neo4j-migrations';

import { InitMigration1234 } from './1234-Init';

dotenv.config();

export const migrations: Neo4jMigrationList = [
  InitMigration1234,
  // ... append more migrations here
];

export const uri = process.env.NEO4J_URI;
export const username = process.env.NEO4J_USERNAME;
export const password = process.env.NEO4J_PASSWORD;

Migration example

Note that key can be any sequential number, but it is recommended to use timestamp for it. Make sure your up() and down() methods are idempotent. Use IF NOT EXISTS and IF EXISTS to avoid errors when running migrations multiple times.

Note: session is exposed to allow multi-transactional migrations on large datasets, when memory is a concern. In most cases you'll want to use single transaction. Learn more here https://neo4j.com/docs/javascript-manual/current/transactions/

import { Neo4jMigration, Neo4jSession } from 'nestjs-neo4j-migrations';

export class AddIndex1723704012000 implements Neo4jMigration {
  readonly key = 1723704012000;

  async up(session: Neo4jSession): Promise<void> {
    await session.executeWrite((trx) =>
      trx.run(`CREATE INDEX ageId IF NOT EXISTS FOR (e:employees) ON (e.age)`),
    );

    // Beware that if you change schema (indexes, constraints, etc) you cannot have data changes in the same migration, you'll have to split them.
  }

  async down(session: Neo4jSession): Promise<void> {
    const trx = session.beginTransaction();
    await trx.run(`DROP INDEX ageId IF EXISTS`);
    await trx.commit();
  }
}

Reverting

npx nestjs-neo4j-migrations revert -c ./migrations

In this example migrations directory contains index.ts file described above.

About

Neo4j migration management for NestJs applications

Resources

License

Stars

Watchers

Forks

Packages

No packages published