Implement 'Get Subscribers'
In this section, we'll deploy a Lambda function linked to API Gateway. We'll begin by crafting the IaC for get-subscribers
.
Implementation
- Navigate to
lambda-api-gateway-start-here
located withincdk-src
. - Our stack is already set up in
app.py
, so our modifications will focus onlambda_api_gateway_stack.py
. - Before moving forward, ensure you create and activate your
virtualenv
. Sincevirtualenv
isn't included in git, set it up usingpython -m venv .venv
or your preferred method, such as poetry, then activate withsource .venv/bin/activate
. - Install the necessary dependencies with
pip install -r requirements.txt
. - While code organization can be subjective, a best practice in CDK is to structure it as:
|--app1
|----iac #(1)!
|----application code #(2)!
|--app2
|----iac
|----application code
app.py #(3)!
- CDK code related to this app is found here. Usually, each app will create a separate stack.
- The source code for the application itself.
- Tie all the stacks in a single file -
app.py
.
Where each app corresponds to its own stack.
- Insert the content
from constructs import Construct
from aws_cdk import (
Stack,
aws_lambda_python_alpha as lambda_python, #(1)!
aws_apigateway as apigw,
aws_lambda,
aws_dynamodb as ddb,
CfnOutput
)
class LambdaApiGatewayStack(Stack): #(2)!
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: #(3)!
super().__init__(scope, construct_id, **kwargs)
table = ddb.Table( #(4)!
self,
"SubscribersTable",
partition_key={"name": "group_name", "type": ddb.AttributeType.STRING},
sort_key = {"name": "subscriber", "type": ddb.AttributeType.STRING}
)
get_subscribers_function = lambda_python.PythonFunction( #(5)!
self,
"GetSubscribersFunction",
entry="mailinglist_app",
index="src/app.py",
handler="lambda_handler",
runtime=aws_lambda.Runtime.PYTHON_3_11,
environment={
"SUBSCRIBERS_TABLE": table.table_name
}
)
api = apigw.LambdaRestApi( #(6)!
self,
"MailingListAPI",
handler=get_subscribers_function,
proxy=False, #(7)!
integration_options=apigw.LambdaIntegrationOptions(allow_test_invoke=False), #(8)!
)
group = api.root.add_resource("{group}") #(9)!
subscribers = group.add_resource("subscribers")
subscribers.add_method("GET")
table.grant_read_data(get_subscribers_function) #(10)!
CfnOutput(self, "GroupURL", value=f"{api.url}{{group}}/subscribers") #(11)!
- Start by importing the necessary constructs. Beyond the built-in constructs, we'll also incorporate an alpha construct named
aws_lambda_python_alpha
. - Here, we're creating a stack.
- Each stack receives a parent construct and a logical ID.
- We'll begin by specifying our initial resource: a DynamoDB table. It's crucial to note the order of definition; the table must be established before the Lambda since we assign permissions to it subsequently.
- Next, define a Lambda function.
- Establish a Rest API Gateway linked to the aforementioned Lambda.
- For more granular control over our API Gateway resources and methods (refer to item 9), ensure the
proxy
attribute is set tofalse
. If not adjusted, all http methods will be configured on the root path. - When set to
True
, an additional permission is appended to the AWS Lambda resource policy, enabling thetest-invoke-stage
stage to invoke this handler. If set toFalse
, the function can only be accessed via the deployment endpoint. - Append resources and methods. It's vital to differentiate between path parameters and the remaining parts of the path.
- Bestow the Lambda with the necessary permissions to interface with DynamoDB.
- As a part of the CDK deployment process, output the complete HTTP path.
into iac/lambda_api_gateway_stack.py
- Deploy the application. Run
cdk deploy
- Try invoking the API listed in the outputs.
Insights
Constructs
Constructs form the core of every CDK application. In the aforementioned code, we employed three constructs to establish a Lambda function, an API Gateway, and a DynamoDB table. Additionally, we devised a construct symbolizing a stack, consumed by the CDK app outlined in app.py
.
Your CDK endeavors will likely follow this pattern: importing various constructs, utilizing them to create resources, and bundling everything within a stack.
Alpha Constructs
Constructs come in different maturity levels; some are designated as "preview," meaning they aren't integral to the official SDK. These constructs possess distinct packages and necessitate separate installations. In our example, we adopted a construct that enhances the packaging procedure for an AWS Lambda scripted in Python. The conventional Lambda construct demands manual construction of the deployment package, like installing requirements and zipping. The alpha construct simplifies this by anticipating a Python packaging file, such as requirements.txt
or pyproject.toml
.
Typically, preview constructs are appended with an -alpha
suffix.
Bear in mind, the alpha Python construct mandates Docker.
Order Importance
Contrary to the template crafted using AWS SAM, our script here is imperative, making execution sequence pivotal. This is because we're sharing parameters across various objects, necessitating certain objects to be instantiated first for subsequent code use. We required the table name within the Lambda environment settings, prompting us to initialize the table at the outset. In AWS SAM, the underlying engine (CloudFormation) determines the creation sequence.
Outputs
Diverging from AWS SAM, which mandates explicit output definitions to view any, CDK provides certain default outputs. For instance, without any specific definition, you'll receive the HTTP address in the output, resembling:
LambdaApiGatewayStack.MailingListAPIEndpoint92BB94CD = https://yyyyyy.execute-api.us-east-1.amazonaws.com/prod/
CfnOutput
construct can be utilized.
Exercises
- Include the DynamoDB table name in the output.
- Enhance the stack by adding the
add_subscriber
lambda.