AWS CLI tool - AWS SAM CLI

AWS SAM CLI is a AWS CLI tool that allows you to develop, test and analysis your application in the local environment.

In MAC environment, install SAM tool by brew:

brew tap aws/tap
brew install aws-sam-cli
sam --version

If your SAM tool already exists, can execute the following command to update:

brew upgrade aws-sam-cli

Install AWS Toolkit plugin

AWS Toolkit

Lambda Boilerplate Snippet for VS Code

After installing plugins, you can start to create AWS serverless application locally.

In VS Code, press Shift+Command+P and search AWS: Create Lambda SAM Application

AWS VS Code local develop tool

(Here we are using Mac M1)

Lambda supports two types of deployment packages, for example in NodeJS with: File archives(.zip): nodejs16.x Container images: nodejs16.x (image)

Here we select items nodejs16.x, arm64, AWS SAM Hello World, current-path, my-app.

Install and test helloworld

After installing the hello world application, cd hello-world/ and run npm install

next, go to the test folder to install Mocha test framework test tool and run testing:

cd test/
npm install
npm run test

Run lambda function locally

In the my-app path run the following commands:

sam local invoke -e events/event.json

Will output the following content:

Invoking app.lambdaHandler (nodejs16.x)
Skip pulling image and use local one: public.ecr.aws/sam/emulation-nodejs16.x:rapid-1.60.0-arm64.

Mounting /local-lambda/my-app/hello-world as /var/task:ro,delegated inside runtime container
START RequestId: xxx-41ba-4f99-86b4-958478ee08c8 Version: $LATEST
{"statusCode":200,"body":"{\"message\":\"hello world\"}"}END RequestId: f7b63d4c-41ba-4f99-86b4-958478ee08c8
REPORT RequestId: xxx-41ba-4f99-86b4-958478ee08c8  Init Duration: 0.10 ms  Duration: 101.99 ms     Billed Duration: 102 ms Memory Size: 128 MB  Max Memory Used: 128 MB

Mock the Event JSON from SQS source

Here we change the events/event.json content to mock the member subscription event message from SQS source:

{
  "Records": [
    {
      "messageId": "xxx-xxx-xxx",
      "receiptHandle": "d",
      "body": "{\"id\":\"1\",\"type\":\"SUBSCRIPTION\",\"has_payload\":true,\"payload\":{\"@type\":\"t\",\"id\":\"32\",\"type\":\"BASIC_PLAN\",\"status\":\"START_REGISTED\",\"amount\":{\"currency_code\":\"TWD\",\"units\":\"100\",\"nanos\":0},\"tax\":{\"currency_code\":\"TWD\",\"units\":\"0\",\"nanos\":0},\"user_id\":\"3445\",\"subscription_id\":\"\",\"create_time\":\"2022-10-20T10:45:19.363Z\",\"update_time\":\"2022-10-20T10:45:19.363Z\"}}",
      "attributes": {
        "ApproximateReceiveCount": "1",
        "SentTimestamp": "1666302877792",
        "SenderId": "xxxx",
        "ApproximateFirstReceiveTimestamp": "1666302878792"
      },
      "messageAttributes": {},
      "md5OfBody": "xxxx",
      "eventSource": "aws:sqs",
      "eventSourceARN": "arn:aws:sqs:ap-northeast-1:xxx:xxxxsqsname_SQS",
      "awsRegion": "ap-northeast-1"
    }
  ]
}

Change the lambda app.js function content to output SQS body:

exports.lambdaHandler = async (event) => {
    console.log("==output event body==");
    let body = event.Records[0].body;
    console.log(body);
    try {
        return {
            'statusCode': 200,
            'body': JSON.stringify({
                message: 'hello world',
            })
        }
    } catch (err) {
        console.log(err);
        return err;
    }
};

Excute local command:

sam local invoke -e events/event.json

Output:

Invoking app.lambdaHandler (nodejs16.x)
Skip pulling image and use local one: public.ecr.aws/sam/emulation-nodejs16.x:rapid-1.60.0-arm64.

Mounting /local-lambda/my-app/hello-world as /var/task:ro,delegated inside runtime container
START RequestId: xxx-16c5-4003-a486-58905c76a699 Version: $LATEST
2022-10-21T09:12:07.835Z        xxx-16c5-4003-a486-58905c76a699    INFO    ==output event body==
2022-10-21T09:12:07.836Z        xxx-16c5-4003-a486-58905c76a699    INFO    {"id":"1","type":"SUBSCRIPTION","has_payload":true,"payload":{"@type":"t","id":"32","type":"BASIC_PLAN","status":"START_REGISTED","amount":{"currency_code":"TWD","units":"100","nanos":0},"tax":{"currency_code":"TWD","units":"0","nanos":0},"user_id":"3445","subscription_id":"","create_time":"2022-10-20T10:45:19.363Z","update_time":"2022-10-20T10:45:19.363Z"}}
END RequestId: xxx-16c5-4003-a486-58905c76a699
REPORT RequestId: xxx-16c5-4003-a486-58905c76a699  Init Duration: 0.25 ms  Duration: 112.33 ms     Billed Duration: 113 ms Memory Size: 128 MB  Max Memory Used: 128 MB
{"statusCode":200,"body":"{\"message\":\"hello world\"}"}%    

Mock Lambda Environment Variables Locally

Here declare our local lambda environment variables like: “HOST”, “PATH”, “STAG” in a env.json file

(Here we use Parameters is means the globally apply variables, and here can change to function name. Can refer here)

env/env.json

{
    "Parameters": {
        "HOST": "localtable",
        "PATH": "/user/subscription",
        "STAGE": "testing"
    }
}

Those environments need to be declared in the cloudformation template file, open, and modify the template. yaml to add Environment.Variables with those variable

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  testing_payment_integration_lambda

  Sample SAM Template for testing_payment_integration_lambda

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs16.x
      Architectures:
        - arm64
      Environment:
        Variables:
          HOST: host
          PATH: path
          STAGE: staging
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

....

Open app.js and using the process.env get environment variables:

exports.lambdaHandler = async (event) => {
    console.log("==output event body==");
    let body = event.Records[0].body;
    console.log(body);
    console.log("==output env variables==");
    let env = JSON.stringify(process.env.HOST);
    console.log(env);
    console.log("===stop");
    try {
        return {
            'statusCode': 200,
            'body': JSON.stringify({
                message: 'hello world',
            })
        }
    } catch (err) {
        console.log(err);
        return err;
    }
};

Finally, execute the following command --env-vars file_name (or -n file_name) can override the env.json to global environment variables in the template

sam local invoke --env-vars env/env.json -e events/event.json

REference

Install the AWS Toolkit for VSCode

Invoking Lambda functions locally