如何导出异步加载的数据以在其他节点模块中使用?

时间:2019-07-06 11:20:44

标签: javascript node.js asynchronous async-await configuration-files

我在加载由异步调用加载的配置变量时遇到问题。

如下所示的配置变量:

//config.js
let config = {
    db: {
        dev: {
            client: ENV_VARS.dev_db_client,
            host: ENV_VARS.dev_db_host,
            name: ENV_VARS.dev_db_name,
            user: ENV_VARS.dev_db_user,
            password: ENV_VARS.dev_db_password,
            min_pool: ENV_VARS.dev_db_min_pool,
            max_pool: ENV_VARS.dev_db_max_pool
        },
        staging: {
            client: ENV_VARS.staging_db_client,
            host: ENV_VARS.staging_db_host,
            name: ENV_VARS.staging_db_name,
            user: ENV_VARS.staging_db_user,
            password: ENV_VARS.staging_db_password,
            min_pool: ENV_VARS.staging_db_min_pool,
            max_pool: ENV_VARS.staging_db_max_pool
        },
        production: async function () {
            let secret = await getSecretFromStore(`SECRET_NAME`);
            let tmpConfig = JSON.parse(secret);
            return {
                    client: ENV_VARS.production_db_client,
                    host: tmpConfig.host,
                    password: tmpConfig.password,
                    user: tmpConfig.username,
                    name: tmpConfig.db_name,
                    min_pool: ENV_VARS.production_db_min_pool,
                    max_pool: ENV_VARS.production_db_max_pool
                };
            });
    },
    NODE_ENV: ENV_VARS.NODE_ENV,
    STACK_ID: ENV_VARS.STACK_ID,
};
module.exports = config;

//knexfile.js
const config = require('authenticator/config');
let productionConfig = {};
if (config.NODE_ENV == 'production') {
// how to load the production vars in the right way and export them?
//the next line is async call and it is wrong to call it like this
    productionConfig = config.db.production();
}
const _config = {
    development: {
        client: config.db.dev.client,
        connection: {
            host: config.db.dev.host,
            database: config.db.dev.name,
            user: config.db.dev.user,
            password: config.db.dev.password,
            // debug: ['ComQueryPacket']
        },
        pool: {
            min: Number(config.db.dev.min_pool) || 0,
            max: Number(config.db.dev.max_pool) || 1
        },
        migrations: {
            tableName: 'knex_migrations'
        }
    },

    staging: {
        client: config.db.staging.client,
        connection: {
            host: config.db.staging.host,
            database: config.db.staging.name,
            user: config.db.staging.user,
            password: config.db.staging.password
        },
        pool: {
            min: Number(config.db.staging.min_pool) || 0,
            max: Number(config.db.staging.max_pool) || 1
        },
        migrations: {
            tableName: 'knex_migrations'
        }
    },

    production: {
        client: productionConfig.client,
        connection: {
            host: productionConfig.host,
            database: productionConfig.name,
            user: productionConfig.user,
            password: productionConfig.password
        },
        pool: {
            min: Number(productionConfig.min_pool) || 0,
            max: Number(productionConfig.max_pool) || 1
        },
        migrations: {
            tableName: 'knex_migrations'
        }
    }
};
module.exports = _config;
//db.js
const config = require('config');
//the config are loaded into knexfile.js and used but production can't be loaded cuz it is async call
//the next line should call the production vars in the env is production
const knexConfig = require('../knexfile')[config.NODE_ENV];
const knex = require('knex')(knexConfig);
module.exports = knex;

那么,有什么好的策略可以将该模块加载到不同的node_module中?

换句话说,加载临时变量和dev变量并使用它们很容易,但是由于生产变量是通过异步调用加载的,那么如何以正确的方式加载它们?

如果使用发电机。这样可以解决问题吗?

1 个答案:

答案 0 :(得分:1)

我要做的第一件事是使开发和登台配置具有与live相同的语义。由于production返回了一个保证,因此devstaging也应该返回一个保证。不这样做就是在为生产准备惊喜。

例如:

db: {
    dev: async function() {
        await delay(0); // Where `delay` is a `setTimeout` wrapper
        return {
            client: ENV_VARS.dev_db_client,
            host: ENV_VARS.dev_db_host,
            name: ENV_VARS.dev_db_name,
            user: ENV_VARS.dev_db_user,
            password: ENV_VARS.dev_db_password,
            min_pool: ENV_VARS.dev_db_min_pool,
            max_pool: ENV_VARS.dev_db_max_pool
        };
    },
// ....

...和分期相同。您甚至可能会看到延迟产生了多长时间,并模仿了devstaging中的延迟。 (除非您返回当前的所有配置。)

您的production也应该是async函数,因为您在其中使用了await

您的下一步是删除该代码。您为所有三个配置都构建了相同的结构。将其集中在一个函数中:

function buildConfig(env) {
    return {
        client: env.client,
        connection: {
            host: env.host,
            database: env.name,
            user: env.user,
            password: env.password,
            // debug: ['ComQueryPacket']
        },
        pool: {
            min: Number(env.min_pool) || 0,
            max: Number(env.max_pool) || 1
        },
        migrations: {
            tableName: 'knex_migrations'
        }
    };
}

然后,导出配置的承诺,而不是实际的配置:

module.exports = Promise.all([config.db.dev, config.db.staging, config.db.production])
    .then(([dev, staging, production]) => {
        return {
            development: buildConfig(dev),
            staging:     buildConfig(staging),
            production:  buildConfig(production)
        };
    });

使用该模块的模块将需要使用该诺言,因为该操作是异步的(在所有环境中):

require(/*...*/).then(config => {
    // Code using the config here...
});

在更长时间之前,多亏了top-level await proposal,这将减轻痛苦(使用ESM模块,而不是CommonJS模块),它可以使模块解析为异步,并允许您在顶层使用await