salsita / node-pg-migrate

Node.js database migration management for PostgreSQL
https://salsita.github.io/node-pg-migrate
MIT License
1.3k stars 179 forks source link

Using config-file and config-value #891

Open mike-usa opened 2 years ago

mike-usa commented 2 years ago

How to properly use config-value?

config/database.json

{
  "dev": {
    "user": "dev-user",
    "password": "dev-pass",
    "host": "localhost",
    "port": 5432,
    "database": "dev-db"
  },
  "test": {
    "user": "test-user",
    "password": "test-pass",
    "host": "localhost",
    "port": 5432,
    "database": "test-db"
  },
}

Given the documentation, my understanding is migrations can run against a specified database via config-value and loaded from a non-standard json file via config-file. However, the following command fails at loading the migration:

npx node-pg-migrate up --config-file config/database.json --config-value dev


For verification, a simplified config/database.json works without a named key:

{
  "user": "dev-user",
  "password": "dev-pass",
  "host": "localhost",
  "port": 5432,
  "database": "dev-db"
}

npx node-pg-migrate up --config-file config/database.json

However, the following also results in an error when using a db key:

{
   "db": {
     "user": "dev-user",
     "password": "dev-pass",
     "host": "localhost",
     "port": 5432,
     "database": "dev-db"
  }
}
mike-usa commented 2 years ago

I'm still reading through https://github.com/salsita/node-pg-migrate/blob/master/bin/node-pg-migrate, specifically the config-file and config-value portion, which seem independent. It seems like the file portion should come first and inside it should evaluate if the config-value was set.

I discovered it'll work if I install config library independently and name the json file to match NODE_ENV or NODE_CONFIG_ENV value. Using the original example with dev and test keys

For config/database.json

npm i config
NODE_ENV=database nix node-pg-migrate up --config-value dev

To me this suggests a couple things:

  1. the version of config that is packaged with node-pg-migrate is out of date
  2. it demonstrates plenty of opportunity to extend the library; you may want multiple config files; one for the general environment settings and then one for database configurations -- think Ruby on Rails (or SailsJS)

Disclaimer: I don't perform much Node development. All of these libraries are fairly new to me. Feel free to set me straight.


Likewise this can work with modules as well:

// config/database.js
module.exports = {
  "dev": {
    "database": "dev",
    "user": "dev-user",
    ...
  },
  "test": {
    "database": "test"
    ...
  }
}
NODE_ENV=database npx node-pg-migrate up --config-value test
mike-usa commented 2 years ago

I am suspicious that downloading the config package was the difference. The version I'm using is 3.3.7 and the version packaged with node-pg-migrate is 3.3.6. I don't think that patch version update included such a substantial change. So maybe there must be something interfering with the config load process?

huzaifa-99 commented 3 weeks ago

I had the same issue and didn't wanted to install the config pkg, tried different ways to get the --config-file arg to work but with no luck.

I ended up with creating a node script to read my config file and map all the config options to node-pg-migrate, this works well.

// scripts/migration/create.js

const { generateNodePgConfigStr, executeCommand } = require('./common');

// migration name from cli arguments
const migrationName = process.argv[2];
if (!migrationName) {
    console.error('please provide a migration name.');
    process.exit(1);
}

executeCommand(`node-pg-migrate create ${migrationName} ${generateNodePgConfigStr()}`);

// scripts/migration/common.js

const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');

function camelToLowerSnakeCase(str) {
    return str
        .replace(/([A-Z])/g, '-$1') // add an underscore before each uppercase letter
        .toLowerCase() // convert the entire string to lowercase
        .replace(/^_/, ''); // remove leading underscore if exists
}

const generateNodePgConfigStr = () => {
    // load the configuration file
    const nodePgConfigFile = path.resolve(__dirname, '../../database/node_pg_config.json');
    const config = JSON.parse(fs.readFileSync(nodePgConfigFile, 'utf8'));

    // prepare the command
    const options = Object.entries(config).map(([k, v]) => `--${camelToLowerSnakeCase(k)}=${v}`).join(' ');

    return options
}

const executeCommand = (command = '') => {
    exec(command, (error, stdout, stderr) => {
        if (error) {
            console.error(`Error executing command: ${error.message}`);
            return;
        }
        if (stderr) {
            console.error(`Error: ${stderr}`);
            return;
        }
        console.log(`Success: ${stdout}`);
    });
}

module.exports = {
    generateNodePgConfigStr,
    executeCommand
}

// database/node_pg_config.json
{
    "schema": "public",
    "createSchema": false,
    "databaseUrlVar": "DATABASE_URL",
    "migrationsDir": "./database/migrations",
    "migrationsSchema": "public",
    "createMigrationsSchema": false,
    "migrationsTable": "migrations",
    "migrationFilenameFormat": "utc",
    "migrationFileLanguage": "js",
    "timestamp": false,
    "checkOrder": true,
    "singleTransaction": true,
    "noLock": false,
    "fake": false,
    "decamelize": false,
    "verbose": true,
    "rejectUnauthorized": false
}

// package.json

{
  "scripts": {
    "migration:create": "node scripts/migrations/create.js"
  },
  "dependencies": {
    ...
  },
  "devDependencies": {
     ...
  }
}