I am creating a lambda layer, bundling up some dependencies, including node_modules. I am successfully creating a layer but when i try to require a module from my code, the console is telling me that the module cannot be found. Here is the code
var Promise = require('promise'); module.exports.handler = function(event, context, callback) { new Promise(function (resolve, reject) { setTimeout(function() { callback(null, "helloWorld2"); }, 9000); }); }; How can I reference node modules from a layer???
84 Answers
How are you running your lambda? If via sam cli, something like the below has worked for me as my template.yaml ...
example template
AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Description: example of node.js lambda function using layer for dependencies Resources: ExampleFunction: Type: AWS::Serverless::Function Properties: Runtime: nodejs8.10 CodeUri: nodejs/ Handler: src/event.handler Layers: - !Ref NodeModulesLayer NodeModulesLayer: Type: AWS::Serverless::LayerVersion Properties: Description: full set of function dependencies ContentUri: ./ CompatibleRuntimes: - nodejs6.10 - nodejs8.10 LicenseInfo: 'Available under the MIT-0 license.' RetentionPolicy: Retain pointing to local layer
The SAM developer guide includes a page on Working with Layers. At the time I'm writing this, they don't really get into how to reference layers at local file paths, and instead focus on references to remotely hosted layers.
The aspect I found tricky is that the directory structure of a node.js layer is expected to be ...
nodejs/ node_modules/ ... which means that in order for your locally installed node_modules directory to work as a layer, your package.json file must be nested inside a folder named nodejs.
Note the paths in the above example template.yaml:
ExampleFunction.Properties.CodeUriis set tonodejs/ExampleFunction.Properties.Handlershould be set to the path to your handler file, relative tonodejs/.NodeModulesLayer.Properties.ContentUriis set to the folder that contains both thetemplate.yamlfile and thenodejsdir.
Which means my example is assuming the following structure ...
nodejs/ node_modules/ src/ event.js package.json template.yaml preserve sam build support
One additional gotcha to be wary of ...
With respect to defining your function resource in template.yaml, there's some "flexibility" in terms of which parts of the path you put in CodeUri vs Handler. In some cases, doing ...
Properties: CodeUri: nodejs/src/ Handler: event.handler ... works just as well as doing ...
Properties: CodeUri: nodejs/ Handler: src/event.handler BUT, if you're using the sam build command, the former will NOT work. That command expects to find package.json inside of the CodeUri directory. So, stick with CodeUri: nodejs/ and use the Handler value to navigate through any additional folder hierarchy necessary to reaching your handler.
Try this, simple example how to set up lambda layer in nodejs:
In order to address your module you have to use path with '/opt' prefix. If it's yours file which you packaged to myLib.zip with myLib.js inside you should write:
const myModule = require('/opt/myLib); If you plugging installed dependency then you upload node_modules.zip with node_modules folder and address as:
const module = require('/opt/node_modules/lib_name'); Dependencies in 2022...
It was quite hard for me to figure out, whether this is still state-of-the-art or we can simplify dependencies now. Turns out it's possible now to include dependencies without hacks or complicated setup.
For me, creating the lambda with new lambda.NodeJsFunction() instead of new lambda.Function() did the trick. Yet, it was super hard for me to find a working sample. I decided to share a sample repo with you.
Sample repository
What it does:
- it has a lot of stuff you don't need to demonstrate the purpose (e.g. it produces a API Gateway based on a config.ts, so ignore that part if you don't need it)
- it allows you to use TS and ES6
- it allows you to use a dependency: i.e. I use node-fetch here
- I just do
cdk deployand no other bundling - according to the docu by default, all node modules are bundled except for aws-sdk.
- I don't know exactly how it works, but it does ;)
I hope this helps someone as I would have appreciated it.