Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exporting data in a node file after it is returned from an API call to AWS Secrets Manager

Tags:

Background

I am storing the database information for a RDS in AWS in the secrets manager. I am using the AWS-SDK to retrieve the password and other data so I can create a secrets object at run time. When I try and create this object and then export it, the object that is exported is always lacking the data that I expect to be returned from the aws-sdk.

What I Have Tried -

I have tried using async await but it is still exporting the object before all of the data is correctly populated.

Example

const AWS = require('aws-sdk');
const region = 'us-west-2';
const secretName = 'example/example/example';
let secrets = {
  username: '',
  password: '',
  host: '',
  port: '',
  database: '',
  email: '[email protected]',
  emailPassword: 'SomePassword'
};

const client = new AWS.SecretsManager({
  region: region
});

client.getSecretValue({ SecretId: secretName }, async (err, data) => {
  if (err) {
    throw err;
  } else {
    const res = await JSON.parse(data.SecretString);
    secrets.username = res.username;
    secrets.password = res.password;
    secrets.host = res.host;
    secrets.port = res.port;
    secrets.database = res.database;
  }
});

module.exports = secrets;

Question

The obvious problem here is not creating the promise correctly but I am not sure why my attempts are completing the promises after the file gets exported. If I console.log(secrets) in another file in some cases it will output the object missing data then show the data returned by the promise a few seconds later after when I console.log(secrets) inside of the function.

What is the proper way to build this object secrets and export it once the data is returned from AWS and added to the object secrets?

like image 425
wuno Avatar asked Oct 15 '18 22:10

wuno


1 Answers

According to the docs, the second argument to getSecretValue is a callback function, so there's no need to use async/await since async/await is meant to work with promises.

Removing async/await should work.

client.getSecretValue({ SecretId: secretName }, (err, data) => {
  if (err) {
    throw err;
  } else {
    const res = JSON.parse(data.SecretString);
    secrets.username = res.username;
    secrets.password = res.password;
    secrets.host = res.host;
    secrets.port = res.port;
    secrets.database = res.database;
  }
}); 

However, you're exporting the secrets object synchronously, and its properties are getting set asynchronously.

Instead, you can return a promise for your other modules to consume.

const AWS = require('aws-sdk');
const region = 'us-west-2';
const secretName = 'example/example/example';
let secrets = {
  email: '[email protected]',
  emailPassword: 'SomePassword'
};

const client = new AWS.SecretsManager({
  region: region
});

const promise = new Promise((resolve, reject) => {
  client.getSecretValue({ SecretId: secretName }, async (err, data) => {
    if (err) {
      reject(err);
    } else {
      const res = await JSON.parse(data.SecretString);
      secrets.username = res.username;
      secrets.password = res.password;
      secrets.host = res.host;
      secrets.port = res.port;
      secrets.database = res.database;
      resolve(secrets);
    }
  });
})

module.exports = promise;

Then, in some other module that consumes this one, you can use async/await since we now have a promise.

import {promise} from "./this-module";

(async () => {
  const secrets = await promise;
  console.log(secrets);
})();

Update

I'm not sure if this will work, but it's worth a shot. Here, set module.exports only after secrets is set. If this doesn't work, then I would ask a new question on StackOverflow about how to export resolved promises with CommonJS (which is the module format that you're using).

const AWS = require('aws-sdk');
const region = 'us-west-2';
const secretName = 'example/example/example';
let secrets = {
  email: '[email protected]',
  emailPassword: 'SomePassword'
};

const client = new AWS.SecretsManager({
  region: region
});

const promise = new Promise((resolve, reject) => {
  client.getSecretValue({ SecretId: secretName }, async (err, data) => {
    if (err) {
      reject(err);
    } else {
      const res = await JSON.parse(data.SecretString);
      secrets.username = res.username;
      secrets.password = res.password;
      secrets.host = res.host;
      secrets.port = res.port;
      secrets.database = res.database;
      resolve(secrets);
    }
  });
});

(async () => {
  module.exports = await promise;
})();
like image 105
Raphael Rafatpanah Avatar answered Oct 26 '22 23:10

Raphael Rafatpanah