Morpheus is a modern, open-source database migration tool for Neo4j. It is designed to be a simple, intuitive tool for managing database migrations. The project is inspired by Michael Simons' tool for Java.
npm install -g morpheus4j
morpheus init # Create config file
morpheus create user-nodes # Creates V1_0_0__user-nodes.cypher
morpheus migrate # Run migrations
Migration files:
.cypher
extensionV1_0_0__create_users.cypher
)Example migration file V1_0_0__create_users.cypher
:
CREATE CONSTRAINT user_email IF NOT EXISTS FOR (u:User) REQUIRE u.email IS UNIQUE;
CREATE (u:User {
email: 'admin@example.com',
name: 'Admin User',
created_at: datetime()
});
Migration files follow this pattern:
V
(for version)1_0_0
)__
.cypher
Example: V1_0_0__create-user-constraints.cypher
Morpheus supports the following environment variables:
MORPHEUS_HOST
- Neo4j hostMORPHEUS_PORT
- Neo4j portMORPHEUS_SCHEME
- Neo4j schemeMORPHEUS_USERNAME
- Neo4j usernameMORPHEUS_PASSWORD
- Neo4j passwordMORPHEUS_DATABASE
- Neo4j database nameMORPHEUS_MIGRATIONS_PATH
- Path to migrations directory$ npm install -g morpheus4j
$ morpheus COMMAND
running command...
$ morpheus (--version)
morpheus4j/4.2.0 linux-x64 node-v20.13.1
$ morpheus --help [COMMAND]
USAGE
$ morpheus COMMAND
...
morpheus autocomplete [SHELL]
morpheus clean
morpheus create NAME
morpheus info
morpheus init
morpheus migrate
morpheus autocomplete [SHELL]
Display autocomplete installation instructions.
USAGE
$ morpheus autocomplete [SHELL] [-r]
ARGUMENTS
SHELL (zsh|bash|powershell) Shell type
FLAGS
-r, --refresh-cache Refresh cache (ignores displaying instructions)
DESCRIPTION
Display autocomplete installation instructions.
EXAMPLES
$ morpheus autocomplete
$ morpheus autocomplete bash
$ morpheus autocomplete zsh
$ morpheus autocomplete powershell
$ morpheus autocomplete --refresh-cache
See code: @oclif/plugin-autocomplete
morpheus clean
Clean up migration-related database objects
USAGE
$ morpheus clean [--json] [--debug] [--drop-constraints] [-c <value>] [-m <value>] [-h <value>] [-p
<value>] [-s <value>] [-P <value>] [-u <value>] [-d <value>]
FLAGS
-P, --password=<value> Neo4j password. Env: 'MORPHEUS_PASSWORD'
-c, --configFile=<value> Path to the morpheus file. ./morpheus.json by default
-d, --database=<value> Neo4j database. Env: 'MORPHEUS_DATABASE'
-h, --host=<value> Neo4j host. Env: 'MORPHEUS_HOST'
-m, --migrationsPath=<value> Migrations path. Env: 'MORPHEUS_MIGRATIONS_PATH'
-p, --port=<value> Neo4j port. Env: 'MORPHEUS_PORT'
-s, --scheme=<value> Neo4j scheme. Env: 'MORPHEUS_SCHEME'
-u, --username=<value> Neo4j username. Env: 'MORPHEUS_USERNAME'
--drop-constraints Additionally remove all Morpheus-related database constraints
GLOBAL FLAGS
--debug Enable debug logging
--json Format output as json.
DESCRIPTION
Clean up migration-related database objects
Removes all Morpheus migration metadata including nodes, relationships, and optionally constraints.
Use with caution as this will reset the migration history.
EXAMPLES
$ morpheus clean
$ morpheus clean --drop-constraints
$ morpheus clean --config ./custom-config.json
See code: src/commands/clean.ts
morpheus create NAME
Generate a new timestamped migration file with boilerplate code
USAGE
$ morpheus create NAME [--json] [-c <value>] [-m <value>]
ARGUMENTS
NAME Name of the migration (will be prefixed with a semver number)
FLAGS
-c, --configFile=<value> Path to the morpheus file. ./morpheus.json by default
-m, --migrationsPath=<value> Migrations path. Env: 'MORPHEUS_MIGRATIONS_PATH'
GLOBAL FLAGS
--json Format output as json.
DESCRIPTION
Generate a new timestamped migration file with boilerplate code
EXAMPLES
$ morpheus create add-user-nodes
$ morpheus create update-relationships -m ~/path/to/migrations
$ morpheus create update-relationships --config ./custom-config.json
See code: src/commands/create.ts
morpheus info
Info up migration-related database objects
USAGE
$ morpheus info [--json] [--debug] [-c <value>] [-m <value>] [-h <value>] [-p <value>] [-s <value>] [-P
<value>] [-u <value>] [-d <value>]
FLAGS
-P, --password=<value> Neo4j password. Env: 'MORPHEUS_PASSWORD'
-c, --configFile=<value> Path to the morpheus file. ./morpheus.json by default
-d, --database=<value> Neo4j database. Env: 'MORPHEUS_DATABASE'
-h, --host=<value> Neo4j host. Env: 'MORPHEUS_HOST'
-m, --migrationsPath=<value> Migrations path. Env: 'MORPHEUS_MIGRATIONS_PATH'
-p, --port=<value> Neo4j port. Env: 'MORPHEUS_PORT'
-s, --scheme=<value> Neo4j scheme. Env: 'MORPHEUS_SCHEME'
-u, --username=<value> Neo4j username. Env: 'MORPHEUS_USERNAME'
GLOBAL FLAGS
--debug Enable debug logging
--json Format output as json.
DESCRIPTION
Info up migration-related database objects
Removes all Morpheus migration metadata including nodes, relationships, and optionally constraints.
Use with caution as this will reset the migration history.
EXAMPLES
$ morpheus info
$ morpheus info --config ./custom-config.json
See code: src/commands/info.ts
morpheus init
Initialize a new Morpheus configuration file with database connection settings
USAGE
$ morpheus init [-c <value>] [-f]
FLAGS
-c, --configFile=<value> Path to the morpheus file. ./morpheus.json by default
-f, --force Overwrite existing configuration file if it exists
DESCRIPTION
Initialize a new Morpheus configuration file with database connection settings
EXAMPLES
$ morpheus init
$ morpheus init --force
$ morpheus init --config ./custom-path/morpheus.json
$ morpheus init --config .config.json --force
See code: src/commands/init.ts
morpheus migrate
Execute pending database migrations in sequential order
USAGE
$ morpheus migrate [--json] [--debug] [-c <value>] [-m <value>] [-h <value>] [-p <value>] [-s <value>] [-P
<value>] [-u <value>] [-d <value>] [--dry-run] [--transaction-mode PER_MIGRATION|PER_STATEMENT]
FLAGS
-P, --password=<value> Neo4j password. Env: 'MORPHEUS_PASSWORD'
-c, --configFile=<value> Path to the morpheus file. ./morpheus.json by default
-d, --database=<value> Neo4j database. Env: 'MORPHEUS_DATABASE'
-h, --host=<value> Neo4j host. Env: 'MORPHEUS_HOST'
-m, --migrationsPath=<value> Migrations path. Env: 'MORPHEUS_MIGRATIONS_PATH'
-p, --port=<value> Neo4j port. Env: 'MORPHEUS_PORT'
-s, --scheme=<value> Neo4j scheme. Env: 'MORPHEUS_SCHEME'
-u, --username=<value> Neo4j username. Env: 'MORPHEUS_USERNAME'
--dry-run Perform a dry run - no changes will be made to the database
--transaction-mode=<option> [default: PER_MIGRATION] Transaction mode
<options: PER_MIGRATION|PER_STATEMENT>
GLOBAL FLAGS
--debug Enable debug logging
--json Format output as json.
DESCRIPTION
Execute pending database migrations in sequential order
EXAMPLES
$ morpheus migrate
$ morpheus migrate -m ~/path/to/migrations
$ morpheus migrate --config ./custom-config.json
$ morpheus migrate --dry-run
$ morpheus migrate --transaction-mode=PER_STATEMENT
See code: src/commands/migrate.ts
import { MorpheusModule, MorpheusService, Neo4jConfig, Neo4jScheme } from '../../dist/nestjs';
import { Module, Injectable } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [MorpheusModule, ConfigModule.forRoot()],
providers: [MigrationsService],
})
export class MigrationsModule {}
@Injectable()
export class MigrationsService {
constructor(
private readonly morpheusService: MorpheusService,
private readonly configService: ConfigService,
) {}
async onApplicationBootstrap() {
// When no config is provided, the default config is used
// -> morpheus.json
// -> moprheus environment variables
await this.morpheusService.cleanDatabase(); // NOTE: You probably don't want to do this, specially in production
await this.morpheusService.runMigrations();
// Use the ConfigService to access the environment variables
const configs: Neo4jConfig[] = [
{
scheme: Neo4jScheme.BOLT,
host: 'localhost',
port: 7687,
username: 'neo4j',
password: 'password',
migrationsPath: '../neo4j/migrations',
},
];
for (const config of configs) {
// Clean and run migrations
await this.morpheusService.cleanDatabase(config); // NOTE: You probably don't want to do this, specially in production
await this.morpheusService.runMigrations(config);
}
}
}
The approach is simple. Morpheus will read all migrations in the neo4j/migrations
directory and execute them in order.
For each migration, Morpheus will create a transaction and execute the migration. Thus a migration may contain multiple Cypher statements (each statement must end with ;
).
Once a migration file is executed, Morpheus will keep track of the migration and will not execute it again.
Existing migration files that have already been executed can not be modified since they are stored in a database with their corresponding checksum (crc32).
If you want to revert a migration, create a new migration and revert the changes.
You can take a look at schema and explanation on Michael's README - there's a neat graph that shows the migration chain.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.