Getting error if I use DATABASE_URL from AWS secret manager

Feature(s) impacted

I am trying to bring in DATABASE_URL from secret manager rather than having it hard coded in .env file.

Observed behavior

Getting error “Uh oh, no collections found.
The liana didn’t detect any collections.
Please ensure to have models declared in your project.”

  • Project name: Earth Portal
  • Team name: Operations
  • Environment name: Dev
  • Recent changes made on your end if any: Changed config/databases.js and model/index.js

“liana”: “forest-express-mongoose”,

"liana_version": "8.6.5",

"stack": {

  "database_type": "MongoDB",

  "engine": "nodejs",

  "engine_version": "14.17.4",

  "orm_version": "6.2.6"

}

Hello @aayush-sinha :wave:

Can you share your modified files config/database.js and model/index.js ? Remove any secret before sharing.

Thanks :pray:

model/index.js

const fs = require('fs');
const path = require('path');
const Mongoose = require('mongoose');

const databasesConfiguration = require('../config/databases');

const connections = {};
const db = {};

databasesConfiguration.map(async (databaseInfo) => {
  const mongoCred = await databaseInfo.connection();

  const connection = Mongoose.createConnection(mongoCred.url, mongoCred.options);
  connections[databaseInfo.name] = connection;

  const modelsDir = databaseInfo.modelsDir || path.join(__dirname, databaseInfo.name);
  fs.readdirSync(modelsDir)
    .filter((file) => file.indexOf('.') !== 0 && file !== 'index.js')
    .forEach((file) => {
      try {
        const model = require(path.join(modelsDir, file))(connection, Mongoose);
        db[model.modelName] = model;
      } catch (error) {
        console.error(`Model creation error: ${error}`);
      }
    });
});

db.objectMapping = Mongoose;
db.connections = connections;

module.exports = db;

config/database.js

const path = require('path');
const SecretsManager = require('../utils/secretsManager');

const databaseOptions = {
  useNewUrlParser: true,
  useUnifiedTopology: true,
};

module.exports = [
  {
    name: 'default',
    modelsDir: path.resolve(__dirname, '../models'),
    connection: async () => {
      const secretKeys = await SecretsManager.getSecrets(process.env.DATABASE_URL);
      
      return {
        url: `mongodb+srv://${secretKeys.username}:${secretKeys.password}@${secretKeys.host}`,
        options: { ...databaseOptions },
      };
    },
   
  },
];

Thank you :pray:

When you use an async function as the callback of databasesConfiguration.map, the subsequent code will be executed without waiting on the map to finish. So module.exports = db will be executed before db[model.modelName] = model and the db object will be exported without models.

Try a for of loop instead :

for (const databaseInfo of databasesConfiguration) {
  const mongoCred = await databaseInfo.connection();

  const connection = Mongoose.createConnection(mongoCred.url, mongoCred.options);
  connections[databaseInfo.name] = connection;

  const modelsDir = databaseInfo.modelsDir || path.join(__dirname, databaseInfo.name);
  fs.readdirSync(modelsDir)
    .filter((file) => file.indexOf('.') !== 0 && file !== 'index.js')
    .forEach((file) => {
      try {
        const model = require(path.join(modelsDir, file))(connection, Mongoose);
        db[model.modelName] = model;
      } catch (error) {
        console.error(`Model creation error: ${error}`);
      }
    });
}

Let me know if it works :wink:

Hi @NicolasGriselle
Thanks for pointing that out. I made it work with some tweaks. Had to put the for of loop inside an async function to use await and some changes on middlewares/forestadmin.js.

model/index.js

const dbconnection = async () => {
  for (const databaseInfo of databasesConfiguration) {
    const mongoCred = await databaseInfo.connection();

    const connection = Mongoose.createConnection(mongoCred.url, mongoCred.options);
    connections[databaseInfo.name] = connection;

    const modelsDir = databaseInfo.modelsDir || path.join(__dirname, databaseInfo.name);
    fs.readdirSync(modelsDir)
      .filter((file) => file.indexOf('.') !== 0 && file !== 'index.js')
      .forEach((file) => {
        try {
          const model = require(path.join(modelsDir, file))(connection, Mongoose);
          db[model.modelName] = model;
        } catch (error) {
          console.error(`Model creation error: ${error}`);
        }
      });
  }
  db.objectMapping = Mongoose;
  db.connections = connections;

  return db;
};

module.exports = { dbconnection };

middlewares/forestadmin.js.

module.exports = async function forestadmin(app) {
  const { objectMapping, connections } = await dbconnection();
  app.use(
    await Liana.init({
      configDir: path.join(__dirname, '../forest'),
      envSecret: process.env.FOREST_ENV_SECRET,
      authSecret: process.env.FOREST_AUTH_SECRET,
      objectMapping,
      connections,
    })
  );

You guys have built a great platform, I love the flexibility it provides and your support team is awesome. Thanks again for helping.

1 Like