I published a post this summer with a few simple demo applications I use when configuring AWS infrastructure. I needed something similar for a serverless application on AWS. In other words, a Lambda function sitting behind an API Gateway or Application Load Balancer (ALB).
I tend to use this simple Node.js function. It will return whatever it received as input. This is useful when you are debugging the infrastructure. For example, configuring Cognito with API Gateway or OIDC with ALB.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  | 
exports.handler = async (event, context) => {
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "isBase64Encoded": false,
        "body": JSON.stringify(event) 
    };
};
  | 
 
Deploying this as a serverless function behind an API Gateway REST API is really easy. Here is a SAM template.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  | 
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
  SimpleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs14.x
      Timeout: 300
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Method: get
            Path: /
      InlineCode: |
        exports.handler = async (event, context) => {
            return {
                "statusCode": 200,
                "headers": {
                    "Content-Type": "application/json"
                },
                "isBase64Encoded": false,
                "body": JSON.stringify(event) 
            };
        };
  | 
 
Deploying an HTTP API is nearly identical. I simply change ApiEvent to HttpApiEvent.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  | 
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
  SimpleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs14.x
      Timeout: 300
      Events:
        HttpApiEvent:
          Type: HttpApi
          Properties:
            Path: /
            Method: GET
      InlineCode: |
        exports.handler = async (event, context) => {
            return {
                "statusCode": 200,
                "headers": {
                    "Content-Type": "application/json"
                },
                "isBase64Encoded": false,
                "body": JSON.stringify(event) 
            };
        };
  | 
 
The Application Load Balancer is a little more complicated. First, SAM templates don’t support ALB as a event source. Second, the ALB requires you specify subnets and a security group. I use the following template.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  | 
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Parameters:
  Subnets:
    Description: Subnets the Application Load Balancer should be deployed in
    Type: List<AWS::EC2::Subnet::Id>
  SecurityGroup:
    Description: Security Group applied to the Application Load Balancer
    Type: AWS::EC2::SecurityGroup::Id
Resources:
  SimpleFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs14.x
      Timeout: 300
      InlineCode: |
        exports.handler = async (event, context) => {
            return {
                "statusCode": 200,
                "headers": {
                    "Content-Type": "application/json"
                },
                "isBase64Encoded": false,
                "body": JSON.stringify(event) 
            };
        };
  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Subnets: !Ref Subnets
      SecurityGroups:
        - !Ref SecurityGroup
  LoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref LoadBalancerTargetGroup
  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt [ SimpleFunction, Arn ] 
      Action: 'lambda:InvokeFunction'
      Principal: elasticloadbalancing.amazonaws.com
  LoadBalancerTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckEnabled: false
      TargetType: lambda
      Targets:
      - Id: !GetAtt [ SimpleFunction, Arn ]
  |