Useful or not, from you.
typeorm Support for async ormconfig

Issue type:

[ ] question [ ] bug report [x] feature request [ ] documentation issue


Propose to support async/Promises returned from the ormconfig.js and ormconfig.ts files.

The use case for this is to be able to figure out connection details in a non-standard ways.

E.g. two uses cases come to mind:

  1. Database credentials are obtained from a remote secret store.
  2. Loading up complex configs from store in a non-blocking way. E.g. reading many files and combining the config.
  3. Usage of dependency inversion containers. E.g. load up config as a dependency first, and then inject the config somewhere. This is my actual use case atm.

I think the fix can be pretty simple. We can just Promise.resolve any output from the .js and .ts files. If it is static data, then it will be resolved immediately anyways, if it's a Promise, then we'll wait for the Promise to resolve.

Example use case:

module.exports = () => {
  return Promise.resolve({
    type: 'postgres',
    url: process.env.DATABASE_URL,
  })
}

I can work on a PR, if this is deemed to be a good feature.

That's a useful answer
Without any help

Thanks @biern for the hack. I get an error

import { ConnectionOptionsReader } from 'typeorm/connection/ConnectionOptionsReader';
       ^

SyntaxError: Unexpected token {
    at Module._compile (internal/modules/cjs/loader.js:723:23)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Function.PlatformTools.load (/Users/USER/PATH/node_modules/typeorm/platform/PlatformTools.js:108:28)
    at ConnectionOptionsReader.<anonymous> (/Users/USER/PATH/node_modules/typeorm/connection/ConnectionOptionsReader.js:121:75)
    at step (/Users/USER/PATH/node_modules/tslib/tslib.js:136:27)

Here is my ormconfig.ts:

import { ConnectionOptionsReader } from 'typeorm/connection/ConnectionOptionsReader';
import {
  SQL_PORT,
  SQL_LOGGING,
  SQL_HOST,
  SQL_USERNAME,
  SQL_PASSWORD,
  SQL_DATABASE,
} from 'datasources/sql/constants/config.json';
import EnvKeys from 'constants/env-keys';
import Environments from 'constants/environments';
import { getEnvs } from '@packages/infrastructure';

// This is a hacky patch
// async functions for the ormconfig
/* eslint-disable */

function patchAsyncConnectionSetup() {
  const { prototype } = ConnectionOptionsReader;

  // @ts-ignore
  const original = prototype.normalizeConnectionOptions;

  // @ts-ignore
  prototype.normalizeConnectionOptions = function(options) {
    if ('then' in options) {
      // @ts-ignore
      return options.then(arg => original.call(this, arg));
    }

    return original.call(this, options);
  };
}
/* eslint-enable */
// @ts

patchAsyncConnectionSetup();

async function buildConnectionOptions() {
  const configs = [
    EnvKeys.ENV,
    SQL_PORT,
    SQL_LOGGING,
    SQL_HOST,
    SQL_USERNAME,
    SQL_PASSWORD,
    SQL_DATABASE,
  ];
  const [
    env,
    port,
    logging,
    host,
    username,
    password,
    database,
  ] = await Promise.all(configs.map(getEnvs));

  const isLocal = env === Environments.LOCAL;
  const extension = isLocal ? 'ts' : 'js';

  const config = {
    type: 'mysql',
    host: isLocal ? host : undefined,
    port: Number(port),
    username,
    password,
    database,
    synchronize: true,
    entities: [`src/datasources/sql/entities/**/*.${extension}`],
    migrations: [`src/datasources/sql/migrations/**/*.${extension}`],
    // subscribers: [
    //   `src/datasources/sql/subscribers/**/*.${extension}`,
    // ],
    cli: {
      migrationsDir: 'src/datasources/sql/migrations',
    },
    logging: logging === 'true',
    // extra: {
    //   socketPath: !isLocal ? `/cloudsql/${host}` : undefined,
    // },
  };

  return [
    {
      ...config,
    },
    {
      ...config,
      name: 'seed',
      migrationsTableName: 'seeds',
      migrations: `src/datasources/sql/seeds/**/*.${extension}`,
      cli: {
        migrationsDir: `src/datasources/sql/seeds/**/*.${extension}`,
      },
    },
  ];
}

const config = buildConnectionOptions();

module.exports = config;

When using a simple ormconfig.json with the same arguments, it works but not with the ormconfig.ts. Any idea what I'm missing?