Import packages in serverless functions

Highlighted
New Contributor

Hi,

 

i started playing around with the cms event serverless implementation. The code from https://github.com/HubSpot/cms-event-registration works good so far. 

 

I want to extend the functionality by creating a sendmail endpoint. I created a new file called sendMail.js and registered a new GET-Endpoint.

 

The endpoint was correctly initialized. To send an email i want to use nodemailer. I added the package wir "yarn add nodemailer" and imported the mailer in my sendMail.js.

 

Now i get the following error message:

 

{"error":"A server error occurred: 2020-06-03T14:54:09.514Z\tundefined\tERROR\tUncaught Exception \t{\"errorType\":\"Runtime.ImportModuleError\",\"errorMessage\":\"Error: Cannot find module 'nodemailer'\

 

Here is the complete sendMail.js:

 

const util = require('util');
const request = util.promisify(require('request'));

const nodemailer = require('nodemailer');

exports.main = ({ }, sendResponse) => {
    sendResponse({
        statusCode: 200,
        body: { message: 'Testresponse' },
    });
};

Can someone explain why i get this message? 

 

Thanks in advance.

Reply
0 Upvotes
6 Replies 6
Highlighted
HubSpot Moderator

Hey @dev-valentin,

 

Digging further into this error, I found this stackoverflow discussion in which one user state that:

 

1. nodemailer requires Node.js version 6+ to work, are you currently using version 6+? You can check your node.js version with `node --version`

 

and 

 

2. Did you include the following dependency on the package.json file?

{
  "dependencies": {
    "nodemailer": "^4.0.1"
  }
}
Reply
0 Upvotes
Highlighted
New Contributor

Hey @WendyGoh ,

 

thanks for your reply.

 

Locally the Node Version v14.0.0 is installed. I also tried to add nodemailer with these versions:

 
"nodemailer": "^6.4.8",
and
"nodemailer": "^4.0.1"
 
But without luck. It's still the same error. Could the webpack.config.js be the problem?
In the Serverless docs there is part with "How to include other packages":
 
It's is mentioned that you should "combine" your node modules for the serverless functions. How can that be achieved?
Reply
0 Upvotes
Highlighted
HubSpot Moderator

Hey @dev-valentin,

 

That could be it however before we go there, I'm wondering if it's because your team is using const instead of var? 

 

Looking at the same discussion it looks like most of them suggest to use 

 

var nodemailer = require ('nodemailer')

 

Could you try and see if this works?

Highlighted
New Contributor

Hey @WendyGoh ,

 

both is not working, still shows the same error message.

 

No differences between:

 

const nodemailer = require ('nodemailer');
and
 
var nodemailer = require ('nodemailer');

 

By the way, here is the url to the endpoint:

http://7837708.hs-sites.com/_hcms/api/sendMail

 

Maybe there is another loading issue?!

Reply
0 Upvotes
Highlighted
HubSpot Moderator

Hey @dev-valentin,


I checked in this case with my team and was able to confirm that at the moment, we do not support the defining of any node dependencies. We only support a small list of dependencies built-in. You can check out the list here.


As such, if you'd like to use the dependency, you would need to use something like webpack to bundle the dependency into your function.

 

While we currently do not have any real resources on that front just yet, do let us know if your team run into any specific issues bundling the dependency.

 

Here are the webpack docs: https://webpack.js.org/.

Highlighted
HubSpot Employee

Hi Valentin:

 

Slightly simplified, your function handler currently looks like this:

 

const nodemailer = require('nodemailer');

exports.main = ({ }, sendResponse) => {
    sendResponse({
        statusCode: 200,
        body: { message: 'nodemailer contains: ' + Object.keys(nodemailer) },
    });
};

Trying to GET this lambda results in "Error: Cannot find module 'nodemailer'", as you observed.

 

This error can be resolved via webpack using a procedure as follows.

 

First, split this file into two files:

 

File #1

exports.main = ({ }, sendResponse) => {

// We'll paste the bundled webpack output right here.

};

File #2

const nodemailer = require('nodemailer');

sendResponse({
    statusCode: 200,
    body: { message: 'nodemailer contains: ' + Object.keys(nodemailer) },
});

 

Second, we set up "File #2" as a Node app ("File #2" is called "sendMail.js" in the following screenshot). Starting off like so:

 

Screenshot 2020-06-12 20.33.04.png

 

The "package.json" file looks as follows in my case:

 

{
  "name": "webpack_test",
  "version": "1.0.0",
  "description": "test webpack on HS Serverless",
  "main": "sendMail.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "v",
  "license": "ISC",
  "dependencies": {
    "nodemailer": "^6.4.8"
  }
}

While I set up "webpack.config.js" as follows:

 

const path = require('path');

module.exports = {
  entry: './sendMail.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, "webpack_output"),
  },
  node: {
    child_process: 'empty',
    fs: 'empty',
    dns: 'empty',
    net: 'empty',
    tls: 'empty'
  }
};

 

Installing dependencies (in particular, "nodemail") via 

$ npm install --save

 

Third, we run webpack on this app:

$ webpack --config webpack.config.js

Webpack has now compiled the node app into a flat file "bundle.js" (as per my config file), which contains close to 1 Million characters of gibberish (i.e. compiled javascript, including all nested dependencies of the app):

 

Screenshot 2020-06-12 20.47.41.png

 

Fourth and finally, we take this ENTIRE output and paste it into "File #1" (where I had placed the comment):

 

Now when I route a GET request to the function handler in "File #1", I get a successfull response:

 

Screenshot 2020-06-12 20.59.02.png

 

Yippieh! I hope these steps are helpful for implementing webpack with HubSpot Serverless.

 

NOTE:

 

Please note that I added

node: {
    child_process: 'empty',
    fs: 'empty',
    dns: 'empty',
    net: 'empty',
    tls: 'empty'
  }

to the webpack configuration file after seeing some rather gruesome access errors. I implemented this fix as per https://github.com/googleapis/google-api-nodejs-client/issues/1732 , but I haven't checked whether I actually broke the functionality of nodemail.js this way. So this solution might need some more work, but I hope it illustrates the general approach.

 

Thanks to HubSpotters Mike (@mgallant23) and Derek (@dgervais), whose internal resources on this topic I have relied on in preparing this solution.

 

Valentin, feel free to get in touch with any further questions on this topic.