2

I am trying to package code for AWS Lambda. Lambda has various restrictions, such as using Node 6.10, and not having a build step, like AWS EB does. I also am using NPM modules, so these will need to be bundled with the AWS Lambda handler.

Here is what I would like to do:

  • Define and use NPM modules (pure JS modules only)
  • Transpile all code (including NPM modules) to a JS version that Node 6.10 supports
  • Statically link all NPM modules into one big JS file
  • Upload that single file to AWS Lambda

For example, suppose I have an NPM module foo (node_modules/foo/index.js):

export default { x: 1 };

and I have my own code ('index.js'):

import foo from 'foo';

export const handler = (event, context, callback) => {
  console.log(foo); // Will appear in CloudWatch logs
  callback(null, 'OK');
};

The output would be something like this ('dist/bundle.js'):

var foo = { x: 1 };

exports.handler = function(event, context, callback) {
  console.log(foo);
  callback(null, 'OK');
};

I should be able to upload and run bundle.js on AWS Lambda without further modification.

How can I achieve this using existing JS tools?

Chacko
  • 1,506
  • 1
  • 20
  • 42
sdgfsdh
  • 33,689
  • 26
  • 132
  • 245
  • I understand you want to create one big bundle.js file, but what is much more practical for AWS Lambda is to zip your project structure including all js files as well as the whole node_modules folder. You can upload the zip file to Lambda directly or if it is too large you upload it to S3 and reference it when creating the Lambda function. – Marcel Böttcher Dec 20 '17 at 12:41
  • one big bundle file is probably not doable with existing tools, but if you are using the [serverless framework](https://serverless.com/) the [serverless-webpack](https://www.npmjs.com/package/serverless-webpack) plugin will help a lot (in combination with the [webpack-node-externals](https://www.npmjs.com/package/webpack-node-externals) module). (take a look at the package individually option) – hereandnow78 Dec 20 '17 at 12:46

2 Answers2

4

You can use serverless with serverless-webpack

Then you deploy your bundle with serverless deploy

Gabriel Bleu
  • 9,703
  • 2
  • 30
  • 43
1

It turns out that this is possible, but it requires some tricky configuration to achieve. I have created a boiler-plate repo for others to use.

Here are the important bits...

First, you need a .babelrc that targets Node.js 6.10:

{
  "presets": [
    [
      "env", {
        "targets": {
          "node": "6.10"
        }, 
        "loose": false,
        "spec": true
      }
    ]
  ]
}

Next, you need to configure Webpack to generate a commonjs library targetting node:

const path = require('path');
const webpack = require('webpack');

const debug = process.env.NODE_ENV !== 'production';

module.exports = {
  context: __dirname,
  entry: [ 'babel-polyfill', './index.js' ],
  output: {
    path: path.join(__dirname, 'out'),
    filename: 'index.js',
    libraryTarget: 'commonjs'
  },
  devtool: debug ? 'source-map' : false,
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            babelrc: true,
            compact: !debug
          }
        }
      }
    ],
  },
  target: 'node',
  plugins: [
    new webpack.DefinePlugin({ 'global.GENTLY': false })
  ]
};

Note that you do not want to ignore the node_modules folder, since that would prevent static-linking.

The babel-polyfill plugin is also crucial if you want to use modern JS features.

Your actual handler code should have a named export that matches what you have set in the AWS console:

export const handler = (event, context, callback) => callback(null, 'OK');

Do not do it like this!

// Bad! 
export default {
  handler: (event, context, callback) => callback(null, 'OK'),
};

When packaging the code, make sure you add index.js to the top level of the zip:

zip -j bundle.zip ./out/index.js
sdgfsdh
  • 33,689
  • 26
  • 132
  • 245