This is the AWS CDK v2 Developer Guide. The older CDK v1 entered maintenance on June 1, 2022 and ended support on June 1, 2023.
In this tutorial, you use the AWS Cloud Development Kit (AWS CDK) to create a simple serverless Hello World application that implements a basic API backend consisting of the following:
-
Amazon API Gateway REST API – Provides an HTTP endpoint that is used to invoke your function through an HTTP GET request.
-
AWS Lambda function – Function that returns a
Hello World!
message when invoked with the HTTP endpoint. -
Integrations and permissions – Configuration details and permissions for your resources to interact with one another and perform actions, such as writing logs to Amazon CloudWatch.
The following diagram shows the components of this application:

For this tutorial, you will create and interact with your application in the following steps:
-
Create an AWS CDK project.
-
Define a Lambda function and API Gateway REST API using L2 constructs from the AWS Construct Library.
-
Deploy your application to the AWS Cloud.
-
Interact with your application in the AWS Cloud.
-
Delete the sample application from the AWS Cloud.
Topics
Prerequisites
Before starting this tutorial, complete the following:
-
Create an AWS account and have the AWS Command Line Interface (AWS CLI) installed and configured.
-
Install Node.js and npm.
-
Install the CDK Toolkit globally, using
npm install -g aws-cdk
.
For more information, see Getting started with the AWS CDK.
We also recommend a basic understanding of the following:
-
What is the AWS CDK? for a basic introduction to the AWS CDK.
-
Learn AWS CDK core concepts for an overview of core concepts of the AWS CDK.
Step 1: Create a CDK project
In this step, you create a new CDK project using the AWS CDK CLI
cdk init
command.
To create a CDK project
-
From a starting directory of your choice, create and navigate to a project directory named
cdk-hello-world
on your machine:$
mkdir cdk-hello-world && cd cdk-hello-world
-
Use the
cdk init
command to create a new project in your preferred programming language:$
cdk init --language typescript
Install AWS CDK libraries:
$
npm install aws-cdk-lib constructs
The CDK CLI creates a project with the following structure:
cdk-hello-world ├── .git ├── .gitignore ├── .npmignore ├── README.md ├── bin │ └── cdk-hello-world.ts ├── cdk.json ├── jest.config.js ├── lib │ └── cdk-hello-world-stack.ts ├── node_modules ├── package-lock.json ├── package.json ├── test │ └── cdk-hello-world.test.ts └── tsconfig.json
The CDK CLI automatically creates a CDK app that contains a single stack. The
CDK app instance is created from the App
class. The following is a portion of your CDK
application file:
Located in bin/cdk-hello-world.ts
:
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkHelloWorldStack } from '../lib/cdk-hello-world-stack';
const app = new cdk.App();
new CdkHelloWorldStack(app, 'CdkHelloWorldStack', {
});
Step 2: Create your Lambda function
Within your CDK project, create a lambda
directory that includes a new
hello.js
file. The following is an example:
From the root of your project, run the following:
$
mkdir lambda && cd lambda
$
touch hello.js
The following should now be added to your CDK project:
cdk-hello-world
└── lambda
└── hello.js
Note
To keep this tutorial simple, we use a JavaScript Lambda function for all CDK programming languages.
Define your Lambda function by adding the following to the newly created file:
exports.handler = async (event) => {
return {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: JSON.stringify({ message: "Hello, World!" }),
};
};
Step 3: Define your constructs
In this step, you will define your Lambda and API Gateway resources using AWS CDK L2 constructs.
Open the project file that defines your CDK stack. You will modify this file to define your constructs. The following is an example of your starting stack file:
Located in lib/cdk-hello-world-stack.ts
:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class CdkHelloWorldStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Your constructs will go here
}
}
In this file, the AWS CDK is doing the following:
-
Your CDK stack instance is instantiated from the
Stack
class. -
The
Constructs
base class is imported and provided as the scope or parent of your stack instance.
Define your Lambda function resource
To define your Lambda function resource, you import and use the aws-lambda
L2 construct from the
AWS Construct Library.
Modify your stack file as follows:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// Import Lambda L2 construct
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class CdkHelloWorldStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Define the Lambda function resource
const helloWorldFunction = new lambda.Function(this, 'HelloWorldFunction', {
runtime: lambda.Runtime.NODEJS_20_X, // Choose any supported Node.js runtime
code: lambda.Code.fromAsset('lambda'), // Points to the lambda directory
handler: 'hello.handler', // Points to the 'hello' file in the lambda directory
});
}
}
Here, you create a Lambda function resource and define the following properties:
-
runtime
– The environment the function runs in. Here, we use Node.js version 20.x. -
code
– The path to the function code on your local machine. -
handler
– The name of the specific file that contains your function code.
Define your API Gateway REST API resource
To define your API Gateway REST API resource, you import and use the aws-apigateway
L2 construct from the
AWS Construct Library.
Modify your stack file as follows:
// ...
//Import API Gateway L2 construct
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
export class CdkHelloWorldStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// ...
// Define the API Gateway resource
const api = new apigateway.LambdaRestApi(this, 'HelloWorldApi', {
handler: helloWorldFunction,
proxy: false,
});
// Define the '/hello' resource with a GET method
const helloResource = api.root.addResource('hello');
helloResource.addMethod('GET');
}
}
Here, you create an API Gateway REST API resource, along with the following:
-
An integration between the REST API and your Lambda function, allowing the API to invoke your function. This includes the creation of a Lambda permission resource.
-
A new resource or path named
hello
that is added to the root of the API endpoint. This creates a new endpoint that adds/hello
to your base URL. -
A GET method for the
hello
resource. When a GET request is sent to the/hello
endpoint, the Lambda function is invoked and its response is returned.
Step 4: Prepare your application for deployment
In this step you prepare your application for deployment by building, if necessary, and performing basic validation
with the AWS CDK CLI
cdk synth
command.
If necessary, build your application:
From the root of your project, run the following:
$
npm run build
Run cdk synth
to synthesize an AWS CloudFormation template from your CDK code. By using L2 constructs,
many of the configuration details required by AWS CloudFormation to facilitate the interaction between your Lambda function and
REST API are provisioned for you by the AWS CDK.
From the root of your project, run the following:
$
cdk synth
Note
If you receive an error like the following, verify that you are in the cdk-hello-world
directory and try again:
--app is required either in command-line, in cdk.json or in ~/.cdk.json
If successful, the AWS CDK CLI will output the AWS CloudFormation template in YAML format at the
command prompt. A JSON formatted template is also saved in the cdk.out
directory.
The following is an example output of the AWS CloudFormation template:
Resources:
HelloWorldFunctionServiceRoleunique-identifier
:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: "2012-10-17"
ManagedPolicyArns:
- Fn::Join:
- ""
- - "arn:"
- Ref: AWS::Partition
- :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldFunction/ServiceRole/Resource
HelloWorldFunctionunique-identifier
:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket:
Fn::Sub: cdk-unique-identifier
-assets-${AWS::AccountId}-${AWS::Region}
S3Key: unique-identifier
.zip
Handler: hello.handler
Role:
Fn::GetAtt:
- HelloWorldFunctionServiceRoleunique-identifier
- Arn
Runtime: nodejs20.x
DependsOn:
- HelloWorldFunctionServiceRoleunique-identifier
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldFunction/Resource
aws:asset:path: asset.unique-identifier
aws:asset:is-bundled: false
aws:asset:property: Code
HelloWorldApiunique-identifier
:
Type: AWS::ApiGateway::RestApi
Properties:
Name: HelloWorldApi
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldApi/Resource
HelloWorldApiDeploymentunique-identifier
:
Type: AWS::ApiGateway::Deployment
Properties:
Description: Automatically created by the RestApi construct
RestApiId:
Ref: HelloWorldApiunique-identifier
DependsOn:
- HelloWorldApihelloGETunique-identifier
- HelloWorldApihellounique-identifier
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldApi/Deployment/Resource
HelloWorldApiDeploymentStageprod012345ABC
:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId:
Ref: HelloWorldApiDeploymentunique-identifier
RestApiId:
Ref: HelloWorldApiunique-identifier
StageName: prod
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldApi/DeploymentStage.prod/Resource
HelloWorldApihellounique-identifier
:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- HelloWorldApiunique-identifier
- RootResourceId
PathPart: hello
RestApiId:
Ref: HelloWorldApiunique-identifier
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldApi/Default/hello/Resource
HelloWorldApihelloGETApiPermissionCdkHelloWorldStackHelloWorldApiunique-identifier
:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName:
Fn::GetAtt:
- HelloWorldFunctionunique-identifier
- Arn
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Join:
- ""
- - "arn:"
- Ref: AWS::Partition
- ":execute-api:"
- Ref: AWS::Region
- ":"
- Ref: AWS::AccountId
- ":"
- Ref: HelloWorldApi9E278160
- /
- Ref: HelloWorldApiDeploymentStageprodunique-identifier
- /GET/hello
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldApi/Default/hello/GET/ApiPermission.CdkHelloWorldStackHelloWorldApiunique-identifier
.GET..hello
HelloWorldApihelloGETApiPermissionTestCdkHelloWorldStackHelloWorldApiunique-identifier
:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName:
Fn::GetAtt:
- HelloWorldFunctionunique-identifier
- Arn
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Join:
- ""
- - "arn:"
- Ref: AWS::Partition
- ":execute-api:"
- Ref: AWS::Region
- ":"
- Ref: AWS::AccountId
- ":"
- Ref: HelloWorldApiunique-identifier
- /test-invoke-stage/GET/hello
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldApi/Default/hello/GET/ApiPermission.Test.CdkHelloWorldStackHelloWorldApiunique-identifier
.GET..hello
HelloWorldApihelloGETunique-identifier
:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri:
Fn::Join:
- ""
- - "arn:"
- Ref: AWS::Partition
- ":apigateway:"
- Ref: AWS::Region
- :lambda:path/2015-03-31/functions/
- Fn::GetAtt:
- HelloWorldFunctionunique-identifier
- Arn
- /invocations
ResourceId:
Ref: HelloWorldApihellounique-identifier
RestApiId:
Ref: HelloWorldApiunique-identifier
Metadata:
aws:cdk:path: CdkHelloWorldStack/HelloWorldApi/Default/hello/GET/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Analytics: v2:deflate64:unique-identifier
Metadata:
aws:cdk:path: CdkHelloWorldStack/CDKMetadata/Default
Condition: CDKMetadataAvailable
Outputs:
HelloWorldApiEndpointunique-identifier
:
Value:
Fn::Join:
- ""
- - https://
- Ref: HelloWorldApiunique-identifier
- .execute-api.
- Ref: AWS::Region
- "."
- Ref: AWS::URLSuffix
- /
- Ref: HelloWorldApiDeploymentStageprodunique-identifier
- /
Conditions:
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
- il-central-1
- Fn::Equals:
- Ref: AWS::Region
- me-central-1
- Fn::Equals:
- Ref: AWS::Region
- me-south-1
- Fn::Equals:
- Ref: AWS::Region
- sa-east-1
- Fn::Or:
- Fn::Equals:
- Ref: AWS::Region
- us-east-1
- Fn::Equals:
- Ref: AWS::Region
- us-east-2
- Fn::Equals:
- Ref: AWS::Region
- us-west-1
- Fn::Equals:
- Ref: AWS::Region
- us-west-2
Parameters:
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]
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.
By using L2 constructs, you define a few properties to configure your resources and use helper methods to integrate them together. The AWS CDK configures the majority of your AWS CloudFormation resources and properties required to provision your application.
Step 5: Deploy your application
In this step, you use the AWS CDK CLI
cdk deploy
command to deploy your application. The AWS CDK works with the AWS CloudFormation service to provision your
resources.
Important
You must perform a one-time bootstrapping of your AWS environment before deployment. For instructions, see Bootstrap your environment for use with the AWS CDK.
From the root of your project, run the following. Confirm changes if prompted:
$
cdk deploy
✨ Synthesis time: 2.44s ... Do you wish to deploy these changes (y/n)?y
When deployment completes, the AWS CDK CLI will output your endpoint URL. Copy this URL for the next step. The following is an example:
... ✅ HelloWorldStack ✨ Deployment time: 45.37s Outputs: HelloWorldStack.HelloWorldApiEndpoint
unique-identifier
= https://<api-id>
.execute-api.<region>
.amazonaws.com/prod/ Stack ARN: arn:aws:cloudformation:region
:account-id
:stack/HelloWorldStack/unique-identifier
...
Step 6: Interact with your application
In this step, you initiate a GET request to your API endpoint and receive your Lambda function response.
Locate your endpoint URL from the previous step and add the /hello
path. Then, using your browser or
command prompt, send a GET request to your endpoint. The following is an example:
$
curl https://
{"message":"Hello World!"}%<api-id>
.execute-api.<region>
.amazonaws.com/prod/hello
Congratulations, you have successfully created, deployed, and interacted with your application using the AWS CDK!
Step 7: Delete your application
In this step, you use the AWS CDK CLI to delete your application from the AWS Cloud.
To delete your application, run cdk destroy
. When prompted, confirm your request to delete the
application:
$
cdk destroy
Are you sure you want to delete: CdkHelloWorldStack (y/n)?y
CdkHelloWorldStack: destroying... [1/1] ... ✅ CdkHelloWorldStack: destroyed
Troubleshooting
Error: {“message”: “Internal server error”}%
When invoking the deployed Lambda function, you receive this error. This error could occur for multiple reasons.
To troubleshoot further
Use the AWS CLI to invoke your Lambda function.
-
Modify your stack file to capture the output value of your deployed Lambda function name. The following is an example:
... class CdkHelloWorldStack extends Stack { constructor(scope, id, props) { super(scope, id, props); // Define the Lambda function resource // ... new CfnOutput(this, 'HelloWorldFunctionName', { value: helloWorldFunction.functionName, description: 'JavaScript Lambda function' }); // Define the API Gateway resource // ...
-
Deploy your application again. The AWS CDK CLI will output the value of your deployed Lambda function name:
$
cdk deploy
✨ Synthesis time: 0.29s ... ✅ CdkHelloWorldStack ✨ Deployment time: 20.36s Outputs: ... CdkHelloWorldStack.HelloWorldFunctionName = CdkHelloWorldStack-HelloWorldFunctionunique-identifier
... -
Use the AWS CLI to invoke your Lambda function in the AWS Cloud and output the response to a text file:
$
aws lambda invoke --function-name CdkHelloWorldStack-HelloWorldFunction
unique-identifier
output.txt -
Check
output.txt
to see your results.
- Possible cause: API Gateway resource is defined incorrectly in your stack file.
-
If
output.txt
shows a successful Lambda function response, the issue could be with how you defined your API Gateway REST API. The AWS CLI invokes your Lambda directly, not through your endpoint. Check your code to ensure it matches this tutorial. Then, deploy again. - Possible cause: Lambda resource is defined incorrectly in your stack file.
-
If
output.txt
returns an error, the issue could be with how you defined your Lambda function. Check your code to ensure it matches this tutorial. Then deploy again.