3

I'm trying to set up an emailing system with nodemailer that's agnostic of the transport type it uses.

Here's how I'm trying to send the email:

const config = require('config')
const mailerConfig = config.get('mailer')
const transporter = nodemailer.createTransport(mailerConfig.transport)
transporter.sendMail({
  from: mailerConfig.from,
  to: toEmail,
  subject,
  text: textBody,
  html: htmlBody,
})

And here's how my config file looks like:

const AWS = require('aws-sdk')
module.exports = {
  mailer: {
    from: 'test_sender@domain.com',
    transport: {
      SES: new AWS.SES({
        accessKeyId: 'secret-key',
        secretAccessKey: 'access-key',
        region: 'region',
      }),
    },
  }
}

Then, whenever I try to send an email, I get the following error:

error:  TypeError: Key must be a buffer
    at new Hmac (crypto.js:117:16)
    at Object.Hmac (crypto.js:115:12)
    at Object.hmac (/Users/sebi/Work/node_modules/aws-sdk/lib/util.js:401:30)
    at Object.getSigningKey (/Users/sebi/Work/node_modules/aws-sdk/lib/signers/v4_credentials.js:59:8)
at V4.signature (/Users/sebi/Work/node_modules/aws-sdk/lib/signers/v4.js:97:36)
at V4.authorization (/Users/sebi/Work/node_modules/aws-sdk/lib/signers/v4.js:92:36)
at V4.addAuthorization (/Users/sebi/Work/node_modules/aws-sdk/lib/signers/v4.js:34:12)
at /Users/sebi/Work/node_modules/aws-sdk/lib/event_listeners.js:215:18
at finish (/Users/sebi/Work/node_modules/aws-sdk/lib/config.js:320:7)
at /Users/sebi/Work/node_modules/aws-sdk/lib/config.js:338:9
at /Users/sebi/Work/node_modules/aws-sdk/lib/credentials.js:123:23
at Credentials.refresh (/Users/sebi/Work/node_modules/aws-sdk/lib/credentials.js:194:5)
at Credentials.get (/Users/sebi/Work/node_modules/aws-sdk/lib/credentials.js:121:12)
at getAsyncCredentials (/Users/sebi/Work/node_modules/aws-sdk/lib/config.js:332:24)
at Config.getCredentials (/Users/sebi/Work/node_modules/aws-sdk/lib/config.js:352:9)
at Request.SIGN (/Users/sebi/Work/node_modules/aws-sdk/lib/event_listeners.js:192:22)

Please note that when I'm instantiating AWS.SES() in the same place where I'm sending the email, the code works fine. Is there anything special that config does to break the code?

Sebastian
  • 250
  • 1
  • 13

3 Answers3

2

Is there anything special that config does to break the code?

Exactly. config package deep-merges all configs. So it walks thru your config, and breaks object returned from AWS.SES(). You can try to put your keys in config, and apply them to ses only on usage:

const AWS = require('aws-sdk')
module.exports = {
  mailer: {
    from: 'test_sender@domain.com',
    transport: {
      SES: {
        accessKeyId: 'secret-key',
        secretAccessKey: 'access-key',
        region: 'region',
      },
    },
  }
}

And use it:

const config = require('config')
const mailerConfig = config.get('mailer')
const transporter = nodemailer.createTransport({
    SES: new AWS.SES(mailerConfig.transport.SES)
})
transporter.sendMail({
  from: mailerConfig.from,
  to: toEmail,
  subject,
  text: textBody,
  html: htmlBody,
})

As alternative you can require your config file directly:

const config = require('./config') // depends on your files structure
iofjuupasli
  • 3,818
  • 1
  • 16
  • 17
  • Thanks for your reply! I've mentioned in my question that instantiating SES when I'm actually using it works fine :( But what I'm trying to do is to setup a mailing function that can be easily configured to use a different transport. So you're saying that `config` somehow breaks the AWS.SES object and that there's no way around that? :( – Sebastian Feb 19 '18 at 13:36
  • I think, it's either bug in `config` package or the way it should work, with simple objects only – iofjuupasli Feb 19 '18 at 13:39
1

In my case accessKey and secretAccessKey was undefined.

Shobhit Mishra
  • 337
  • 1
  • 2
  • 13
  • same in my case, but its very odd. `process.env.AWS_REGION` prints out properly, but `process.env.AWS_ACCESS_KEY` and `process.env.AWS_SECRET` print undefined. spelling and everything is correct. This is in Lambda btw. – Stephen Tetreault Dec 11 '18 at 22:45
0

I'm one of the maintainers of node-config. It sounds like it may be an issue that node-config has added some methods to your configuration object when you just want the plain data back.

Since v1.27.0, config.util.toObject(someValue) was added to allow you to get back a plain old JavaScript object for a given config value. It's documented on our utils wiki page

If you have an older version of node-config and don't want to upgrade, you also do this on a config structure returned bynode-config:

var plainOldValue = JSON.parse(JSON.stringify(configValue)

That has the effect of removing the methods added by node-config and is exactly what the new .toObject() method does internally.

Mark Stosberg
  • 12,961
  • 6
  • 44
  • 49
  • 1
    Thanks for your help! I've tried both methods you suggested but the SES object still seems to be broken: `TypeError: this.ses.sendRawEmail is not a function` – Sebastian Mar 02 '18 at 14:16
  • I've also tried the `cloneDeep` util function and it seems to do a better job at cloning the SES object but I still get an error: `error: TypeError: Cannot read property 'protocol' of undefined at ucfirst (/Users/sebi/Work/node_modules/aws-sdk/lib/query/query_param_serializer.js:11:38)` – Sebastian Mar 02 '18 at 14:32