Skip to content
This repository was archived by the owner on Apr 30, 2024. It is now read-only.

Multiple Lambda functions from the sam.yml file possible? #5

Open
joelfogue opened this issue Feb 16, 2018 · 37 comments
Open

Multiple Lambda functions from the sam.yml file possible? #5

joelfogue opened this issue Feb 16, 2018 · 37 comments

Comments

@joelfogue
Copy link

I have a situation where I have multiple lambda functions within our API Gateway's API.
I'm new to AWS and learning as much as I can.
I am wondering, how would I change the template yaml (sam.yaml) so that we can deploy to any number of lambda functions and not just one as is the case in this lab.
Would you create multiple index.js file per lambda function?

  • so index1.js -> will map to first lambda function
  • index2.js -> second lambda function;
  • etc....

And how do I update the sam.yaml file to reflect this change?

Here's a snippet code of what I'm trying to accomplish:

Resources:
  HelloWorld:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs4.3
      Role:
        Fn::ImportValue:
          !Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]
      Events:
        GetEvent:
          Type: Api
          Properties:
            Path: /
            Method: get
        PostEvent:
          Type: Api
          Properties:
            Path: /
            Method: post
.... and more endpoints here to hit more lambda functions? So do I create multiple index.js to map to each individual lambda function?
@andrewryan1906
Copy link

Guys,

I have the same question.

Really trying to learn serverless and questions like these - on best practices - is making it super difficult. Would love some direction here.

@dfloresgonz
Copy link

any help

@jairoVera
Copy link

jairoVera commented May 3, 2018

As far as I am aware, you can have multiple JS files, each containing a handler to a different Lambda. The files don't have to be in the same directory! You can also define the 2nd Lambda function in the same SAM template yaml file where you defined the 1st Lambda function.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Outputs the time

Resources:
  TimeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: firstsample/firstsample.handler   # firstsample.js file is in firstsample direcotory
      Role: !GetAtt BasicAWSLambdaRole.Arn
      Runtime: nodejs6.10
      CodeUri: ./                          # Look for code in the same directory as the output SAM file
      Events:
        MyTimeApi:
          Type: Api
          Properties:
            Path: /TimeResource
            Method: GET
  
  SecondSampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: secondsample.handler                  # didn't have to include secondsample directory
      Role: !GetAtt BasicAWSLambdaRole.Arn
      Runtime: nodejs6.10
      CodeUri: ./secondsample           # code is in the secondsample directory, located in same directory
      Events:
        MyTimeApi:
          Type: Api
          Properties:
            Path: /TextResource
            Method: GET

@red8888
Copy link

red8888 commented Sep 18, 2018

This is the top google result for "sam template multiple functions". Be nice to have airoVera's solution in the official docs if thats the way we should be doing this.

And how does this work with aws cloudformation package and aws cloudformation deploy ? Do I need to run that for each lambda/CodeUri?

@joeyshub
Copy link

@red8888 what I do is having my repo structure as below. I used Jenkins pipeline to do 'npm install' for my lambda functions, FYR.

├── 00_DEVOPS-test1
│   ├── index.js
│   ├── package.json
│   ├── package-lock.json
│   └── README.md
├── 00_DEVOPS-test2
│   ├── index.js
│   ├── package.json
│   ├── package-lock.json
│   └── README.md
├── Jenkinsfile
├── packaged.yaml
├── README.md
└── template.yaml

@hugoprudente
Copy link

hugoprudente commented Jan 25, 2019

@red8888, using the @jairoVera approach, as both AWS::Serverless::Function are on the same template only one sam package | aws cloudformation package must be executed, same for the deploy.

Regarding the first code if you have a main.js that check the origin of the request and it can route it Post to index-1.js and the Get to index-2.js using the main.js handler and directing to 2 "subhandlers"

@deleugpn
Copy link

@jairoVera do you know if that approach will generate 1 or 2 API Gateways?

@sam-mundle
Copy link

It will generate two separate API gateways but the issue is (at least when generating a testing environment locally) if you create separate functions that have separate handler files both of the handler files will be written into the build folder for BOTH Lambda functions. I'm not sure if this would happen when deploying though.

capture

@sertaco
Copy link

sertaco commented Feb 25, 2019

It seems SAM is intended for one lambda per application. For multi-function applications, there is Nested application method.

@red8888
Copy link

red8888 commented Mar 6, 2019

SAM does support multiple functions, the right way to do it is with the sam build command which pulls deps for all functions and creates a package that will be deployed as a "serverless application"

Maybe someone from the SAM team can reiterate this?

@estebansolo
Copy link

estebansolo commented Mar 29, 2019

@deleugpn it will generate two API Gateways, but you can add the API Gateway resource and It will create just one

Transform: AWS::Serverless-2016-10-31
Description: Outputs the time

Resources:
  ApiResource:
    Type: AWS::Serverless::Api
    Properties:
      StageName: prod  
  
  TimeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: firstsample/firstsample.handler   # firstsample.js file is in firstsample direcotory
      Role: !GetAtt BasicAWSLambdaRole.Arn
      Runtime: nodejs6.10
      CodeUri: ./                          # Look for code in the same directory as the output SAM file
      Events:
        MyTimeApi:
          Type: Api
          Properties:
            Path: /TimeResource
            Method: GET
            RestApiId: !Ref ApiResource
  
  SecondSampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: secondsample.handler                  # didn't have to include secondsample directory
      Role: !GetAtt BasicAWSLambdaRole.Arn
      Runtime: nodejs6.10
      CodeUri: ./secondsample           # code is in the secondsample directory, located in same directory
      Events:
        MyTimeApi:
          Type: Api
          Properties:
            Path: /TextResource
            Method: GET
            RestApiId: !Ref ApiResource```

@MatteoGioioso
Copy link

And how about shared libraries and code?

@estebansolo
Copy link

You can use Layers

@debendraoli
Copy link

debendraoli commented Mar 17, 2020

@jairoVera is life saver, I was banging on this problem for a week.

I have 13 serverless functions having go1.x and python run times and dependent to each others, previously I was managing the code bases and aws services manually, it was so pain, accidentally I discovered sam and I introduced one template file for all the functions but build sam was unable to find the code bases.

I specified CodeUri where my codes points.

Now I can easily debug locally, build and deploy in one click. :-)

@javiortizmol
Copy link

@jairoVera is life saver, I was banging on this problem for a week.

I have 13 serverless functions having go1.x and python run times and dependent to each others, previously I was managing the code bases and aws services manually, it was so pain, accidentally I discovered sam and I introduced one template file for all the functions but build sam was unable to find the code bases. Now I can easily debug locally, build and deploy in one click. :-)

So works or no? "but build sam was unable to find the code bases. Now I can easily debug locally, build and deploy in one click"

@debendraoli
Copy link

@jairoVera is life saver, I was banging on this problem for a week.
I have 13 serverless functions having go1.x and python run times and dependent to each others, previously I was managing the code bases and aws services manually, it was so pain, accidentally I discovered sam and I introduced one template file for all the functions but build sam was unable to find the code bases. Now I can easily debug locally, build and deploy in one click. :-)

So works or no? "but build sam was unable to find the code bases. Now I can easily debug locally, build and deploy in one click"

I already told.

"Now I can easily debug locally, build and deploy in one click. :-)"

@gavetisyanca
Copy link

I have a different question. How do I have 1 lambda function with different events (different http methods) I don't want to create several lambda functions for each method its not interesting and not efficient. I want to be able trigger that function with either with boolean logic inside the actual script or even better edit this serverless.yml file so I can do that from yaml rather than inside the script
Please help me, will much appreciate that

@sromano88-svc
Copy link

It will generate two separate API gateways but the issue is (at least when generating a testing environment locally) if you create separate functions that have separate handler files both of the handler files will be written into the build folder for BOTH Lambda functions. I'm not sure if this would happen when deploying though.

capture

Yes, it happens when you deploy it. You can see it in the AWS Lambda console.
Were you (or someone) able to isolate the functions handlers when deploying?

In my case, when running sam build, it executes the npm install for each function (Running NodejsNpmBuilder:NpmInstall) and it takes a lot of time. This is the main issue I am trying to avoid.

@sromano88-svc
Copy link

I have a different question. How do I have 1 lambda function with different events (different http methods) I don't want to create several lambda functions for each method its not interesting and not efficient. I want to be able trigger that function with either with boolean logic inside the actual script or even better edit this serverless.yml file so I can do that from yaml rather than inside the script
Please help me, will much appreciate that

This is what I do:

EcommerceFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ecommerce-lambda
      Handler: src/handlers/ecommerceHandler.handler
      Runtime: nodejs12.x
      Description: Products Lambda function.
      Policies:
        - AWSLambdaBasicExecutionRole
        - AmazonDynamoDBFullAccess
      Environment:
        Variables:
          LAMBDA_ENVIRONMENT: local
      Events:
        # Server
        GetServiceRunningAPI:
          Type: Api
          Properties:
            Path: /api/products-health-check
            Method: GET
        # Products
        GetProductsAPI:
          Type: Api
          Properties:
            Path: /api/products
            Method: GET
        GetSingleProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: GET
        PostProductsAPI:
          Type: Api
          Properties:
            Path: /api/products
            Method: POST
        PutProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: PUT
        DeleteProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: DELETE

@emaayan
Copy link

emaayan commented Nov 29, 2020

Hi, how does this work with java maven reactor build, is every module considered a lambda function? Is a deployment package generated from the root module contains all lambda on one single jar?

@jesstucker
Copy link

This is what I do:

...
        PutProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: PUT
        DeleteProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: DELETE

Only example I've been able to find where multiple http methods are specified from the same endpoint. Thank you!

@afern247
Copy link

This is what I do:

...
        PutProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: PUT
        DeleteProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: DELETE

Only example I've been able to find where multiple http methods are specified from the same endpoint. Thank you!

The million dollar question, how do you do it multiple paths and methods for HTTP APIs?

@estebansolo
Copy link

I have a different question. How do I have 1 lambda function with different events (different http methods) I don't want to create several lambda functions for each method its not interesting and not efficient. I want to be able trigger that function with either with boolean logic inside the actual script or even better edit this serverless.yml file so I can do that from yaml rather than inside the script
Please help me, will much appreciate that

This is what I do:

EcommerceFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ecommerce-lambda
      Handler: src/handlers/ecommerceHandler.handler
      Runtime: nodejs12.x
      Description: Products Lambda function.
      Policies:
        - AWSLambdaBasicExecutionRole
        - AmazonDynamoDBFullAccess
      Environment:
        Variables:
          LAMBDA_ENVIRONMENT: local
      Events:
        # Server
        GetServiceRunningAPI:
          Type: Api
          Properties:
            Path: /api/products-health-check
            Method: GET
        # Products
        GetProductsAPI:
          Type: Api
          Properties:
            Path: /api/products
            Method: GET
        GetSingleProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: GET
        PostProductsAPI:
          Type: Api
          Properties:
            Path: /api/products
            Method: POST
        PutProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: PUT
        DeleteProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: DELETE

This is the way

@keithics
Copy link

one handler for all of the products API?

@sromano88-svc
Copy link

one handler for all of the products API?

Yes, I had that approach initially (one handler for all of the products API).
Because on develop building several functions for each change was not possible.
Right now I have moved 1 function per API endpoint.

Comment 1:
Right now (Feb 2021 - and a couple of versions before), sam build has a --cached and --parallel parameters that helps to build faster

Comment 2:
We have moved to typescript with a webpack solution, so real hot-reload is achieved even without building (sam build)

@iongion
Copy link

iongion commented Jun 2, 2021

@sromano88-svc @estebansolo That looks so so nice!

@iongion
Copy link

iongion commented Jun 2, 2021

one handler for all of the products API?

Yes, I had that approach initially (one handler for all of the products API).
Because on develop building several functions for each change was not possible.
Right now I have moved 1 function per API endpoint.

Comment 1:
Right now (Feb 2021 - and a couple of versions before), sam build has a --cached and --parallel parameters that helps to build faster

Comment 2:
We have moved to typescript with a webpack solution, so real hot-reload is achieved even without building (sam build)

How do you do that ? What is your file-system structure ? I have webpack too and plan to use typescript

@hughesjj
Copy link

From #5 (comment)

I have a different question. How do I have 1 lambda function with different events (different http methods) I don't want to create several lambda functions for each method its not interesting and not efficient. I want to be able trigger that function with either with boolean logic inside the actual script or even better edit this serverless.yml file so I can do that from yaml rather than inside the script
Please help me, will much appreciate that

This is what I do:

EcommerceFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ecommerce-lambda
      Handler: src/handlers/ecommerceHandler.handler
      Runtime: nodejs12.x
      Description: Products Lambda function.
      Policies:
        - AWSLambdaBasicExecutionRole
        - AmazonDynamoDBFullAccess
      Environment:
        Variables:
          LAMBDA_ENVIRONMENT: local
      Events:
        # Server
        GetServiceRunningAPI:
          Type: Api
          Properties:
            Path: /api/products-health-check
            Method: GET
        # Products
        GetProductsAPI:
          Type: Api
          Properties:
            Path: /api/products
            Method: GET
        GetSingleProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: GET
        PostProductsAPI:
          Type: Api
          Properties:
            Path: /api/products
            Method: POST
        PutProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: PUT
        DeleteProductsAPI:
          Type: Api
          Properties:
            Path: /api/products/{productId}
            Method: DELETE

This is the way

If we need multiple languages on the same API path (we have a library which only exists in python, but our code is otherwise kotlin, and we need time to port the library to jvm), is there a way to do so?

Or would we be forced to use a separate API in this case?

(also in general I'm curious if there's a way to use the --use-container flag with sam build for heterogeneous runtimes without specifying a custom docker image)

@estebansolo
Copy link

@hughesjj This example shows how you can use the same lambda function (also language) with different endpoints.

If you want to use the same api with different endpoints and every of them with a different language, you can do it easily, check a previous comment where there are two functions with the same api, all you have to do is change the runtime and do what you need to do

@ninjasujan
Copy link

ninjasujan commented Jun 16, 2021

@jairoVera's solution worked for me I just created two resource in same templete.yml file.
here is my folder structure
But node dependency will be heavy worried about optimization in build process.

Root Dir
  api1 - api1 folder
      index.js - handler file
     node_modules/
 api2 - api2 folder
    index.js - handler file
    node_modules/
template.yml file

** yml file **

Globals:
  Function:
    Timeout: 100
    AutoPublishAlias: live
    DeploymentPreference:
      Enabled: true
      Type: Canary10Percent5Minutes
      Role: !Ref CodeDeployRole

Resources:
  api1:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub <Fn name>
      Handler: index.handler
      Runtime: nodejs12.x
      CodeUri: api1/
      Environment:
        Variables:
          NODE_ENV: ""
          MONGO_URI: ""
      Role:
        Fn::GetAtt:
          - LambdaExecutionRole
          - Arn
      Events:
        GetEvent:
          Type: Api
          Properties:
            Path: /
            Method: get
  api2:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub <Fn Name>
      Handler: index.handler
      Runtime: nodejs12.x
      CodeUri: api2/
      Environment:
        Variables:
          NODE_ENV: ""
          MONGO_URI: ""
      Role:
        Fn::GetAtt:
          - LambdaExecutionRole
          - Arn
      Events:
        GetEvent:
          Type: Api
          Properties:
            Path: /get-user
            Method: get

@sromano88-svc
Copy link

one handler for all of the products API?

Yes, I had that approach initially (one handler for all of the products API).
Because on develop building several functions for each change was not possible.
Right now I have moved 1 function per API endpoint.
Comment 1:
Right now (Feb 2021 - and a couple of versions before), sam build has a --cached and --parallel parameters that helps to build faster
Comment 2:
We have moved to typescript with a webpack solution, so real hot-reload is achieved even without building (sam build)

How do you do that ? What is your file-system structure ? I have webpack too and plan to use typescript

Sorry for the delay.... I have moved from this AWS project so I missed part of the code, but here are some parts....
In package.json I had this two scripts lines

        "build": "webpack --watch",
        "start": "sam local start-api --warm-containers EAGER --skip-pull-image",

And the structure was
.
└── src
├── handlers
├── services
├── tests
└── utils

Also I am attaching the webpack.config.js file (renamed as txt).
webpack.config.js.txt

@saadzaman
Copy link

@ninjasujan What about package.json ? can it be removed ?

@ninjasujan
Copy link

@saadzaman you need to keep package.json file as well for each lambda fn.

but after making detail research on SAM and the use case of the applications, I decided to change my approach, now I'm creating separate SAM package for each lambda function, if you have some small function like handlers or authentication (Lambda authorizer then you can create 2 functions in sam file).
Its completely my use-case and approach you can use it depending on your requirement.

@noopd13
Copy link

noopd13 commented Jan 24, 2022

Hello guys, i need to have single api gateway with dev and prod stages, which will be triggered by multiple lambda function according to stages
any help would be appreciated

@iongion
Copy link

iongion commented Jan 24, 2022

@noopd13 what do you mean by triggered ? You mean the client code calling the api will decide if it invokes staging or prod / dev environments ?

The typical way to solve this is using stage urls, AWS has full support for this.

Try to avoid exposing your non production environments to the world also, non-production code might contain bugs, which might bring security issues.

@noopd13
Copy link

noopd13 commented Jan 25, 2022

@iongion , Yes the client will decide , based on the base path, I need to use the same api gateway
for the both the environment(stage lambda and prod lambda ).I am having hard time writing yml for it

@jmactibold
Copy link

Autant que je sache, vous pouvez avoir plusieurs fichiers JS, chacun contenant un gestionnaire pour un Lambda différent. Les fichiers ne doivent pas nécessairement se trouver dans le même répertoire ! Vous pouvez également définir la 2ème fonction Lambda dans le même fichier yaml de modèle SAM où vous avez défini la 1ère fonction Lambda.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Outputs the time

Resources:
  TimeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: firstsample/firstsample.handler   # firstsample.js file is in firstsample direcotory
      Role: !GetAtt BasicAWSLambdaRole.Arn
      Runtime: nodejs6.10
      CodeUri: ./                          # Look for code in the same directory as the output SAM file
      Events:
        MyTimeApi:
          Type: Api
          Properties:
            Path: /TimeResource
            Method: GET
  
  SecondSampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: secondsample.handler                  # didn't have to include secondsample directory
      Role: !GetAtt BasicAWSLambdaRole.Arn
      Runtime: nodejs6.10
      CodeUri: ./secondsample           # code is in the secondsample directory, located in same directory
      Events:
        MyTimeApi:
          Type: Api
          Properties:
            Path: /TextResource
            Method: GET

Thank you very much for the answer. Do you know if it is possible to host the deployment packages of all the lambdas on a remote repository (Github for example) so that the CodeURI property in the yaml file for each resource retrieves the content it needs to build the lambda? Thanks in advance

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests