Sharing code in AWS Lambda - node.js

What is the prefered way to share code between AWS Lambda functions?
I have a structure like this:
functions
a
node_modules
index.js
package.json
b
node_modules
index.js
package.json
c
node_modules
index.js
package.json
This let every function keep its own node_modules and I can package the whole thing with the CLI.
But what about custom code that needs to be shared?
I can require("../mylibrary") but the package command would still not include it.

As dmigo mentioned already it's possible with Lambda layers. Here is some SAM template code for using the Lambda Layer Code:
Globals:
Function:
Runtime: nodejs8.10
MyFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: a/
Handler: index.handler
Layers:
- !Ref MySharedLayer
MySharedLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: SharedLayer
Description: Some code to share with the other lambda functions
ContentUri: layer/
CompatibleRuntimes:
- nodejs8.10
RetentionPolicy: Retain

Now you can use Layers to share libraries and code between your Functions. You can create a Layer from a zip file the same way you do that for a Function.
The layer package will look more or less like this:
my-layer.zip
└ nodejs/node_modules/mylibrary
If you create your Functions on top of this Layer then in the code it can be referenced like this:
const shared = require('mylibrary');
It is worth noticing that Layers support versioning and relate to Functions as many-to-many. Which makes them second npm.

Try serverless framework, you can use the include/exclude artifacts without having to write your own scripts.
checkout serverless.com
I also use private node packages, but they need to be installed before sls deploy.

The problem with Lambda is, you have to deploy a ZIP-file, so all your code needs to be accesible from one root directory.
I solved this with a script that copies my shared code to all the Lambda-function directories before I archive every single function and upload them.
Symlinks are probably also possible.

Related

How to share my own custom fucntions on AWS lambda nodejs

I Currently have a project in AWS with several lambda functions, most of the functions in NodeJS, I want to know if is there a way to create a lambda layer with my own code functions that I use in different lambdas without publish it in npm, I already search in old questions in stack question-1 question-2, but these were not answered
Thanks for help!
create a folder in your local machine called nodejs
put your "shared" logic in that folder like /nodejs/shared.js
you can zip this nodejs folder and upload as a layer
in your lambda code require the shared.js as const shared = require('/opt/nodejs/shared.js')
Links:
Lambda layers: https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html
Detailed guide: https://explainexample.com/computers/aws/aws-lambda-layers-node-app
Using layers with SAM: https://docs.aws.amazon.com/serverlessrepo/latest/devguide/sharing-lambda-layers.html

How to emulate AWS Parameter Store on local computer for lambda function development?

I'm using Serverless framework and NodeJS to develop my AWS Lambda function. So far, I have used .env file to store my secrets. So, I can get access to them in serverless.yml like this
provider:
...
environment:
DB_HOST: ${env:DB_HOST}
DB_PORT: ${env:DB_PORT}
But now I need to use AWS Parameter Store instead of .env file. I have tried to find information about how to emulate it on my local machine, but I couldn't.
I think, I have to use one serverless config file on local and staging. I need a way to select somehow env values either from .env file (if it's local machine) or from Parameter Store (if it's AWS Lambda). Is there any way how to do it? Thanks!
It should work like this: within your serverless.yml you can reference .env parameters with ${env:keyname} and AWS Parameters using the ${param:keyname} syntax.
If you need to support both of them you just need to write ${env:keyname, param:keyname}.
Here's an example:
provider:
...
environment:
ALLOWED_ORIGINS: ${env:ALLOWED_ORIGINS, param:ALLOWED_ORIGINS}
AUTHORIZER_ARN: ${env:AUTHORIZER_ARN, param:AUTHORIZER_ARN}
MONGODB_URL: ${env:MONGODB_URL, param:MONGODB_URL}

Moving existing lambda edge to Serverless Framework

I have a Lambda Edge attached to a CloudFront distribution. What I want to do is use Serverless Framework to publish the lambda (instead of manually uploading files and click on "Deploy to Lambda#Edge"). What I've tried to do, looking at the serverless documentation, is add this yml file to the project and run the deployment script
service: cloudfront-service
provider:
name: aws
runtime: nodejs10.x
functions:
cfLambda:
handler: index.handler
events:
- cloudFront:
eventType: origin-request
origin: <CloudFront-Origin-ID>
This deployed the Lambda but it didn't attached it to CloudFront (it hasn't been published and there is no versions or triggers related). So how can I do this, using an existing CloudFront distribution?
This plugin #silvermine/serverless-plugin-cloudfront-lambda-edge will not help if you want to use an existing cloud front distribution. It is only helpful if you are going to create a new one.
This issue has been already reported and as per the forum, this functionality they are not supporting.
Lambda#Edge with Serverless-Framework is quite easy. We use this plugin.
plugins:
- '#silvermine/serverless-plugin-cloudfront-lambda-edge'
Please go directly to the plugin author's website for complete examples: https://github.com/silvermine/serverless-plugin-cloudfront-lambda-edge
Base on your implementation you have a wrong indentation so I think it wont really attach it to your cloudfront. Having a wrong indetation will not create an events on your lambda function so intead of this
events:
- cloudFront:
eventType: origin-request
origin: <CloudFront-Origin-ID>
Do this:
events:
- cloudFront:
eventType: origin-request
origin: <CloudFront-Origin-ID>
I hope that this will solve your problem. Because I encounter this wrong indentation myself and wander why it is not being implemented properly.

SAM Lambda Layer module not found for shared nodejs code

I'm defining multiple lambda functions in a single template.yaml. These functions have some common code, but not published modules. I assumed I could turn this common stuff into a versioned layer. With a directory to the effect as follows:
Project
LambdaFunc1
package.json
node_modules
func1.js
LambdaFunc2
package.json
node_modules
func2.js
common-stuff
package.json
my-common.js
template.yaml
node_modules
After testing, I copy common-stuff into the Projects/node_modules directory and my other LambdaFuncs resolve require('common-stuff') based on Node moving up the directory structure for not found modules.
To have SAM do the build/package/deploy, I noticed SAM doesn't touch the common-stuff however creates an .aws-sam/build structure with the two other Lambda functions. I had to create a structure for SAM's CodeURI to zip up.
Package/common-stuff/packaged/nodejs/node_modules/common-stuff/ with my package.json and my-common.js.
My package.json uses name: "common-stuff", main: "my-common.js"
There are no other files - nothing under nodejs as I'm only packaging the modules. This appears to me the reason for Layers. I have verified SAM packages a zip file containing nodejs/node_modules/common-stuff/... by downloading the Layer zip file.
In the Lambda function template def, I add the permission to allow 'lambda:GetLayerVersion'. When I view the Lambda function in the console, I see this permission along with the others.
Interestingly, aws lambda get-layer-version-policy --layer-name MyLayer --version-number 8 --output text
returns an error that there are no policies attached. My guess is that is because I've directly added it to the function, as I see it on the Lambda function with the correct Allow/GetLayerVersion.
This would seem to satisfy what I've read, however Node doesn't find the module. CloudWatch logs just say it can't find the module, nothing about permissions or syntax. Also, these functions worked until I added the Layer approach.
'sam local start-api' doesn't work either, same error. When I look in the Windows 10 default layer cache directory C:\Users\me\AppData\Roaming\AWS SAM\ there is an empty layers-pkg directory.
Is there some other magic I'm missing? Is there a better approach for sharing common code across Node Lambda functions?
I can't tell if AWS can't get the Layer, or the zip structure is wrong, or the require('common-stuff') is different (hope not).
Scott

Structure of a serverless application

I am new to serverless application. I followed the aws tutorial to build a simple nodejs serverless app with codestar and lambda.
However, imagine this node app does multiple things. In consequence, it has mutiple functions inside index.js, one for functionnality A, one for functionnality B, etc (for example).
Do I have to attach multiple lambda expressions, one for each functionality, to this codestar project?
Question: Do I have to attach multiple lambda expressions, one for each functionality, to this codestar project?
Answer: Yes
AWS CodeStar Project Details:
AWS Code star project contains below file structure(reference link):
README.md - this file
buildspec.yml - this file is used by AWS CodeBuild to package your service for deployment to AWS Lambda
app.js - this file contains the sample Node.js code for the web service
index.js - this file contains the AWS Lambda handler code
template.yml - this file contains the Serverless Application Model (SAM) used by AWS Cloudformation to deploy your service to AWS Lambda and Amazon API Gateway.
Assume you have the template.yml file like below:
AWSTemplateFormatVersion: 2010-09-09
Transform:
- AWS::Serverless-2016-10-31
- AWS::CodeStar
Resources:
HelloWorld:
Type: AWS::Serverless::Function
Properties:
Handler: index.first_handler
Runtime: nodejs4.3
Role:
Fn::ImportValue:
!Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]
Events:
GetEvent:
Type: Api
Properties:
Path: /first
Method: get
HelloWorld2:
Type: AWS::Serverless::Function
Properties:
Handler: index.second_handler
Runtime: nodejs4.3
Role:
Fn::ImportValue:
!Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]
Events:
GetEvent:
Type: Api
Properties:
Path: /second
Method: get
Notice that, in above tamplate.yml file specified "HelloWorld" and "HelloWorld2" configurations.
HelloWorld configuration contains "Handler" value as "index.first_handler" meaning that "index" is the filename of index.js and first_handler is the method in index.js file.
Likewise, HelloWorld2 configuration contains "Handler" value as "index.second_handler" meaning that "index" is the filename of index.js and second_handler is the method in index.js file.
Conclusion:
You can specify any number of lambda functions in your index.js (whatever.js) file. Only you need to specify the proper Handler to identify the app your lambda function.
Hope this is the answer to your question. Feel free to ask doubts, if you have!
you don't need multiple handler functions (index.js) and you cannot have multiple handler functions. If the different functionality is doing the logically separated job then you can add multiple JS files and write functions there but you should refer that to your handler function (index.js). Alternatively, you can write functionality in index.js itself but better idea and clean code is to separate logically different functionality to another file and refer it

Resources