

# Getting Started with AWS Solutions Constructs
<a name="getting-started-with-aws-solutions-constructs"></a>

This topic describes how to install and configure AWS Cloud Development Kit (AWS CDK), AWS Solutions Constructs, and create your first AWS CDK app using AWS Solutions Constructs patterns.

**Tip**  
Want to dig deeper? Try the [CDK Workshop](https://cdkworkshop.com/) for a more in-depth tour of a real-world project.

**Tip**  
For more information about getting started with the AWS Cloud Development Kit (AWS CDK), refer to the [AWS CDK Developer Guide](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html).

## Prerequisites
<a name="prerequisites"></a>

AWS Solutions Constructs is built upon the AWS CDK, so you need to install Node.js (>= 10.3.0), even those working in languages other than TypeScript or JavaScript. This is because the [AWS CDK](https://github.com/aws/aws-cdk) and AWS Solutions Constructs are developed in TypeScript and run on Node.js. The bindings for other supported languages use this backend and toolset.

You must provide your credentials and an AWS Region to use the AWS CDK CLI, as described in Specifying Your Credentials and Region.

Other prerequisites depend on your development language, as follows.


| Language | Prerequisites | 
| --- | --- | 
|   ![\[Python\]](http://docs.aws.amazon.com/solutions/latest/constructs/images/python.png)Python  |  Python >= 3.6  | 
|   ![\[TypeScript\]](http://docs.aws.amazon.com/solutions/latest/constructs/images/typescript.png)TypeScript  |  TypeScript >= 2.7  | 
|   ![\[Java\]](http://docs.aws.amazon.com/solutions/latest/constructs/images/java.png)Java  |  Java >= 1.8  | 

## Installing the AWS CDK
<a name="installing-the-aws-cdk"></a>

To install and configure the AWS CDK, please refer to the AWS CDK Developer Guide - [Installing the AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_install).

## Working with AWS Solutions Constructs
<a name="working-with-the-aws-solutions-constructs-library"></a>

The typical workflow for creating a new app when working with AWS Solutions Constructs follows the same approach as the AWS CDK.

1. Create the app directory.

1. Initialize the app.

1. Add the AWS Solutions Constructs pattern dependencies.

1. Add additional code to the app.

1. Compile the app, if necessary.

1. Deploy the resources defined in the app.

1. Test the app.

If there are any issues, loop through modify, compile (if necessary), deploy, and test again.

# Walkthrough - Part 1
<a name="walkthrough-part-1-v2"></a>

This tutorial walks you through how to create and deploy a simple "Hello Constructs" AWS CDK app that uses a pattern from AWS Solutions Constructs, from initializing the project to deploying the resulting AWS CloudFormation template. The Hello Constructs app will create the following simple solution:

![\[Architecture Diagram\]](http://docs.aws.amazon.com/solutions/latest/constructs/images/tutorial-part1.png)


## Hello Constructs
<a name="hello-konstruk"></a>

Let’s get started building our first AWS CDK App using AWS Solutions Constructs.

**Note**  
This is a sample modification of `Hello CDK!` from the [CDK Workshop](https://cdkworkshop.com/) . If this is your first time using the AWS CDK, we recommend starting with this workshop for a hands-on walkthrough and how to leverage the CDK in building a real-world project.

## Creating the App Directory and Initializing the AWS CDK
<a name="creating-the-app-directory-and-initializing-the-aws-cdk"></a>

If you have not installed the AWS CDK yet, do so following the instructions [here](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html). Confirm you are using version 2.0 or higher of the AWS CDK by running this command.

```
cdk --version
```

Create a directory for your CDK app, and then create a AWS CDK app in that directory.

**Example**  

```
mkdir hello-constructs
cd hello-constructs
cdk init --language typescript
```

```
mkdir hello-constructs
cd hello-constructs
cdk init --language python
```

```
mkdir hello-constructs
cd hello-constructs
cdk init --language java
```

**Tip**  
Now’s a good time to open the project in your favorite IDE and explore. To learn more about the project structure, select the appropriate link:  
 [TypeScript](https://cdkworkshop.com/20-typescript/20-create-project/300-structure.html) 
 [Python](https://cdkworkshop.com/30-python/20-create-project/300-structure.html) 
 [Java](https://cdkworkshop.com/50-java/20-create-project/300-structure.html) 

## Install project base dependencies
<a name="update-project-base-dependencies-to-use-aws-cdk"></a>

Install the projects base dependencies.

**Example**  

```
npm install
```

```
source .venv/bin/activate
pip install -r requirements.txt
```

```
mvn install
```

Build and run the app and confirm that it creates an empty stack.

**Example**  

```
npm run build
cdk synth
```

```
cdk synth
```

```
mvn package
cdk synth
```

You should see a stack like the following, where `CDK-VERSION` is the version of the CDK. (Your output may differ slightly from what’s shown here – the metadata and parameters are omitted from this listing.)

**Example**  

```
Resources:
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
          Modules: aws-cdk=CDK-VERSION,@aws-cdk/core=VERSION_NUMBER,@aws-cdk/cx-api=VERSION_NUMBER,jsii-runtime=node.js/VERSION_NUMBER
```

```
Resources:
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
          Modules: aws-cdk=CDK-VERSION,@aws-cdk/core=VERSION_NUMBER,@aws-cdk/cx-api=VERSION_NUMBER,jsii-runtime=Python/VERSION_NUMBER
```
\$1

```
Resources:
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
          Modules: aws-cdk=CDK-VERSION,@aws-cdk/core=VERSION_NUMBER,@aws-cdk/cx-api=VERSION_NUMBER,jsii-runtime=Java/VERSION_NUMBER
```

## Lambda handler code
<a name="lambda-handler-code"></a>

We’ll start with the AWS Lambda handler code.

Create a directory `lambda` in the root of your project tree.

**Example**  
+ Add a file called `lambda/hello.js` with the following contents:

  ```
  exports.handler = async function(event) {
    console.log("request:", JSON.stringify(event, null, 2));
    return {
      statusCode: 200,
      headers: { "Content-Type": "text/plain" },
      body: `Hello, AWS Solutions Constructs! You've hit ${event.path}\n`
    };
  };
  ```
+ Add a file called `lambda/hello.py` with the following contents:

  ```
  import json
  
  def handler(event, context):
      print('request: {}'.format(json.dumps(event)))
      return {
          'statusCode': 200,
          'headers': {
              'Content-Type': 'text/plain'
          },
          'body': 'Hello, AWS Solutions Constructs! You have hit {}\n'.format(event['path'])
      }
  ```
+ Add a file called `lambda/hello.js` with the following contents:

  ```
  exports.handler = async function(event) {
    console.log("request:", JSON.stringify(event, null, 2));
    return {
      statusCode: 200,
      headers: { "Content-Type": "text/plain" },
      body: `Hello, AWS Solutions Constructs! You've hit ${event.path}\n`
    };
  };
  ```

This is a simple Lambda function which returns the text "Hello, Constructs\$1 You’ve hit [url path]". The function’s output also includes the HTTP status code and HTTP headers. These are used by API Gateway to formulate the HTTP response to the user. It’s in this function where you would insert logic for your application.

For more information on writing Lambda functions in your language of choice, refer to the [AWS Lambda documentation](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) .

## Install the AWS Solutions Constructs dependencies
<a name="install-the-aws-cdk-and-aws-solutions-constructs-library-dependencies"></a>

The AWS Solutions Constructs is shipped with an extensive library of constructs. The library is divided into modules, one for each well-architected pattern. For example, if you want to define an Amazon API Gateway Rest API that invokes an AWS Lambda function, we will need to use the `aws-apigateway-lambda` pattern library.

The AWS Lambda and Amazon API Gateway modules are installed with the AWS CDK.

Install the AWS Solutions Constructs `aws-apigateway-lambda` module and all its dependencies into our project:

**Example**  

```
npm install -s @aws-solutions-constructs/aws-apigateway-lambda
```

```
pip install aws_solutions_constructs.aws_apigateway_lambda
```
+ Edit the `pom.xml` file with the following information:
**Note**  
2.5.0 was the latest version of AWS Solutions Constructs at this writing. To find the latest version of AWS Solutions Constructs (and any other dependencies), use the command `mvn versions:display-dependency-updates` and update the pom.xml file.

  ```
  <properties>
    <solutionconstructs.version>2.5.0</solutionconstructs.version>
  </properties>
  
  <dependency>
      <groupId>software.amazon.awsconstructs</groupId>
      <artifactId>apigatewaylambda</artifactId>
      <version>${solutionconstructs.version}</version>
  </dependency>
  ```

  Run the command:

  ```
  mvn install
  ```

## Add an Amazon API Gateway/AWS Lambda pattern to your stack
<a name="add-an-aws-api-gatewayaws-lambda-pattern-to-your-stack"></a>

Now, let’s define the AWS Solutions Constructs pattern for implementing an Amazon API Gateway with an AWS Lambda proxy.

**Example**  
+ Replace the code in `lib/hello-constructs-stack.ts` with the following:

  ```
  import { Construct } from 'constructs';
  import { Stack, StackProps } from 'aws-cdk-lib';
  import * as lambda from 'aws-cdk-lib/aws-lambda';
  import * as api from 'aws-cdk-lib/aws-apigateway';
  import { ApiGatewayToLambda, ApiGatewayToLambdaProps } from '@aws-solutions-constructs/aws-apigateway-lambda';
  
  export class HelloConstructsStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
      super(scope, id, props);
  
      // The code that defines your stack goes here
      const api_lambda_props: ApiGatewayToLambdaProps = {
        lambdaFunctionProps: {
          code: lambda.Code.fromAsset('lambda'),
          runtime: lambda.Runtime.NODEJS_22_X,
          handler: 'hello.handler'
        },
        apiGatewayProps: {
          defaultMethodOptions: {
            authorizationType: api.AuthorizationType.NONE
          }
        }
      };
  
      new ApiGatewayToLambda(this, 'ApiGatewayToLambda', api_lambda_props);
    }
  }
  ```
+ Replace the code in `hello_constructs/hello_constructs_stack.py` with the following:

  ```
  from constructs import Construct
  from aws_cdk import (
      aws_lambda as _lambda,
      aws_apigateway as apigw,
      App,
      Stack
  )
  
  from aws_solutions_constructs import (
      aws_apigateway_lambda as apigw_lambda
  )
  
  class HelloConstructsStack(Stack):
  
      def __init__(self, scope: Construct, id: str, **kwargs) -> None:
          super().__init__(scope, id, **kwargs)
  
          # The code that defines your stack goes here
  
          apigw_lambda.ApiGatewayToLambda(
              self, 'ApiGatewayToLambda',
              lambda_function_props=_lambda.FunctionProps(
                  runtime=_lambda.Runtime.PYTHON_3_11,
                  code=_lambda.Code.from_asset('lambda'),
                  handler='hello.handler',
              ),
              api_gateway_props=apigw.RestApiProps(
                  default_method_options=apigw.MethodOptions(
                      authorization_type=apigw.AuthorizationType.NONE
                  )
              )
          )
  ```
+ Replace the code in `HelloConstructsStack.java` with the following:

  ```
  package com.myorg;
  
  import software.constructs.Construct;
  import software.amazon.awscdk.Stack;
  import software.amazon.awscdk.StackProps;
  
  import software.amazon.awscdk.services.lambda.*;
  import software.amazon.awscdk.services.lambda.Runtime;
  import software.amazon.awscdk.services.apigateway.*;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambda;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambdaProps;
  
  public class HelloConstructsStack extends Stack {
      public HelloConstructsStack(final Construct scope, final String id) {
          this(scope, id, null);
      }
  
      public HelloConstructsStack(final Construct scope, final String id, final StackProps props) {
          super(scope, id, props);
  
          new ApiGatewayToLambda(this, "ApiGatewayToLambdaPattern", new ApiGatewayToLambdaProps.Builder()
                  .lambdaFunctionProps(new FunctionProps.Builder()
                          .runtime(Runtime.NODEJS_22_X) // execution environment
                          .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                          .handler("hello.handler") // file is "hello", function is "handler"
                          .build())
                  .apiGatewayProps(new RestApiProps.Builder()
                          .defaultMethodOptions(new MethodOptions.Builder()
                                  .authorizationType(AuthorizationType.NONE)
                                  .build())
                          .build())
                  .build());
      }
  }
  ```

That’s it. This is all you need to do in order to define an API Gateway which proxies all requests to an AWS Lambda function. Let’s compare our new stack to the original one:

===

------
#### [ Typescript ]

```
npm run build
cdk diff
```

------
#### [ Python ]

```
cdk diff
```

------
#### [ Java ]

```
mvn package
cdk diff
```

===

------

The output should look like this:

```
Stack HelloConstructsStack
IAM Statement Changes
┌───┬────────────────┬────────┬────────────────┬────────────────┬───────────────────┐
│   │ Resource       │ Effect │ Action         │ Principal      │ Condition         │
├───┼────────────────┼────────┼────────────────┼────────────────┼───────────────────┤
│ + │ ${ApiGatewayTo │ Allow  │ lambda:InvokeF │ Service:apigat │ "ArnLike": {      │
│   │ LambdaPattern/ │        │ unction        │ eway.amazonaws │   "AWS:SourceArn" │
│   │ LambdaFunction │        │                │ .com           │ : "arn:${AWS::Par │
│   │ .Arn}          │        │                │                │ tition}:execute-a │
│   │                │        │                │                │ pi:${AWS::Region} │
│   │                │        │                │                │ :${AWS::AccountId │
│   │                │        │                │                │ }:${ApiGatewayToL │
│   │                │        │                │                │ ambdaPatternLambd │
│   │                │        │                │                │ aRestApiC0598E46} │
│   │                │        │                │                │ /${ApiGatewayToLa │
│   │                │        │                │                │ mbdaPattern/Lambd │
│   │                │        │                │                │ aRestApi/Deployme │
│   │                │        │                │                │ ntStage.prod}/*/* │
│   │                │        │                │                │ "                 │
│   │                │        │                │                │ }                 │
│ + │ ${ApiGatewayTo │ Allow  │ lambda:InvokeF │ Service:apigat │ "ArnLike": {      │
│   │ LambdaPattern/ │        │ unction        │ eway.amazonaws │   "AWS:SourceArn" │
│   │ LambdaFunction │        │                │ .com           │ : "arn:${AWS::Par │
│   │ .Arn}          │        │                │                │ tition}:execute-a │
│   │                │        │                │                │ pi:${AWS::Region} │
│   │                │        │                │                │ :${AWS::AccountId │
│   │                │        │                │                │ }:${ApiGatewayToL │
│   │                │        │                │                │ ambdaPatternLambd │
│   │                │        │                │                │ aRestApiC0598E46} │
│   │                │        │                │                │ /test-invoke-stag │
│   │                │        │                │                │ e/*/*"            │
│   │                │        │                │                │ }                 │
│ + │ ${ApiGatewayTo │ Allow  │ lambda:InvokeF │ Service:apigat │ "ArnLike": {      │
│   │ LambdaPattern/ │        │ unction        │ eway.amazonaws │   "AWS:SourceArn" │
│   │ LambdaFunction │        │                │ .com           │ : "arn:${AWS::Par │
│   │ .Arn}          │        │                │                │ tition}:execute-a │
│   │                │        │                │                │ pi:${AWS::Region} │
│   │                │        │                │                │ :${AWS::AccountId │
│   │                │        │                │                │ }:${ApiGatewayToL │
│   │                │        │                │                │ ambdaPatternLambd │
│   │                │        │                │                │ aRestApiC0598E46} │
│   │                │        │                │                │ /${ApiGatewayToLa │
│   │                │        │                │                │ mbdaPattern/Lambd │
│   │                │        │                │                │ aRestApi/Deployme │
│   │                │        │                │                │ ntStage.prod}/*/" │
│   │                │        │                │                │ }                 │
│ + │ ${ApiGatewayTo │ Allow  │ lambda:InvokeF │ Service:apigat │ "ArnLike": {      │
│   │ LambdaPattern/ │        │ unction        │ eway.amazonaws │   "AWS:SourceArn" │
│   │ LambdaFunction │        │                │ .com           │ : "arn:${AWS::Par │
│   │ .Arn}          │        │                │                │ tition}:execute-a │
│   │                │        │                │                │ pi:${AWS::Region} │
│   │                │        │                │                │ :${AWS::AccountId │
│   │                │        │                │                │ }:${ApiGatewayToL │
│   │                │        │                │                │ ambdaPatternLambd │
│   │                │        │                │                │ aRestApiC0598E46} │
│   │                │        │                │                │ /test-invoke-stag │
│   │                │        │                │                │ e/*/"             │
│   │                │        │                │                │ }                 │
├───┼────────────────┼────────┼────────────────┼────────────────┼───────────────────┤
│ + │ ${ApiGatewayTo │ Allow  │ sts:AssumeRole │ Service:lambda │                   │
│   │ LambdaPattern/ │        │                │ .amazonaws.com │                   │
│   │ LambdaFunction │        │                │                │                   │
│   │ ServiceRole.Ar │        │                │                │                   │
│   │ n}             │        │                │                │                   │
├───┼────────────────┼────────┼────────────────┼────────────────┼───────────────────┤
│ + │ ${ApiGatewayTo │ Allow  │ sts:AssumeRole │ Service:apigat │                   │
│   │ LambdaPattern/ │        │                │ eway.amazonaws │                   │
│   │ LambdaRestApiC │        │                │ .com           │                   │
│   │ loudWatchRole. │        │                │                │                   │
│   │ Arn}           │        │                │                │                   │
├───┼────────────────┼────────┼────────────────┼────────────────┼───────────────────┤
│ + │ *              │ Allow  │ xray:PutTeleme │ AWS:${ApiGatew │                   │
│   │                │        │ tryRecords     │ ayToLambdaPatt │                   │
│   │                │        │ xray:PutTraceS │ ern/LambdaFunc │                   │
│   │                │        │ egments        │ tionServiceRol │                   │
│   │                │        │                │ e}             │                   │
├───┼────────────────┼────────┼────────────────┼────────────────┼───────────────────┤
│ + │ arn:${AWS::Par │ Allow  │ logs:CreateLog │ AWS:${ApiGatew │                   │
│   │ tition}:logs:$ │        │ Group          │ ayToLambdaPatt │                   │
│   │ {AWS::Region}: │        │ logs:CreateLog │ ern/LambdaRest │                   │
│   │ ${AWS::Account │        │ Stream         │ ApiCloudWatchR │                   │
│   │ Id}:*          │        │ logs:DescribeL │ ole}           │                   │
│   │                │        │ ogGroups       │                │                   │
│   │                │        │ logs:DescribeL │                │                   │
│   │                │        │ ogStreams      │                │                   │
│   │                │        │ logs:FilterLog │                │                   │
│   │                │        │ Events         │                │                   │
│   │                │        │ logs:GetLogEve │                │                   │
│   │                │        │ nts            │                │                   │
│   │                │        │ logs:PutLogEve │                │                   │
│   │                │        │ nts            │                │                   │
├───┼────────────────┼────────┼────────────────┼────────────────┼───────────────────┤
│ + │ arn:${AWS::Par │ Allow  │ logs:CreateLog │ AWS:${ApiGatew │                   │
│   │ tition}:logs:$ │        │ Group          │ ayToLambdaPatt │                   │
│   │ {AWS::Region}: │        │ logs:CreateLog │ ern/LambdaFunc │                   │
│   │ ${AWS::Account │        │ Stream         │ tionServiceRol │                   │
│   │ Id}:log-group: │        │ logs:PutLogEve │ e}             │                   │
│   │ /aws/lambda/*  │        │ nts            │                │                   │
└───┴────────────────┴────────┴────────────────┴────────────────┴───────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}

Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}

Resources
[+] AWS::IAM::Role ApiGatewayToLambdaPattern/LambdaFunctionServiceRole ApiGatewayToLambdaPatternLambdaFunctionServiceRole0C123D8D
[+] AWS::IAM::Policy ApiGatewayToLambdaPattern/LambdaFunctionServiceRole/DefaultPolicy ApiGatewayToLambdaPatternLambdaFunctionServiceRoleDefaultPolicy253751F2
[+] AWS::Lambda::Function ApiGatewayToLambdaPattern//LambdaFunction ApiGatewayToLambdaPatternLambdaFunction5DC51B7E
[+] AWS::Logs::LogGroup ApiGatewayToLambdaPattern/ApiAccessLogGroup ApiGatewayToLambdaPatternApiAccessLogGroup9383E9FC
[+] AWS::ApiGateway::RestApi ApiGatewayToLambdaPattern/LambdaRestApi ApiGatewayToLambdaPatternLambdaRestApiC0598E46
[+] AWS::ApiGateway::Deployment ApiGatewayToLambdaPattern/LambdaRestApi/Deployment ApiGatewayToLambdaPatternLambdaRestApiDeployment4109DB93346ab5d96a64d161f4cf4f020d3cdf94
[+] AWS::ApiGateway::Stage ApiGatewayToLambdaPattern/LambdaRestApi/DeploymentStage.prod ApiGatewayToLambdaPatternLambdaRestApiDeploymentStageprodFDEB8074
[+] AWS::ApiGateway::Resource ApiGatewayToLambdaPattern/LambdaRestApi/Default/{proxy+} ApiGatewayToLambdaPatternLambdaRestApiproxyF368A2D5
[+] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/{proxy+}/ANY/ApiPermission.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY..{proxy+} ApiGatewayToLambdaPatternLambdaRestApiproxyANYApiPermissionHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANYproxy1D514583
[+] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/{proxy+}/ANY/ApiPermission.Test.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY..{proxy+} ApiGatewayToLambdaPatternLambdaRestApiproxyANYApiPermissionTestHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANYproxyA134B3D7
[+] AWS::ApiGateway::Method ApiGatewayToLambdaPattern/LambdaRestApi/Default/{proxy+}/ANY ApiGatewayToLambdaPatternLambdaRestApiproxyANY321FD2C2
[+] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/ANY/ApiPermission.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY.. ApiGatewayToLambdaPatternLambdaRestApiANYApiPermissionHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANY5ABB21A5
[+] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/ANY/ApiPermission.Test.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY.. ApiGatewayToLambdaPatternLambdaRestApiANYApiPermissionTestHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANY7AB6A51B
[+] AWS::ApiGateway::Method ApiGatewayToLambdaPattern/LambdaRestApi/Default/ANY ApiGatewayToLambdaPatternLambdaRestApiANY07ADEFED
[+] AWS::ApiGateway::UsagePlan ApiGatewayToLambdaPattern/LambdaRestApi/UsagePlan ApiGatewayToLambdaPatternLambdaRestApiUsagePlan837CF24C
[+] AWS::IAM::Role ApiGatewayToLambdaPattern/LambdaRestApiCloudWatchRole ApiGatewayToLambdaPatternLambdaRestApiCloudWatchRole3977FB08
[+] AWS::ApiGateway::Account ApiGatewayToLambdaPattern/LambdaRestApiAccount ApiGatewayToLambdaPatternLambdaRestApiAccountA31D1168

Outputs
[+] Output ApiGatewayToLambdaPattern/LambdaRestApi/Endpoint ApiGatewayToLambdaPatternLambdaRestApiEndpoint9A7AA3AD: {"Value":{"Fn::Join":["",["https://",{"Ref":"ApiGatewayToLambdaPatternLambdaRestApiC0598E46"},".execute-api.",{"Ref":"AWS::Region"},".",{"Ref":"AWS::URLSuffix"},"/",{"Ref":"ApiGatewayToLambdaPatternLambdaRestApiDeploymentStageprodFDEB8074"},"/"]]}}

Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
```

This simple example with one well-architected pattern from the AWS Solutions Constructs added 17 new resources to your stack.

## cdk deploy
<a name="cdk-deploy"></a>

**Tip**  
Before you can deploy your first AWS CDK app containing a Lambda function, you must bootstrap your AWS environment. This creates a staging bucket that the AWS CDK uses to deploy stacks containing assets. If this is the first time you are using the AWS CDK to deploy assets, you will need to run the `cdk bootstrap` command to deploy the CDK toolkit stack into your AWS environment.

Okay, ready to deploy?

```
cdk deploy
```

## Stack outputs
<a name="stack-outputs"></a>

When deployment is complete, you’ll notice this line:

```
Outputs:
  HelloConstructsStack.RestApiEndpoint0551178A = https://`xxxxxxxxxx`.execute-api.us-east-1.amazonaws.com/prod/
```

This is a stack output that’s automatically added by the AWS Solutions Constructs pattern and includes the URL of the API Gateway endpoint.

## Testing your app
<a name="testing-your-app"></a>

Let’s try to hit this endpoint with `curl` . Copy the URL and execute (your prefix and Region will likely be different).

```
curl https://
   xxxxxxxxxx
    .execute-api.us-east-1.amazonaws.com/prod/
```

Output should look like this:

```
Hello, AWS Solutions Constructs! You've hit /
```

If this is the output you received, your app works\$1

# Walkthrough - Part 2
<a name="walkthrough-part-2-v2"></a>

This tutorial walks you through how to modify the "Hello Constructs" app created in [part 1](walkthrough-part-1-v2.md) . Our modification will add a site hit counter using the AWS Lambda to DynamoDB pattern from AWS Solutions Constructs. Modifying the Hello Constructs app will result in the following solution:

![\[AWS Solutions Constructs diagram showing API Gateway, Lambda functions, DynamoDB, and CloudWatch interactions.\]](http://docs.aws.amazon.com/solutions/latest/constructs/images/tutorial-part2.png)


## Hit Counter Lambda code
<a name="hit-counter-lambda-code"></a>

Let’s get started by writing the code for the Hit Counter AWS Lambda function. This function will:
+ increment a counter related to the API path in a Amazon DynamoDB table,
+ invoke the downstream Hello AWS Lambda function,
+ and return the response to end user.

**Example**  
+ Add a file called `lambda/hitcounter.js` with the following contents:

  ```
  const { DynamoDB, Lambda } = require('aws-sdk');
  
  exports.handler = async function(event) {
    console.log("request:", JSON.stringify(event, undefined, 2));
  
    // create AWS SDK clients
    const dynamo = new DynamoDB();
    const lambda = new Lambda();
  
    // update dynamo entry for "path" with hits++
    await dynamo.updateItem({
      TableName: process.env.DDB_TABLE_NAME,
      Key: { path: { S: event.path } },
      UpdateExpression: 'ADD hits :incr',
      ExpressionAttributeValues: { ':incr': { N: '1' } }
    }).promise();
  
    // call downstream function and capture response
    const resp = await lambda.invoke({
      FunctionName: process.env.DOWNSTREAM_FUNCTION_NAME,
      Payload: JSON.stringify(event)
    }).promise();
  
    console.log('downstream response:', JSON.stringify(resp, undefined, 2));
  
    // return response back to upstream caller
    return JSON.parse(resp.Payload);
  };
  ```
+ Add a file called `lambda/hitcounter.py` with the following contents:

  ```
  import json
  import os
  import boto3
  
  ddb = boto3.resource('dynamodb')
  table = ddb.Table(os.environ['DDB_TABLE_NAME'])
  _lambda = boto3.client('lambda')
  
  
  def handler(event, context):
      print('request: {}'.format(json.dumps(event)))
      table.update_item(
          Key={'path': event['path']},
          UpdateExpression='ADD hits :incr',
          ExpressionAttributeValues={':incr': 1}
      )
  
      resp = _lambda.invoke(
          FunctionName=os.environ['DOWNSTREAM_FUNCTION_NAME'],
          Payload=json.dumps(event),
      )
  
      body = resp['Payload'].read()
  
      print('downstream response: {}'.format(body))
      return json.loads(body)
  ```
+ Add a file called `lambda/hitcounter.js` with the following contents:

  ```
  const { DynamoDB, Lambda } = require('aws-sdk');
  
  exports.handler = async function(event) {
    console.log("request:", JSON.stringify(event, undefined, 2));
  
    // create AWS SDK clients
    const dynamo = new DynamoDB();
    const lambda = new Lambda();
  
    // update dynamo entry for "path" with hits++
    await dynamo.updateItem({
      TableName: process.env.DDB_TABLE_NAME,
      Key: { path: { S: event.path } },
      UpdateExpression: 'ADD hits :incr',
      ExpressionAttributeValues: { ':incr': { N: '1' } }
    }).promise();
  
    // call downstream function and capture response
    const resp = await lambda.invoke({
      FunctionName: process.env.DOWNSTREAM_FUNCTION_NAME,
      Payload: JSON.stringify(event)
    }).promise();
  
    console.log('downstream response:', JSON.stringify(resp, undefined, 2));
  
    // return response back to upstream caller
    return JSON.parse(resp.Payload);
  };
  ```

### Install the new dependency
<a name="install-the-new-dependency"></a>

As usual, we first need to install the dependency we need for our solution update. Install the AWS Solutions Constructs `aws-lambda-dynamodb` module and all its dependency into our project:

**Example**  

```
npm install -s @aws-solutions-constructs/aws-lambda-dynamodb
```

```
pip install aws_solutions_constructs.aws_lambda_dynamodb
```
+ Edit the `pom.xml` file with the following information:

  ```
  <dependency>
      <groupId>software.amazon.awsconstructs</groupId>
      <artifactId>lambdadynamodb</artifactId>
      <version>${solutionconstructs.version}</version>
  </dependency>
  ```

  Run the command:

  ```
  mvn install
  ```

### Define the resources
<a name="w6aab7c17b9b3"></a>

Now, let’s update our stack code to accomodate our new architecture.

First, we are going to import our new dependency and move the "Hello" function outside of the `aws-apigateway-lambda` pattern we created in part 1.

**Example**  
+ Replace the code in `lib/hello-constructs-stack.ts` with the following:

  ```
  import { Construct } from 'constructs';
  import { Stack, StackProps } from 'aws-cdk-lib';
  import * as lambda from 'aws-cdk-lib/aws-lambda';
  import * as api from 'aws-cdk-lib/aws-apigateway';
  import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
  import { ApiGatewayToLambda, ApiGatewayToLambdaProps } from '@aws-solutions-constructs/aws-apigateway-lambda';
  import { LambdaToDynamoDB, LambdaToDynamoDBProps } from '@aws-solutions-constructs/aws-lambda-dynamodb';
  
  export class HelloConstructsStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
      super(scope, id, props);
  
      // The code that defines your stack goes here
  
      const helloFunc = new lambda.Function(this, 'HelloHandler', {
        runtime: lambda.Runtime.NODEJS_22_X,
        code: lambda.Code.fromAsset('lambda'),
        handler: 'hello.handler'
      });
  
      const api_lambda_props: ApiGatewayToLambdaProps = {
        lambdaFunctionProps: {
          code: lambda.Code.fromAsset('lambda'),
          runtime: lambda.Runtime.NODEJS_22_X,
          handler: 'hello.handler'
        },
        apiGatewayProps: {
          defaultMethodOptions: {
            authorizationType: api.AuthorizationType.NONE
          }
        }
      };
  
      new ApiGatewayToLambda(this, 'ApiGatewayToLambda', api_lambda_props);
    }
  }
  ```
+ Replace the code in `hello_constructs/hello_constructs_stack.py` with the following:

  ```
  from constructs import Construct
  from aws_cdk import (
      aws_lambda as _lambda,
      aws_apigateway as apigw,
      aws_dynamodb as ddb,
      App,
      Stack
  )
  from aws_solutions_constructs import (
      aws_apigateway_lambda as apigw_lambda,
      aws_lambda_dynamodb as lambda_ddb
  )
  
  class HelloConstructsStack(Stack):
  
      def __init__(self, scope: Construct, id: str, **kwargs) -> None:
          super().__init__(scope, id, **kwargs)
  
          # The code that defines your stack goes here
  
          self._handler = _lambda.Function(
            self, 'HelloHandler',
            runtime=_lambda.Runtime.PYTHON_3_11,
            handler='hello.handler',
            code=_lambda.Code.from_asset('lambda'),
          )
  
          apigw_lambda.ApiGatewayToLambda(
              self, 'ApiGatewayToLambda',
              lambda_function_props=_lambda.FunctionProps(
                  runtime=_lambda.Runtime.PYTHON_3_11,
                  code=_lambda.Code.from_asset('lambda'),
                  handler='hello.handler',
              ),
              api_gateway_props=apigw.RestApiProps(
                  default_method_options=apigw.MethodOptions(
                      authorization_type=apigw.AuthorizationType.NONE
                  )
              )
          )
  ```
+ Replace the code in `HelloConstructsStack.java` with the following:

  ```
  package com.myorg;
  
  import software.constructs.Construct;
  import software.amazon.awscdk.Stack;
  import software.amazon.awscdk.StackProps;
  
  import software.amazon.awscdk.services.lambda.*;
  import software.amazon.awscdk.services.lambda.Runtime;
  import software.amazon.awscdk.services.apigateway.*;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambda;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambdaProps;
  
  public class HelloConstructsStack extends Stack {
      public HelloConstructsStack(final Construct scope, final String id) {
          this(scope, id, null);
      }
  
      public HelloConstructsStack(final Construct scope, final String id, final StackProps props) {
          super(scope, id, props);
  
          final Function hello = Function.Builder.create(this, "HelloHandler")
                  .runtime(Runtime.NODEJS_22_X) // execution environment
                  .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                  .handler("hello.handler") // file is "hello", function is "handler"
                  .build();
  
          new ApiGatewayToLambda(this, "ApiGatewayToLambdaPattern", new ApiGatewayToLambdaProps.Builder()
                  .lambdaFunctionProps(new FunctionProps.Builder()
                          .runtime(Runtime.NODEJS_22_X) // execution environment
                          .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                          .handler("hello.handler") // file is "hello", function is "handler"
                          .build())
                  .apiGatewayProps(new RestApiProps.Builder()
                          .defaultMethodOptions(new MethodOptions.Builder()
                                  .authorizationType(AuthorizationType.NONE)
                                  .build())
                          .build())
                  .build());
      }
  }
  ```

Next, we are going to add the `aws-lambda-dynamodb` pattern to build out the hit counter service for our updated architecture.

![\[AWS Solutions Constructs diagram showing API Gateway, Lambda, DynamoDB, and CloudWatch interactions.\]](http://docs.aws.amazon.com/solutions/latest/constructs/images/tutorial-part2a.png)


The next update below defines the properties for the `aws-lambda-dynamodb` pattern by defining the AWS Lambda function with the Hit Counter handler. Additionally, the Amazon DynamoDB table is defined with a name of `SolutionsConstructsHits` and a partition key of `path` .

**Example**  
+ Edit the file `lib/hello-constructs-stack.ts` with the following:

  ```
  import { Construct } from 'constructs';
  import { Stack, StackProps, RemovalPolicy } from 'aws-cdk-lib';
  import * as lambda from 'aws-cdk-lib/aws-lambda';
  import * as api from 'aws-cdk-lib/aws-apigateway';
  import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
  import { ApiGatewayToLambda, ApiGatewayToLambdaProps } from '@aws-solutions-constructs/aws-apigateway-lambda';
  import { LambdaToDynamoDB, LambdaToDynamoDBProps } from '@aws-solutions-constructs/aws-lambda-dynamodb';
  
  export class HelloConstructsStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
      super(scope, id, props);
  
      // The code that defines your stack goes here
  
      const helloFunc = new lambda.Function(this, 'HelloHandler', {
        runtime: lambda.Runtime.NODEJS_22_X,
        code: lambda.Code.fromAsset('lambda'),
        handler: 'hello.handler'
      });
  
      // hit counter, aws-lambda-dynamodb pattern
      const lambda_ddb_props: LambdaToDynamoDBProps = {
        lambdaFunctionProps: {
            code: lambda.Code.fromAsset(`lambda`),
            runtime: lambda.Runtime.NODEJS_22_X,
            handler: 'hitcounter.handler',
            environment: {
                DOWNSTREAM_FUNCTION_NAME: helloFunc.functionName
            }
        },
        dynamoTableProps: {
            tableName: 'SolutionsConstructsHits',
            partitionKey: { name: 'path', type: dynamodb.AttributeType.STRING },
            removalPolicy: RemovalPolicy.DESTROY
        }
      };
  
      const hitcounter = new LambdaToDynamoDB(this, 'LambdaToDynamoDB', lambda_ddb_props);
  
      const api_lambda_props: ApiGatewayToLambdaProps = {
        lambdaFunctionProps: {
          code: lambda.Code.fromAsset('lambda'),
          runtime: lambda.Runtime.NODEJS_22_X,
          handler: 'hello.handler'
        },
        apiGatewayProps: {
          defaultMethodOptions: {
            authorizationType: api.AuthorizationType.NONE
          }
        }
      };
  
      new ApiGatewayToLambda(this, 'ApiGatewayToLambda', api_lambda_props);
    }
  }
  ```
+ Edit the file `hello_constructs/hello_constructs_stack.py` with the following:

  ```
  from constructs import Construct
  from aws_cdk import (
      aws_lambda as _lambda,
      aws_apigateway as apigw,
      aws_dynamodb as ddb,
      App,
      Stack,
      RemovalPolicy
  )
  from aws_solutions_constructs import (
      aws_apigateway_lambda as apigw_lambda,
      aws_lambda_dynamodb as lambda_ddb
  )
  
  
  class HelloConstructsStack(Stack):
  
      def __init__(self, scope: Construct, id: str, **kwargs) -> None:
          super().__init__(scope, id, **kwargs)
  
          # The code that defines your stack goes here
  
          self.hello_func = _lambda.Function(
              self, 'HelloHandler',
              runtime=_lambda.Runtime.PYTHON_3_11,
              handler='hello.handler',
              code=_lambda.Code.from_asset('lambda'),
          )
  
          #  hit counter, aws-lambda-dynamodb pattern
          self.hit_counter = lambda_ddb.LambdaToDynamoDB(
              self, 'LambdaToDynamoDB',
              lambda_function_props=_lambda.FunctionProps(
                  runtime=_lambda.Runtime.PYTHON_3_11,
                  code=_lambda.Code.from_asset('lambda'),
                  handler='hitcounter.handler',
                  environment={
                      'DOWNSTREAM_FUNCTION_NAME': self.hello_func.function_name
                  }
              ),
              dynamo_table_props=ddb.TableProps(
                  table_name='SolutionsConstructsHits',
                  partition_key={
                      'name': 'path',
                      'type': ddb.AttributeType.STRING
                  },
                  removal_policy=RemovalPolicy.DESTROY
              )
          )
  
          apigw_lambda.ApiGatewayToLambda(
              self, 'ApiGatewayToLambda',
              lambda_function_props=_lambda.FunctionProps(
                  runtime=_lambda.Runtime.PYTHON_3_11,
                  code=_lambda.Code.from_asset('lambda'),
                  handler='hello.handler',
              ),
              api_gateway_props=apigw.RestApiProps(
                  default_method_options=apigw.MethodOptions(
                      authorization_type=apigw.AuthorizationType.NONE
                  )
              )
          )
  ```
+ Edit the file `HelloConstructsStack.java` with the following:

  ```
  package com.myorg;
  
  import java.util.Map;
  import java.util.HashMap;
  
  import software.constructs.Construct;
  import software.amazon.awscdk.RemovalPolicy;
  import software.amazon.awscdk.Stack;
  import software.amazon.awscdk.StackProps;
  
  import software.amazon.awscdk.services.lambda.*;
  import software.amazon.awscdk.services.lambda.Runtime;
  import software.amazon.awscdk.services.apigateway.*;
  import software.amazon.awscdk.services.dynamodb.*;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambda;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambdaProps;
  import software.amazon.awsconstructs.services.lambdadynamodb.*;
  
  public class HelloConstructsStack extends Stack {
      public HelloConstructsStack(final Construct scope, final String id) {
          this(scope, id, null);
      }
  
      public HelloConstructsStack(final Construct scope, final String id, final StackProps props) {
          super(scope, id, props);
  
          final Function helloFunc = Function.Builder.create(this, "HelloHandler")
                  .runtime(Runtime.NODEJS_22_X) // execution environment
                  .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                  .handler("hello.handler") // file is "hello", function is "handler"
                  .build();
  
          final Map<String, String> lambdaEnvironment = new HashMap<>();
          lambdaEnvironment.put("DOWNSTREAM_FUNCTION_NAME", helloFunc.getFunctionName());
  
          final LambdaToDynamoDB hitcounter = new LambdaToDynamoDB(this, "LambdaToDynamoDBPattern",
                  new LambdaToDynamoDBProps.Builder()
                          .lambdaFunctionProps(new FunctionProps.Builder()
                                  .runtime(Runtime.NODEJS_22_X) // execution environment
                                  .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                                  .handler("hitcounter.handler") // file is "hello", function is "handler"
                                  .environment(lambdaEnvironment)
                                  .build())
                          .dynamoTableProps(new TableProps.Builder()
                                  .tableName("SolutionsConstructsHits")
                                  .partitionKey(new Attribute.Builder()
                                          .name("path")
                                          .type(AttributeType.STRING)
                                          .build())
                                  .removalPolicy(RemovalPolicy.DESTROY)
                                  .build())
                          .build());
  
          new ApiGatewayToLambda(this, "ApiGatewayToLambdaPattern", new ApiGatewayToLambdaProps.Builder()
                  .lambdaFunctionProps(new FunctionProps.Builder()
                          .runtime(Runtime.NODEJS_22_X) // execution environment
                          .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                          .handler("hello.handler") // file is "hello", function is "handler"
                          .build())
                  .apiGatewayProps(new RestApiProps.Builder()
                          .defaultMethodOptions(new MethodOptions.Builder()
                                  .authorizationType(AuthorizationType.NONE)
                                  .build())
                          .build())
                  .build());
      }
  }
  ```

Next, we need to grant the Hit Counter function created from the `aws-lambda-dynamodb` pattern added above permission to invoke our Hello function.

**Example**  
+ Edit the file `lib/hello-constructs-stack.ts` with the following:

  ```
  import { Construct } from 'constructs';
  import { Stack, StackProps, RemovalPolicy } from 'aws-cdk-lib';
  import * as lambda from 'aws-cdk-lib/aws-lambda';
  import * as api from 'aws-cdk-lib/aws-apigateway';
  import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
  import { ApiGatewayToLambda, ApiGatewayToLambdaProps } from '@aws-solutions-constructs/aws-apigateway-lambda';
  import { LambdaToDynamoDB, LambdaToDynamoDBProps } from '@aws-solutions-constructs/aws-lambda-dynamodb';
  
    export class HelloConstructsStack extends Stack {
      constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);
  
        // The code that defines your stack goes here
  
        // hello function responding to http requests
        const helloFunc = new lambda.Function(this, 'HelloHandler', {
          runtime: lambda.Runtime.NODEJS_22_X,
          code: lambda.Code.fromAsset('lambda'),
          handler: 'hello.handler'
        });
  
        // hit counter, aws-lambda-dynamodb pattern
        const lambda_ddb_props: LambdaToDynamoDBProps = {
          lambdaFunctionProps: {
              code: lambda.Code.fromAsset(`lambda`),
              runtime: lambda.Runtime.NODEJS_22_X,
              handler: 'hitcounter.handler',
              environment: {
                  DOWNSTREAM_FUNCTION_NAME: helloFunc.functionName
              }
          },
          dynamoTableProps: {
              tableName: 'SolutionsConstructsHits',
              partitionKey: { name: 'path', type: dynamodb.AttributeType.STRING },
              removalPolicy: RemovalPolicy.DESTROY
          }
        };
  
        const hitcounter = new LambdaToDynamoDB(this, 'LambdaToDynamoDB', lambda_ddb_props);
  
        // grant the hitcounter lambda role invoke permissions to the hello function
        helloFunc.grantInvoke(hitcounter.lambdaFunction);
  
        const api_lambda_props: ApiGatewayToLambdaProps = {
          lambdaFunctionProps: {
            code: lambda.Code.fromAsset('lambda'),
            runtime: lambda.Runtime.NODEJS_22_X,
            handler: 'hello.handler'
          },
          apiGatewayProps: {
            defaultMethodOptions: {
              authorizationType: api.AuthorizationType.NONE
            }
          }
        };
  
        new ApiGatewayToLambda(this, 'ApiGatewayToLambda', api_lambda_props);
      }
    }
  ```
+ Edit the file `hello_constructs/hello_constructs_stack.py` with the following:

  ```
  from constructs import Construct
  from aws_cdk import (
      aws_lambda as _lambda,
      aws_apigateway as apigw,
      aws_dynamodb as ddb,
      App,
      Stack,
      RemovalPolicy
  )
  
  from aws_solutions_constructs import (
      aws_apigateway_lambda as apigw_lambda,
      aws_lambda_dynamodb as lambda_ddb
  )
  
  class HelloConstructsStack(Stack):
  
      def __init__(self, scope: Construct, id: str, **kwargs) -> None:
          super().__init__(scope, id, **kwargs)
  
          # The code that defines your stack goes here
  
          self.hello_func = _lambda.Function(
            self, 'HelloHandler',
            runtime=_lambda.Runtime.PYTHON_3_11,
            handler='hello.handler',
            code=_lambda.Code.from_asset('lambda'),
          )
  
          #  hit counter, aws-lambda-dynamodb pattern
          self.hit_counter = lambda_ddb.LambdaToDynamoDB(
              self, 'LambdaToDynamoDB',
              lambda_function_props=_lambda.FunctionProps(
                  runtime=_lambda.Runtime.PYTHON_3_11,
                  code=_lambda.Code.from_asset('lambda'),
                  handler='hitcounter.handler',
                  environment={
                      'DOWNSTREAM_FUNCTION_NAME': self.hello_func.function_name
                  }
              ),
              dynamo_table_props=ddb.TableProps(
                  table_name='SolutionsConstructsHits',
                  partition_key={
                      'name': 'path',
                      'type': ddb.AttributeType.STRING
                  },
                  removal_policy=RemovalPolicy.DESTROY
              )
          )
  
          # grant the hitcounter lambda role invoke permissions to the hello function
          self.hello_func.grant_invoke(self.hit_counter.lambda_function)
  
          apigw_lambda.ApiGatewayToLambda(
              self, 'ApiGatewayToLambda',
              lambda_function_props=_lambda.FunctionProps(
                  runtime=_lambda.Runtime.PYTHON_3_11,
                  code=_lambda.Code.from_asset('lambda'),
                  handler='hello.handler',
              ),
              api_gateway_props=apigw.RestApiProps(
                  default_method_options=apigw.MethodOptions(
                      authorization_type=apigw.AuthorizationType.NONE
                  )
              )
          )
  ```
+ Edit the file `src/../HelloConstructsStack.java` with the following:

  ```
  package com.myorg;
  
  import java.util.Map;
  import java.util.HashMap;
  
  import software.constructs.Construct;
  import software.amazon.awscdk.RemovalPolicy;
  import software.amazon.awscdk.Stack;
  import software.amazon.awscdk.StackProps;
  
  import software.amazon.awscdk.services.lambda.*;
  import software.amazon.awscdk.services.lambda.Runtime;
  import software.amazon.awscdk.services.apigateway.*;
  import software.amazon.awscdk.services.dynamodb.*;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambda;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambdaProps;
  import software.amazon.awsconstructs.services.lambdadynamodb.*;
  
  public class HelloConstructsStack extends Stack {
      public HelloConstructsStack(final Construct scope, final String id) {
          this(scope, id, null);
      }
  
      public HelloConstructsStack(final Construct scope, final String id, final StackProps props) {
          super(scope, id, props);
  
          final Function helloFunc = Function.Builder.create(this, "HelloHandler")
                  .runtime(Runtime.NODEJS_22_X) // execution environment
                  .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                  .handler("hello.handler") // file is "hello", function is "handler"
                  .build();
  
          final Map<String, String> lambdaEnvironment = new HashMap<>();
          lambdaEnvironment.put("DOWNSTREAM_FUNCTION_NAME", helloFunc.getFunctionName());
  
          final LambdaToDynamoDB hitcounter = new LambdaToDynamoDB(this, "LambdaToDynamoDBPattern",
                  new LambdaToDynamoDBProps.Builder()
                          .lambdaFunctionProps(new FunctionProps.Builder()
                                  .runtime(Runtime.NODEJS_22_X) // execution environment
                                  .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                                  .handler("hitcounter.handler") // file is "hello", function is "handler"
                                  .environment(lambdaEnvironment)
                                  .build())
                          .dynamoTableProps(new TableProps.Builder()
                                  .tableName("SolutionsConstructsHits")
                                  .partitionKey(new Attribute.Builder()
                                          .name("path")
                                          .type(AttributeType.STRING)
                                          .build())
                                  .removalPolicy(RemovalPolicy.DESTROY)
                                  .build())
                          .build());
  
          // grant the hitcounter lambda role invoke permissions to the hello function
          helloFunc.grantInvoke(hitcounter.getLambdaFunction());
  
          new ApiGatewayToLambda(this, "ApiGatewayToLambdaPattern", new ApiGatewayToLambdaProps.Builder()
                  .lambdaFunctionProps(new FunctionProps.Builder()
                          .runtime(Runtime.NODEJS_22_X) // execution environment
                          .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                          .handler("hello.handler") // file is "hello", function is "handler"
                          .build())
                  .apiGatewayProps(new RestApiProps.Builder()
                          .defaultMethodOptions(new MethodOptions.Builder()
                                  .authorizationType(AuthorizationType.NONE)
                                  .build())
                          .build())
                  .build());
      }
  }
  ```

Finally, we need to update our original `aws-apigateway-lambda` pattern to utilize our new Hit Counter function that was provisioned with the `aws-lambda-dynamodb` pattern above.

**Example**  
+ Edit the file `lib/hello-constructs-stack.ts` with the following:

  ```
  import { Construct } from 'constructs';
  import { Stack, StackProps, RemovalPolicy } from 'aws-cdk-lib';
  import * as lambda from 'aws-cdk-lib/aws-lambda';
  import * as api from 'aws-cdk-lib/aws-apigateway';
  import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
  import { ApiGatewayToLambda, ApiGatewayToLambdaProps } from '@aws-solutions-constructs/aws-apigateway-lambda';
  import { LambdaToDynamoDB, LambdaToDynamoDBProps } from '@aws-solutions-constructs/aws-lambda-dynamodb';
  
  export class HelloConstructsStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
      super(scope, id, props);
  
      // The code that defines your stack goes here
  
      // hello function responding to http requests
      const helloFunc = new lambda.Function(this, 'HelloHandler', {
        runtime: lambda.Runtime.NODEJS_22_X,
        code: lambda.Code.fromAsset('lambda'),
        handler: 'hello.handler'
      });
  
      // hit counter, aws-lambda-dynamodb pattern
      const lambda_ddb_props: LambdaToDynamoDBProps = {
        lambdaFunctionProps: {
          code: lambda.Code.fromAsset(`lambda`),
          runtime: lambda.Runtime.NODEJS_22_X,
          handler: 'hitcounter.handler',
          environment: {
            DOWNSTREAM_FUNCTION_NAME: helloFunc.functionName
          }
        },
        dynamoTableProps: {
          tableName: 'SolutionsConstructsHits',
          partitionKey: { name: 'path', type: dynamodb.AttributeType.STRING },
          removalPolicy: RemovalPolicy.DESTROY
        }
      };
  
      const hitcounter = new LambdaToDynamoDB(this, 'LambdaToDynamoDB', lambda_ddb_props);
  
      // grant the hitcounter lambda role invoke permissions to the hello function
      helloFunc.grantInvoke(hitcounter.lambdaFunction);
  
      const api_lambda_props: ApiGatewayToLambdaProps = {
        existingLambdaObj: hitcounter.lambdaFunction,
        apiGatewayProps: {
          defaultMethodOptions: {
            authorizationType: api.AuthorizationType.NONE
          }
        }
      };
  
      new ApiGatewayToLambda(this, 'ApiGatewayToLambda', api_lambda_props);
    }
  }
  ```
+ Edit the file `hello_constructs/hello_constructs_stack.py` with the following:

  ```
  from constructs import Construct
  from aws_cdk import (
      aws_lambda as _lambda,
      aws_apigateway as apigw,
      aws_dynamodb as ddb,
      App,
      Stack,
      RemovalPolicy
  )
  
  from aws_solutions_constructs import (
      aws_apigateway_lambda as apigw_lambda,
      aws_lambda_dynamodb as lambda_ddb
  )
  
  class HelloConstructsStack(Stack):
  
      def __init__(self, scope: Construct, id: str, **kwargs) -> None:
          super().__init__(scope, id, **kwargs)
  
          # The code that defines your stack goes here
  
          self.hello_func = _lambda.Function(
            self, 'HelloHandler',
            runtime=_lambda.Runtime.PYTHON_3_11,
            handler='hello.handler',
            code=_lambda.Code.from_asset('lambda'),
          )
  
          #  hit counter, aws-lambda-dynamodb pattern
          self.hit_counter = lambda_ddb.LambdaToDynamoDB(
              self, 'LambdaToDynamoDB',
              lambda_function_props=_lambda.FunctionProps(
                  runtime=_lambda.Runtime.PYTHON_3_11,
                  code=_lambda.Code.from_asset('lambda'),
                  handler='hitcounter.handler',
                  environment={
                      'DOWNSTREAM_FUNCTION_NAME': self.hello_func.function_name
                  }
              ),
              dynamo_table_props=ddb.TableProps(
                  table_name='SolutionsConstructsHits',
                  partition_key={
                      'name': 'path',
                      'type': ddb.AttributeType.STRING
                  },
                  removal_policy=RemovalPolicy.DESTROY
              )
          )
  
          # grant the hitcounter lambda role invoke permissions to the hello function
          self.hello_func.grant_invoke(self.hit_counter.lambda_function)
  
          apigw_lambda.ApiGatewayToLambda(
              self, 'ApiGatewayToLambda',
              existing_lambda_obj=self.hit_counter.lambda_function,
              api_gateway_props=apigw.RestApiProps(
                  default_method_options=apigw.MethodOptions(
                      authorization_type=apigw.AuthorizationType.NONE
                  )
              )
          )
  ```
+ Edit the file `src/../HelloConstructsStack.java` with the following:

  ```
  package com.myorg;
  
  import java.util.Map;
  import java.util.HashMap;
  
  import software.constructs.Construct;
  import software.amazon.awscdk.RemovalPolicy;
  import software.amazon.awscdk.Stack;
  import software.amazon.awscdk.StackProps;
  
  import software.amazon.awscdk.services.lambda.*;
  import software.amazon.awscdk.services.lambda.Runtime;
  import software.amazon.awscdk.services.apigateway.*;
  import software.amazon.awscdk.services.dynamodb.*;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambda;
  import software.amazon.awsconstructs.services.apigatewaylambda.ApiGatewayToLambdaProps;
  import software.amazon.awsconstructs.services.lambdadynamodb.*;
  
  public class HelloConstructsStack extends Stack {
      public HelloConstructsStack(final Construct scope, final String id) {
          this(scope, id, null);
      }
  
      public HelloConstructsStack(final Construct scope, final String id, final StackProps props) {
          super(scope, id, props);
  
          final Function helloFunc = Function.Builder.create(this, "HelloHandler")
                  .runtime(Runtime.NODEJS_22_X) // execution environment
                  .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                  .handler("hello.handler") // file is "hello", function is "handler"
                  .build();
  
          final Map<String, String> lambdaEnvironment = new HashMap<>();
          lambdaEnvironment.put("DOWNSTREAM_FUNCTION_NAME", helloFunc.getFunctionName());
  
          final LambdaToDynamoDB hitcounter = new LambdaToDynamoDB(this, "LambdaToDynamoDBPattern",
                  new LambdaToDynamoDBProps.Builder()
                          .lambdaFunctionProps(new FunctionProps.Builder()
                                  .runtime(Runtime.NODEJS_22_X) // execution environment
                                  .code(Code.fromAsset("lambda")) // code loaded from the "lambda" directory
                                  .handler("hitcounter.handler") // file is "hitcounter", function is "handler"
                                  .environment(lambdaEnvironment)
                                  .build())
                          .dynamoTableProps(new TableProps.Builder()
                                  .tableName("SolutionsConstructsHits")
                                  .partitionKey(new Attribute.Builder()
                                          .name("path")
                                          .type(AttributeType.STRING)
                                          .build())
                                  .removalPolicy(RemovalPolicy.DESTROY)
                                  .build())
                          .build());
  
          // grant the hitcounter lambda role invoke permissions to the hello function
          helloFunc.grantInvoke(hitcounter.getLambdaFunction());
  
          final ApiGatewayToLambda apigwLambda = new ApiGatewayToLambda(this, "ApiGatewayToLambdaPattern",
                  new ApiGatewayToLambdaProps.Builder()
                          .apiGatewayProps(new RestApiProps.Builder()
                                  .defaultMethodOptions(MethodOptions.builder()
                                          .authorizationType(AuthorizationType.NONE)
                                          .build())
                                  .build())
                          .existingLambdaObj(hitcounter.getLambdaFunction())
                          .build());
      }
  }
  ```

## Review the changes
<a name="review-the-changes"></a>

Let’s build our project and review the changes to our resources that will happen when we deploy this:

**Example**  

```
npm run build
cdk diff
```

```
cdk diff
```

```
mvn package
cdk diff
```

Our output should look like this:

```
IAM Statement Changes
┌───┬────────────┬────────┬────────────┬────────────┬──────────────┐
│   │ Resource   │ Effect │ Action     │ Principal  │ Condition    │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ - │ ${ApiGatew │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ ayToLambda │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ PatternLam │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ bdaFunctio │        │            │ om         │ ${AWS::Parti │
│   │ n5DC51B7E. │        │            │            │ tion}:execut │
│   │ Arn}       │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/${ApiGa │
│   │            │        │            │            │ tewayToLambd │
│   │            │        │            │            │ aPattern/Lam │
│   │            │        │            │            │ bdaRestApi/D │
│   │            │        │            │            │ eploymentSta │
│   │            │        │            │            │ ge.prod}/*/* │
│   │            │        │            │            │ "            │
│   │            │        │            │            │ }            │
│ - │ ${ApiGatew │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ ayToLambda │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ PatternLam │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ bdaFunctio │        │            │ om         │ ${AWS::Parti │
│   │ n5DC51B7E. │        │            │            │ tion}:execut │
│   │ Arn}       │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/test-in │
│   │            │        │            │            │ voke-stage/* │
│   │            │        │            │            │ /*"          │
│   │            │        │            │            │ }            │
│ - │ ${ApiGatew │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ ayToLambda │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ PatternLam │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ bdaFunctio │        │            │ om         │ ${AWS::Parti │
│   │ n5DC51B7E. │        │            │            │ tion}:execut │
│   │ Arn}       │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/${ApiGa │
│   │            │        │            │            │ tewayToLambd │
│   │            │        │            │            │ aPattern/Lam │
│   │            │        │            │            │ bdaRestApi/D │
│   │            │        │            │            │ eploymentSta │
│   │            │        │            │            │ ge.prod}/*/" │
│   │            │        │            │            │ }            │
│ - │ ${ApiGatew │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ ayToLambda │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ PatternLam │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ bdaFunctio │        │            │ om         │ ${AWS::Parti │
│   │ n5DC51B7E. │        │            │            │ tion}:execut │
│   │ Arn}       │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/test-in │
│   │            │        │            │            │ voke-stage/* │
│   │            │        │            │            │ /"           │
│   │            │        │            │            │ }            │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ - │ *          │ Allow  │ xray:PutTe │ AWS:${ApiG │              │
│   │            │        │ lemetryRec │ atewayToLa │              │
│   │            │        │ ords       │ mbdaPatter │              │
│   │            │        │ xray:PutTr │ nLambdaFun │              │
│   │            │        │ aceSegment │ ctionServi │              │
│   │            │        │ s          │ ceRole0C12 │              │
│   │            │        │            │ 3D8D}      │              │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ + │ ${HelloHan │ Allow  │ lambda:Inv │ AWS:${Lamb │              │
│   │ dler.Arn}  │        │ okeFunctio │ daToDynamo │              │
│   │ ${HelloHan │        │ n          │ DBPattern/ │              │
│   │ dler.Arn}: │        │            │ LambdaFunc │              │
│   │ *          │        │            │ tionServic │              │
│   │            │        │            │ eRole}     │              │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ + │ ${HelloHan │ Allow  │ sts:Assume │ Service:la │              │
│   │ dler/Servi │        │ Role       │ mbda.amazo │              │
│   │ ceRole.Arn │        │            │ naws.com   │              │
│   │ }          │        │            │            │              │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ + │ ${LambdaTo │ Allow  │ dynamodb:B │ AWS:${Lamb │              │
│   │ DynamoDBPa │        │ atchGetIte │ daToDynamo │              │
│   │ ttern/Dyna │        │ m          │ DBPattern/ │              │
│   │ moTable.Ar │        │ dynamodb:B │ LambdaFunc │              │
│   │ n}         │        │ atchWriteI │ tionServic │              │
│   │            │        │ tem        │ eRole}     │              │
│   │            │        │ dynamodb:C │            │              │
│   │            │        │ onditionCh │            │              │
│   │            │        │ eckItem    │            │              │
│   │            │        │ dynamodb:D │            │              │
│   │            │        │ eleteItem  │            │              │
│   │            │        │ dynamodb:D │            │              │
│   │            │        │ escribeTab │            │              │
│   │            │        │ le         │            │              │
│   │            │        │ dynamodb:G │            │              │
│   │            │        │ etItem     │            │              │
│   │            │        │ dynamodb:G │            │              │
│   │            │        │ etRecords  │            │              │
│   │            │        │ dynamodb:G │            │              │
│   │            │        │ etShardIte │            │              │
│   │            │        │ rator      │            │              │
│   │            │        │ dynamodb:P │            │              │
│   │            │        │ utItem     │            │              │
│   │            │        │ dynamodb:Q │            │              │
│   │            │        │ uery       │            │              │
│   │            │        │ dynamodb:S │            │              │
│   │            │        │ can        │            │              │
│   │            │        │ dynamodb:U │            │              │
│   │            │        │ pdateItem  │            │              │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ + │ ${LambdaTo │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ DynamoDBPa │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ ttern/Lamb │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ daFunction │        │            │ om         │ ${AWS::Parti │
│   │ .Arn}      │        │            │            │ tion}:execut │
│   │            │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/${ApiGa │
│   │            │        │            │            │ tewayToLambd │
│   │            │        │            │            │ aPattern/Lam │
│   │            │        │            │            │ bdaRestApi/D │
│   │            │        │            │            │ eploymentSta │
│   │            │        │            │            │ ge.prod}/*/* │
│   │            │        │            │            │ "            │
│   │            │        │            │            │ }            │
│ + │ ${LambdaTo │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ DynamoDBPa │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ ttern/Lamb │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ daFunction │        │            │ om         │ ${AWS::Parti │
│   │ .Arn}      │        │            │            │ tion}:execut │
│   │            │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/test-in │
│   │            │        │            │            │ voke-stage/* │
│   │            │        │            │            │ /*"          │
│   │            │        │            │            │ }            │
│ + │ ${LambdaTo │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ DynamoDBPa │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ ttern/Lamb │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ daFunction │        │            │ om         │ ${AWS::Parti │
│   │ .Arn}      │        │            │            │ tion}:execut │
│   │            │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/${ApiGa │
│   │            │        │            │            │ tewayToLambd │
│   │            │        │            │            │ aPattern/Lam │
│   │            │        │            │            │ bdaRestApi/D │
│   │            │        │            │            │ eploymentSta │
│   │            │        │            │            │ ge.prod}/*/" │
│   │            │        │            │            │ }            │
│ + │ ${LambdaTo │ Allow  │ lambda:Inv │ Service:ap │ "ArnLike": { │
│   │ DynamoDBPa │        │ okeFunctio │ igateway.a │   "AWS:Sourc │
│   │ ttern/Lamb │        │ n          │ mazonaws.c │ eArn": "arn: │
│   │ daFunction │        │            │ om         │ ${AWS::Parti │
│   │ .Arn}      │        │            │            │ tion}:execut │
│   │            │        │            │            │ e-api:${AWS: │
│   │            │        │            │            │ :Region}:${A │
│   │            │        │            │            │ WS::AccountI │
│   │            │        │            │            │ d}:${ApiGate │
│   │            │        │            │            │ wayToLambdaP │
│   │            │        │            │            │ atternLambda │
│   │            │        │            │            │ RestApiC0598 │
│   │            │        │            │            │ E46}/test-in │
│   │            │        │            │            │ voke-stage/* │
│   │            │        │            │            │ /"           │
│   │            │        │            │            │ }            │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ + │ ${LambdaTo │ Allow  │ sts:Assume │ Service:la │              │
│   │ DynamoDBPa │        │ Role       │ mbda.amazo │              │
│   │ ttern/Lamb │        │            │ naws.com   │              │
│   │ daFunction │        │            │            │              │
│   │ ServiceRol │        │            │            │              │
│   │ e.Arn}     │        │            │            │              │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ + │ *          │ Allow  │ xray:PutTe │ AWS:${Lamb │              │
│   │            │        │ lemetryRec │ daToDynamo │              │
│   │            │        │ ords       │ DBPattern/ │              │
│   │            │        │ xray:PutTr │ LambdaFunc │              │
│   │            │        │ aceSegment │ tionServic │              │
│   │            │        │ s          │ eRole}     │              │
├───┼────────────┼────────┼────────────┼────────────┼──────────────┤
│ + │ arn:${AWS: │ Allow  │ logs:Creat │ AWS:${Lamb │              │
│   │ :Partition │        │ eLogGroup  │ daToDynamo │              │
│   │ }:logs:${A │        │ logs:Creat │ DBPattern/ │              │
│   │ WS::Region │        │ eLogStream │ LambdaFunc │              │
│   │ }:${AWS::A │        │ logs:PutLo │ tionServic │              │
│   │ ccountId}: │        │ gEvents    │ eRole}     │              │
│   │ log-group: │        │            │            │              │
│   │ /aws/lambd │        │            │            │              │
│   │ a/*        │        │            │            │              │
└───┴────────────┴────────┴────────────┴────────────┴──────────────┘
IAM Policy Changes
┌───┬──────────────────────────────┬───────────────────────────────┐
│   │ Resource                     │ Managed Policy ARN            │
├───┼──────────────────────────────┼───────────────────────────────┤
│ + │ ${HelloHandler/ServiceRole}  │ arn:${AWS::Partition}:iam::aw │
│   │                              │ s:policy/service-role/AWSLamb │
│   │                              │ daBasicExecutionRole          │
└───┴──────────────────────────────┴───────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[-] AWS::IAM::Role ApiGatewayToLambdaPatternLambdaFunctionServiceRole0C123D8D destroy
[-] AWS::IAM::Policy ApiGatewayToLambdaPatternLambdaFunctionServiceRoleDefaultPolicy253751F2 destroy
[-] AWS::Lambda::Function ApiGatewayToLambdaPatternLambdaFunction5DC51B7E destroy
[-] AWS::ApiGateway::Deployment ApiGatewayToLambdaPatternLambdaRestApiDeployment4109DB93346ab5d96a64d161f4cf4f020d3cdf94 destroy
[+] AWS::IAM::Role HelloHandler/ServiceRole HelloHandlerServiceRole11EF7C63
[+] AWS::Lambda::Function HelloHandler HelloHandler2E4FBA4D
[+] AWS::IAM::Role LambdaToDynamoDBPattern/LambdaFunctionServiceRole LambdaToDynamoDBPatternLambdaFunctionServiceRoleAAE562DF
[+] AWS::IAM::Policy LambdaToDynamoDBPattern/LambdaFunctionServiceRole/DefaultPolicy LambdaToDynamoDBPatternLambdaFunctionServiceRoleDefaultPolicy13FCEF5D
[+] AWS::Lambda::Function LambdaToDynamoDBPattern/LambdaFunction LambdaToDynamoDBPatternLambdaFunctionCEB12909
[+] AWS::DynamoDB::Table LambdaToDynamoDBPattern/DynamoTable LambdaToDynamoDBPatternDynamoTable4B679F88
[+] AWS::ApiGateway::Deployment ApiGatewayToLambdaPattern/LambdaRestApi/Deployment ApiGatewayToLambdaPatternLambdaRestApiDeployment4109DB93cb22f0825c29c1c2a437398f022ebdb0
[~] AWS::ApiGateway::Stage ApiGatewayToLambdaPattern/LambdaRestApi/DeploymentStage.prod ApiGatewayToLambdaPatternLambdaRestApiDeploymentStageprodFDEB8074
 └─ [~] DeploymentId
     └─ [~] .Ref:
         ├─ [-] ApiGatewayToLambdaPatternLambdaRestApiDeployment4109DB93346ab5d96a64d161f4cf4f020d3cdf94
         └─ [+] ApiGatewayToLambdaPatternLambdaRestApiDeployment4109DB93cb22f0825c29c1c2a437398f022ebdb0
[~] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/{proxy+}/ANY/ApiPermission.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY..{proxy+} ApiGatewayToLambdaPatternLambdaRestApiproxyANYApiPermissionHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANYproxy1D514583 replace
 └─ [~] FunctionName (requires replacement)
     └─ [~] .Fn::GetAtt:
         └─ @@ -1,4 +1,4 @@
            [ ] [
            [-]   "ApiGatewayToLambdaPatternLambdaFunction5DC51B7E",
            [+]   "LambdaToDynamoDBPatternLambdaFunctionCEB12909",
            [ ]   "Arn"
            [ ] ]
[~] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/{proxy+}/ANY/ApiPermission.Test.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY..{proxy+} ApiGatewayToLambdaPatternLambdaRestApiproxyANYApiPermissionTestHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANYproxyA134B3D7 replace
 └─ [~] FunctionName (requires replacement)
     └─ [~] .Fn::GetAtt:
         └─ @@ -1,4 +1,4 @@
            [ ] [
            [-]   "ApiGatewayToLambdaPatternLambdaFunction5DC51B7E",
            [+]   "LambdaToDynamoDBPatternLambdaFunctionCEB12909",
            [ ]   "Arn"
            [ ] ]
[~] AWS::ApiGateway::Method ApiGatewayToLambdaPattern/LambdaRestApi/Default/{proxy+}/ANY ApiGatewayToLambdaPatternLambdaRestApiproxyANY321FD2C2
 └─ [~] Integration
     └─ [~] .Uri:
         └─ [~] .Fn::Join:
             └─ @@ -12,7 +12,7 @@
                [ ] ":lambda:path/2015-03-31/functions/",
                [ ] {
                [ ]   "Fn::GetAtt": [
                [-]     "ApiGatewayToLambdaPatternLambdaFunction5DC51B7E",
                [+]     "LambdaToDynamoDBPatternLambdaFunctionCEB12909",
                [ ]     "Arn"
                [ ]   ]
                [ ] },
[~] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/ANY/ApiPermission.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY.. ApiGatewayToLambdaPatternLambdaRestApiANYApiPermissionHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANY5ABB21A5 replace
 └─ [~] FunctionName (requires replacement)
     └─ [~] .Fn::GetAtt:
         └─ @@ -1,4 +1,4 @@
            [ ] [
            [-]   "ApiGatewayToLambdaPatternLambdaFunction5DC51B7E",
            [+]   "LambdaToDynamoDBPatternLambdaFunctionCEB12909",
            [ ]   "Arn"
            [ ] ]
[~] AWS::Lambda::Permission ApiGatewayToLambdaPattern/LambdaRestApi/Default/ANY/ApiPermission.Test.HelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5.ANY.. ApiGatewayToLambdaPatternLambdaRestApiANYApiPermissionTestHelloConstructsStackApiGatewayToLambdaPatternLambdaRestApi553584F5ANY7AB6A51B replace
 └─ [~] FunctionName (requires replacement)
     └─ [~] .Fn::GetAtt:
         └─ @@ -1,4 +1,4 @@
            [ ] [
            [-]   "ApiGatewayToLambdaPatternLambdaFunction5DC51B7E",
            [+]   "LambdaToDynamoDBPatternLambdaFunctionCEB12909",
            [ ]   "Arn"
            [ ] ]
[~] AWS::ApiGateway::Method ApiGatewayToLambdaPattern/LambdaRestApi/Default/ANY ApiGatewayToLambdaPatternLambdaRestApiANY07ADEFED
 └─ [~] Integration
     └─ [~] .Uri:
         └─ [~] .Fn::Join:
             └─ @@ -12,7 +12,7 @@
                [ ] ":lambda:path/2015-03-31/functions/",
                [ ] {
                [ ]   "Fn::GetAtt": [
                [-]     "ApiGatewayToLambdaPatternLambdaFunction5DC51B7E",
                [+]     "LambdaToDynamoDBPatternLambdaFunctionCEB12909",
                [ ]     "Arn"
                [ ]   ]
                [ ] },
```

## cdk deploy
<a name="cdk-deploy-1"></a>

Okay, ready to deploy?

```
cdk deploy
```

## Stack outputs
<a name="stack-outputs-1"></a>

When deployment is complete, you’ll notice this line:

```
Outputs:
  HelloConstructsStack.RestApiEndpoint0551178A = https://`xxxxxxxxxx`.execute-api.us-east-1.amazonaws.com/prod/
```

## Testing your app
<a name="testing-your-app-1"></a>

Let’s try to hit this endpoint with curl. Copy the URL and execute (your prefix and region will likely be different).

```
curl https://
  xxxxxxxxxx
  .execute-api.us-east-1.amazonaws.com/prod/
```

Output should look like this:

```
Hello, AWS Solutions Constructs! You've hit /
```

Now, let’s review the `SolutionsConstructsHits` Amazon DynamoDB table.

1. Go to the DynamoDB console.

1. Make sure you are in the Region where you created the table.

1. Select **Tables** in the navigation pane and select the **SolutionsConstructsHits** table.

1. Open the table and select "Items" or "Explore table items" (depending upon which version of the Dyamodb console you are using).

1. You should see how many hits you got for each path (the data below reflects running the curl command 3 times with 3 different resources at the end of the URL).  
![\[Table showing paths and their corresponding hit counts, with three entries listed.\]](http://docs.aws.amazon.com/solutions/latest/constructs/images/tutorial-part2b.png)

1. Try hitting a new path and refresh the Items view. You should see a new item with a `hits` count of one.

If this is the output you received, your app works\$1

## Clean up
<a name="clean-up"></a>

To avoid unexpected charges to your account, make sure you clean up your CDK stack.

You can either delete the stack through the AWS CloudFormation console or use cdk destroy:

```
cdk destroy
```

You’ll be asked:

```
Are you sure you want to delete: HelloConstructsStack (y/n)?
```

Hit "y" and you’ll see your stack being destroyed.

# Sample Use Cases
<a name="sample-use-cases"></a>

This library includes a collection of functional use case implementations to demonstrate the usage of Constructs architectural patterns. These can be used in the same way as architectural patterns, and can be conceptualized as an additional "higher-level" abstraction of those patterns. The following use cases are provided as functional examples.

## AWS Static S3 Website
<a name="aws-static-s3-website"></a>

This use case pattern (`aws-s3-static-website`) implements an Amazon CloudFront distribution, Amazon S3 bucket, and AWS Lambda-based custom resource to copy the static website content for the Wild Rydes demo website (part of the `aws-serverless-web-app` implementation).

**Source Code (aws-s3-static-website)**  
 [https://github.com/awslabs/aws-solutions-constructs/tree/master/source/use_cases/aws-s3-static-website](https://github.com/awslabs/aws-solutions-constructs/tree/master/source/use_cases/aws-s3-static-website) 

## AWS Restaurant Management Demo
<a name="aws-restaurant-management-demo"></a>

This use case pattern (`aws-restaurant-management-demo`) implements a complex, multi-stack architecture that models a restaurant management system. This use case will provision a stack for service/wait staff to open/close orders, a stack for kitchen staff to view/complete orders, and a stack for managers to perform various business functions. It will also provision a stack containing a central DynamoDB table for managing orders, as well as a Lambda layer for sharing common database access patterns.

**Source Code (aws-restaurant-management-demo)**  
 [https://github.com/awslabs/aws-solutions-constructs/tree/master/source/use_cases/aws-restaurant-management-demo](https://github.com/awslabs/aws-solutions-constructs/tree/master/source/use_cases/aws-restaurant-management-demo) 