Ends in
00
days
00
hrs
00
mins
00
secs
ENROLL NOW

🎁 Get 20% Off - Christmas Big Sale on All Practice Exams, Video Courses, and eBooks!

Mastering AWS CDK Part 1: Using Built-In Constructs for Serverless APIs

Home » Others » Mastering AWS CDK Part 1: Using Built-In Constructs for Serverless APIs

Mastering AWS CDK Part 1: Using Built-In Constructs for Serverless APIs

In the realm of Infrastructure as Code (IaC) within AWS, CloudFormation stands out as the foundational service. Many other IaC solutions, whether it’s the Serverless Framework, AWS SAM, or even Terraform, ultimately get translated into CloudFormation. Additionally, the AWS Cloud Development Kit (CDK) has emerged as a distinct player in this space.

What sets AWS CDK apart is its embrace of familiar programming languages. Instead of relying on YAML or proprietary syntax, with AWS CDK, developers can define infrastructure using languages like Python, Javascript, Typescript, Java, C#, and Go. This familiarity accelerates the adoption curve, enabling developers to direct their energy toward crafting business solutions rather than wrestling with infrastructure nuances.

Today, crafting RESTful APIs has become a staple task for backend developers. What once involved intricate configurations and deployments can now be streamlined with serverless architectures.

Here’s the architecture of building serverless RESTful APIs leveraging the AWS CDK:

Mastering AWS CDK Part 1: Using Built-In Constructs for Serverless APIs

This architecture embodies a classic three-tiered design. At its forefront, AWS API Gateway serves as the conduit for front-end interactions. AWS Lambda provides the computational muscle in a serverless fashion, ensuring scalable and efficient processing. Meanwhile, Amazon DynamoDB anchors the setup, offering reliable storage and persistence for data.

Prerequisites

  1. Set up a development environment or development account in AWS.

  2. Install Node, AWS CDK, and the AWS CLI.

  3. Configure AWS credentials locally.

Initializing a new AWS CDK App

Next, to initialize a fresh project with AWS CDK, input the following command:

npx cdk init app --language=typescript

Using npx ensures the utilization of the latest version of AWS CDK.

TypeScript is selected as the programming language for this architecture with AWS CDK. If TypeScript is not installed on the system, it can be added with this command:

npm install -g typescript

Mastering AWS CDK Part 1: Using Built-In Constructs for Serverless APIs

Bootstrapping

Bootstrapping is an essential initial step when using AWS CDK. It grants AWS CDK the necessary permissions to operate within your account. This foundational step must be completed before deploying any AWS CDK apps. During the bootstrapping process, requisite resources are provisioned, including storage for crucial files and IAM roles to facilitate deployments. This action results in the creation of a CloudFormation Stack, typically named CDKToolKit.

To initiate the bootstrapping process for your application, run the command:

cdk bootstrap

In AWS CDK, constructs serve as the foundational elements of infrastructure, similar to Lego bricks in a construction set. Each construct corresponds to a logical resource in CloudFormation, which then translates into tangible or physical resources within the cloud environment. These constructs are housed within Stacks in the AWS CDK, mirroring the concept of CloudFormation Stacks. Essentially, an AWS CDK app outlines one or several such stacks.

The process of establishing an app and stack can be observed in bin/tutorials-dojo-cdk-app.ts. Upon deploying this app to the cloud, it will instantiate a CloudFormation stack named TutorialsDojoCdkAppStack.

Mastering AWS CDK Part 1: Using Built-In Constructs for Serverless APIs

As previously mentioned, constructs reside within AWS CDK Stacks. AWS offers a suite of pre-configured constructs, empowering developers and DevOps professionals to seamlessly craft Infrastructure as Code. An exemplar Stack can be found in lib/tutorials-dojo-cdk-app-stack.ts, which AWS generates during the initialization of an AWS CDK application. To further assist customers, AWS recommends an SQS construct as a guide for declaring other constructs. The serverless RESTful API’s implementation would be integrated within this file.

Mastering AWS CDK Part 1: Using Built-In Constructs for Serverless APIs

The DynamoDB Table AWS CDK Construct

First, extract the DynamoDB construct from the aws-cdk-lib NodeJS package and incorporate it into the lib/tutorials-dojo-cdk-app-stack.ts Stack. This aws-cdk-lib package was already installed when the AWS CDK application was initialized.

import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

Within the Stack class, instantiate the DynamoDB table. The following snippet showcases the AWS CDK method for creating a DynamoDB table, specifying core properties like the partitionKey and tableName. It’s essential to understand that these properties represent just a fraction of the offerings from the DynamoDB Table Construct. When delving into new constructs, consulting the official AWS documentation is a prudent step to ensure comprehensive utilization.

The Lambda Function AWS CDK Construct

Begin by importing the Lambda construct from the aws-cdk-lib NodeJS package and adding it to the Stack:

Tutorials dojo strip
import * as lambda from 'aws-cdk-lib/aws-lambda';

Subsequently, within the Stack class, establish the Lambda function. The provided sample highlights several properties essential for crafting this Lambda function, with the runtime, handler, and code properties being mandatory.

// Create a Lambda function
const lambdaFunction = new lambda.Function(this, 'TutorialsDojoLambdaFunction', {
  runtime: lambda.Runtime.NODEJS_18_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset('lambda'),
  environment: {
    DYNAMO_TABLE_NAME: dynamoTable.tableName,
  }
});

The runtime is specified to determine what programming language and the version the Lambda function would be running (yes, it is possible for a Lambda function to run in python even if the AWS CDK construct used in declaring this function is written in Typescript — same goes for other programming languages).

For the handler, the convention <file-name>.<function-name> is used. Here, file-name denotes the name of the file housing the Lambda function code. Conversely, function-name indicates the specific method or function Lambda should trigger upon invocation. In this instance, the Lambda function is contained within the index.ts file, and the primary method to be invoked is termed handler. This explains the handler’s designation as index.handler.

The code property pinpoints the Lambda function’s location within the codebase. This capability stands out in IaC tools, including AWS CDK, since CloudFormation lacks this direct feature. Conventionally, CloudFormation necessitates the Lambda code to be uploaded to Amazon S3 first, followed by referencing its bucket and key in the CloudFormation script. Conversely, in this context, the Lambda function code is situated in the lambda directory at the project’s root.

Finally, the environment property in the Lambda function construct allows for the specification of the Lambda function’s environment variables, although it’s not mandatory. In this context, the DynamoDB table name is relayed as an environment variable. This facilitates its use during the Lambda function’s runtime, especially when engaging with the AWS SDK to carry out operations on the designated DynamoDB table.

The official AWS documentation for the Lambda function construct offers a comprehensive list of properties not showcased in this example. It’s essential for customers, especially those looking to optimize their Lambda functions via AWS CDK as their preferred IaC tool, to be aware of these additional configuration options. These details provide avenues for further fine-tuning and customization of the Lambda functions.

To align with the Lambda function construct defined, modifications to the current AWS CDK project’s file structure are imperative. Here’s what needs to be done:

  • Establish a new directory at the root level of the project, titled lambda.

  • Within the lambda directory, generate a file named index.ts.

This is what the updated file structure looks like:

Mastering AWS CDK Part 1 Using Built-In Constructs for Serverless APIs 4

Lastly, paste the following code inside the newly created index.ts file:

const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();
const tableName = process.env.DYNAMO_TABLE_NAME;  // This matches the tableName you provided in the CDK code

exports.handler = async (event: any) {
  const httpMethod = event.httpMethod;

  switch (httpMethod) {
    case 'POST':
      const item = JSON.parse(event.body);
      return await writeToDynamoDB(item);
    case 'GET':
      return await readAllFromDynamoDB();
    default:
      return {
        statusCode: 400,
        body: JSON.stringify('Invalid HTTP method')
      };
  }
};

const writeToDynamoDB = async (item: string) {
  const params = {
    TableName: tableName,
    Item: item
  };

  try {
    await dynamodb.put(params).promise();

    const body = {
      success: true,
      message: 'Successfully wrote item',
    }

    return {
      statusCode: 200,
      body: JSON.stringify(body)
    };
  } catch (error: any) {
    return {
      statusCode: 500,
      body: JSON.stringify(error.message)
    };
  }
};

const readAllFromDynamoDB = async () {
  const params = {
    TableName: tableName
  };

  try {
    const result = await dynamodb.scan(params).promise();

    const body = {
      success: true,
      message: 'Successfully read all items',
      data: result.Items
    }
    return {
      statusCode: 200,
      body: JSON.stringify(body)
    };
  } catch (error: any) {
    return {
      statusCode: 500,
      body: JSON.stringify(error.message)
    };
  }
};

Grant the Lambda Function Read and Write Access to the DynamoDB Table

To provide the Lambda function with read and write privileges to the DynamoDB table, consider the constructs dynamoTable and lambdaFunction. Here, dynamoTable pertains to the earlier defined DynamoDB table, while lambdaFunction references the newly set up Lambda function.

// Grant the Lambda function read/write permissions to the DynamoDB table   
dynamoTable.grantReadWriteData(lambdaFunction);

This scenario underscores the elegance of AWS CDK’s abstraction. Traditionally, granting a Lambda function access to a DynamoDB table involves a detailed process: creating an execution role and then defining permissions in an extensive JSON format, a task that can span multiple lines of code, especially when using CloudFormation. However, AWS CDK transforms this intricate setup. Thanks to its abstraction capabilities, what was once a lengthy configuration can be distilled into a succinct, single line of code. This not only simplifies the task but also enhances the developer’s experience.

The Lambda Layer AWS CDK Construct

Lambda layers offer a distinctive feature within AWS Lambda, facilitating the sharing of code and various assets among multiple Lambda functions. This not only champions code reusability but also enhances the efficiency of Lambda cold starts. Through offloading recurrent code to layers it diminishes the size of customers’ Lambda deployment packages.

In the context of this project, the aws-sdk package is anticipated to be a staple across numerous Lambda functions as they scale. Persistently creating distinct package.json files and installing identical dependencies for every Lambda function is inefficient and is flagged as an anti-pattern by AWS. Thus, for managing shared code and packages recurrent across Lambda functions, the best approach is to harness the power of Lambda layers.

Given our current Stack, this is how to instantiate a Lambda Layer:

// Create Lambda layer
const lambdaLayer = new lambda.LayerVersion(this, 'TutorialsDojoLambdaLayer', {
  compatibleRuntimes: [lambda.Runtime.NODEJS_18_X],
  code: lambda.Code.fromAsset('layers'),
  description: 'Tutorials Dojo Lambda Layer',
});

// Add the Lambda layer to the Lambda function
lambdaFunction.addLayers(lambdaLayer);

It’s crucial to ensure that the compatibleRuntimes property of the Lambda layer matches that of the Lambda function it’s intended for. Mismatches in runtime can lead to deployment failures in CloudFormation. The code property, akin to its role in the Lambda function construct, designates the layer’s location in the codebase. Additionally, while the description property is optional in the Lambda layer construct, it offers a way to annotate the layer, making it identifiable in the AWS Console.

Once the Lambda layer is established, the subsequent step is to associate it with the desired Lambda function. Without this linkage, the function remains oblivious to the layer.

For a comprehensive understanding and potential customization, it’s always recommended to consult the official AWS documentation related to the Lambda layer construct.

To seamlessly integrate with the Lambda layer construct, adjustments to the codebase’s file structure are required. Here are the steps to ensure the Lambda layer’s proper utilization:

  • At the project’s root level, introduce a new directory named layers.

  • Within the layers directory, instantiate a sub-directory called nodejs. This aligns with the prerequisites for crafting a Lambda layer targeting a Node runtime. For a deeper dive, the official AWS Lambda layers documentation provided in this guide’s references offers further insights.

  • Navigate to the nodejs directory and initiate a new Node project with the npm init -y command. Crucially, it’s vital to ensure that the local Node version mirrors the specified compatibleRuntimes in the Lambda layer construct to guarantee smooth deployments.

  • Install the aws-sdk package inside the nodejs directory with the npm install aws-sdk command.

This is what the updated file structure looks like:

Mastering AWS CDK Part 1 Using Built-In Constructs for Serverless APIs 5

The API Gateway REST API AWS CDK Construct

The API Gateway serves as the public-facing portal to the backend infrastructure. Upon its deployment, it generates a URL, enabling applications and API clients to interface with it. In the context of this project, AWS’s REST API construct is leveraged to forge a publicly accessible REST API in the AWS cloud.

One standout feature of AWS API Gateway is its robust authorization capabilities, among its myriad of functionalities. This allows for only authenticated and authorized users to be able to invoke APIs in the AWS API Gateway. A deeper exploration of these features is available in the official AWS API Gateway documentation. While crafting an API Gateway from the ground up offers granular control and flexibility, it’s a labor-intensive endeavor. While manual creation provides users with adaptability, it also introduces the possibility of overlooking certain AWS API Gateway features and integrations inherent to other AWS services.

The following is how to instantiate a API Gateway REST API AWS CDK Construct:

// Create an API Gateway
const apiGateway = new api.RestApi(this, "TutorialsDojoApiGateway", {
	restApiName: "TutorialsDojoApiGateway",
	description: "This is the Tutorials Dojo API Gateway",
});

The API Gateway REST API construct boasts a plethora of properties. A comprehensive list of these properties can be found in the official AWS API Gateway REST API construct documentation. These configurations empower users to meticulously tailor their API Gateway, adjusting aspects like CORS settings, headers, permitted methods, and more. In this example, only the restApiName and description are used.

Integration of the Lambda Function to the API Gateway

Integrating Lambda functions with API Gateway has emerged as a dominant serverless pattern in AWS. Recognizing this trend, AWS introduced a dedicated construct for this purpose. In this example, the LambdaIntegration construct facilitates the seamless integration of a Lambda function with an API Gateway.

// Integrate the Lambda function to the API Gateway
const lambdaIntegration = new api.LambdaIntegration(lambdaFunction);

Additionally, AWS CDK can define and manage both resources and their respective methods. This example shows how to integrate a specific resource and its associated methods with a Lambda function:

// Add GET method to the root resource
apiGateway.root.addMethod("GET", lambdaIntegration);

// Add POST method to the root resource
apiGateway.root.addMethod("POST", lambdaIntegration); 

In this example, the apiGateway variable represents the previously instantiated API Gateway REST API construct. These commands link the GET and POST methods to the API Gateway’s root resource. This root resource aligns with the default path in API Gateway, denoted by the / path.

Moreover, the earlier established LambdaIntegration construct is referenced here, indicating which Lambda function will handle these requests once deployed to the cloud.

Deployment of the CDK Application

After creating all these constructs inside the lib/tutorials-dojo-cdk-app-stack.ts Stack. The file should look like this:

Mastering AWS CDK Part 1 Using Built-In Constructs for Serverless APIs 6

Revisiting the architecture, it’s apparent that the right-most resource, the DynamoDB table in this instance, was the first to be defined. This approach aligns with a common best practice in AWS CDK and other IaC tools: begin with the service that has minimal integrations. Typically, this service corresponds to the resource that’s furthest in the architectural diagram.

To deploy the TutorialsDojoCdkAppStack AWS CDK Stack, run the following command in the root directory:

tsc
cdk deploy

The tsc command compiles all TypeScript code in the project, depending on the configurations set in tsconfig.json, and translates it into JavaScript. This conversion is vital as Lambda natively interprets JavaScript and lacks built-in support for TypeScript.

Furthermore, executing cdk deploy initiates the deployment of the TutorialsDojoCdkAppStack. The deployment’s progress can be monitored either through the terminal or directly in CloudFormation. In cases of deployment failures, it’s recommended to inspect the process in CloudFormation for a clearer understanding of the issues encountered.

Testing the Serverless REST API

Free AWS Courses

Evaluating the functionality of the REST API is feasible through any API client. In this demonstration, Postman serves as the chosen tool for probing the freshly deployed REST API endpoints.

Prerequisites

  1. Ensure Postman is installed.

  2. Set up the REST API’s base URL as an environment variable within Postman. This URL gets displayed in the terminal upon the Stack’s deployment.

  3. Create a Postman Collection.

Testing

Within the curated Postman Collection, fashion a new request. The initial test targets the POST request, with the configuration depicted in the following image:

Mastering AWS CDK Part 1 Using Built-In Constructs for Serverless APIs 7

Upon hitting “Send”, expect a response mirroring the format illustrated above.

The subsequent test focuses on the GET request. Replicate the initial POST request, modify the method to GET, and for the body, select “none” since GET requests typically lack a body.

The corresponding configuration can be observed below:

Mastering AWS CDK Part 1 Using Built-In Constructs for Serverless APIs 8

After hitting “Send”, anticipate a response reflecting the format showcased above.

It’s worth noting: as more entries are appended via the PUT Item API, they get enumerated in the output of the GET Items API.

The Most Common Pitfall: Should I Still Learn CloudFormation?

Amidst the rise of popular IaC tools, a question arises: Is mastering CloudFormation still pertinent for deploying AWS infrastructure using IaC? The answer is a resounding yes. A common misconception likens CloudFormation to assembly language and tools like AWS CDK, Terraform, and Serverless Framework to high-level languages. However, such an analogy is oversimplified. It’s crucial to recognize that there isn’t a distinct AWS CDK or Terraform service within AWS. At its core, CloudFormation is the foundational service, and other IaC tools simply offer layers of abstraction. Ultimately, all paths lead back to CloudFormation. Grasping the fundamental principles and mechanics of CloudFormation is essential for anyone leveraging IaC tools for AWS infrastructure deployment. Ignoring this foundation can lead to pitfalls and misunderstandings in the deployment process.

Final Remarks

AWS CDK, alongside other IaC tools, offers a myriad of benefits that redefine modern infrastructure management. The term “Infrastructure as Code” now feels somewhat dated. A more apt phrase in today’s context might be “Infrastructure IS Code.” Today’s infrastructure isn’t merely described or handled using code; it fundamentally materializes as code. Every element, from networking components to databases, is articulated, set up, and overseen through coding practices. Relying on manual deployment, particularly in production environments, has become an outdated approach for cloud-native applications. Contemporary standards gravitate towards IaC tools. From automation prowess and guaranteed consistency to fluid CI/CD integration, the advantages of IaC are manifold, significantly benefiting both developers and DevOps experts.

Constructing infrastructure in AWS with the AWS CDK offers users an enriched development experience, thanks to CDK’s adoption of widely-used programming languages. It’s akin to harnessing CloudFormation but with the familiarity and flexibility of a chosen programming language. This approach provides high-level, pertinent abstractions that elevate the entire development journey.

What to Expect in Part 2?

Part 2 delves deeper into the art of abstraction within the AWS CDK. This section introduces the diverse types of constructs in the AWS CDK, emphasizing the significance of developing custom and reusable constructs. By adopting this approach, the goal is to uphold the DRY (Don’t Repeat Yourself) principle and amplify code clarity. See you next time!

References:

https://docs.aws.amazon.com/cdk/v2/guide/home.html

https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html

https://docs.aws.amazon.com/cdk/v2/guide/hello_world.html

https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-typescript.html

https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html

https://aws.amazon.com/api-gateway/

https://aws.amazon.com/lambda/

https://aws.amazon.com/dynamodb/

Get 20% Off – Christmas Big Sale on All Practice Exams, Video Courses, and eBooks!

Tutorials Dojo portal

Learn AWS with our PlayCloud Hands-On Labs

Tutorials Dojo Exam Study Guide eBooks

tutorials dojo study guide eBook

FREE AWS Exam Readiness Digital Courses

FREE AWS, Azure, GCP Practice Test Samplers

Subscribe to our YouTube Channel

Tutorials Dojo YouTube Channel

Follow Us On Linkedin

Recent Posts

Written by: Iggy Yuson

Iggy is a DevOps engineer in the Philippines with a niche in cloud-native applications in AWS. He possesses extensive skills in developing full-stack solutions for both web and mobile platforms. His area of expertise lies in implementing serverless architectures in AWS. Outside of work, he enjoys playing basketball and competitive gaming.

AWS, Azure, and GCP Certifications are consistently among the top-paying IT certifications in the world, considering that most companies have now shifted to the cloud. Earn over $150,000 per year with an AWS, Azure, or GCP certification!

Follow us on LinkedIn, YouTube, Facebook, or join our Slack study group. More importantly, answer as many practice exams as you can to help increase your chances of passing your certification exams on your first try!

View Our AWS, Azure, and GCP Exam Reviewers Check out our FREE courses

Our Community

~98%
passing rate
Around 95-98% of our students pass the AWS Certification exams after training with our courses.
200k+
students
Over 200k enrollees choose Tutorials Dojo in preparing for their AWS Certification exams.
~4.8
ratings
Our courses are highly rated by our enrollees from all over the world.

What our students say about us?