Structure of a serverless application - node.js

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

Related

How do you create a new version of an API using serverless framework?

I have a simple API developed using serverless framework that is deployed in production. The serverless.yaml is similar to this one:
service: example
provider:
name: aws
plugins:
- serverless-plugin-scripts
- serverless-offline
functions:
package:
runtime: nodejs14.x
handler: build/index.handler
events:
- httpApi: "POST /test"
The API will change in the next version and I want to offer backward compatibility to my clients. I want to create a /v1/test route in API Gateway that will point to the new implementation of the function and I want /test to remain the same.
Is there a way to do this using serverless framework?
There are a few things you can do.
Create a new function entirely, with a new route. This option is simplest to implement, but your setup may not allow you to create a new function for some reason (CloudFormation stack limits, or other non-functional reasons).
functions:
# V0 package function
package:
runtime: nodejs14.x
handler: build/index.handler
events:
- httpApi: "POST /test"
# V1 package function
packageV1:
runtime: nodejs14.x
handler: build/index.handlerv1
events:
- httpApi: "POST v1/test"
Use the same function, but append a new path. Then inside your function code inspect the event payload to determine which path was called and use that to modify the response or functionality to adhere to either API spec.
package:
runtime: nodejs14.x
handler: build/index.handler # Both routes share the function, you'll need to modify logic to implement both v0/v1 spec
events:
- httpApi: "POST /test" # V0 route
- httpApi: "POST v1/test" # V1 route
Both of these are good for temporary migrations where you'll eventually deprecate the old API. If you need both in perpetuity, you could also migrate your v0 API into a new stack (or similarly create a new stack for the v1 API).
Lambda is priced per-invocation, not per-function. So with that in mind, I'd suggest creating a totally distinct function, that will make it easier to deprecate and delete when the time comes.

Serverless AWS Lambda project running on deploy

I have built a simple Node.JS application that consists of a single API endpoint. Deployment works and I can all the API endpoint as expected. However the handler function also runs on deployment of the code, which is problematic for me as handler function posts a tweet to twitter, and I don't want it to tweet every time I deploy a code update. I haven't been able to find anyone online reporting a similar problem, but I'm sure this would not be expected functionality.
This is my serverless.yml file (I created originally from the GitLab Node.JS serverless template here https://gitlab.com/gitlab-org/project-templates/serverless-framework/):
service: my-project
provider:
name: aws
region: ${env:AWS_REGION}
runtime: nodejs10.x
plugins:
- serverless-offline
- serverless-jest-plugin
- serverless-stack-output # Allows us to output endpoint url to json file
functions:
post:
handler: main.main
events:
- http:
path: post
method: post
custom:
output:
handler: main.main
file: stack.json
I believe the problem in your case is the fact that the handler you have for serverless-stack-output plugin is the same as for your function. That plugin explicitly calls the handler (https://github.com/sbstjn/serverless-stack-output#handler) which executes your function. If you will drop use of the plugin or just configure a different handler for it the problem should disappear

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.

Add and Update DNS Records in Route 53 to Cloudfront with Serverless Framework

I'm currently working on expanding this repo. It contains source code to deploy a sapper/svelte webapp with the Serverless Framework to AWS. I'm thinking about how to expand the serverless.yml to include Route53 DNS routing to Cloudfront, but I can't seem to wrap my head around it. The serverless-domain-manager plugin seems to be designed for Route53 to API Gateway routing, but I'm deploying my app only with Cloudfront, an S3 bucket and an Lambda#Edge handler server-side-rendering the application.
I've done something similar here.
I've trimmed the serverless.yml for the relevant parts:
WebAppCloudFrontDistribution:
Type: AWS::CloudFront::Distribution
DependsOn:
- WebAppS3Bucket
Properties:
DistributionConfig:
### content trimmed
Aliases:
- 'www.example.com'
### content trimmed
ViewerCertificate:
AcmCertificateArn: 'CertificateArn' # in the linked repo I'm using a plugin to auto generate the certificate
SslSupportMethod: sni-only
WebsiteDNSName:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: 'HostedZoneId' # Taken from the AWS Console
RecordSets:
- Name: 'www.example.com'
Type: A
AliasTarget:
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt [WebAppCloudFrontDistribution, DomainName]
EvaluateTargetHealth: false

Sharing code in AWS Lambda

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.

Resources