Building a User Management Service with AWS API Gateway, Lambda, and DynamoDB
Welcome to this exciting journey where we’ll create a simple user management service using Amazon API Gateway, AWS Lambda, and Amazon DynamoDB! By leveraging the AWS Cloud Development Kit (CDK) and AWS Chalice, we’ll build our infrastructure and application logic as code. These frameworks allow us to automate deployments and manage code efficiently. Let’s dive in!
Overview of the AWS Frameworks
AWS Cloud Development Kit (CDK) is a powerful open-source framework that allows you to define cloud infrastructure using familiar programming languages such as Python, Java, TypeScript, and more. In our case, we’ll use Python to create a robust architecture.
On the flip side, AWS Chalice is a Python microframework specifically designed for building serverless applications. It simplifies the development of applications utilizing API Gateway and Lambda. Chalice automates many resource definitions, such as creating Swagger documentation and managing application configurations.
By combining CDK for infrastructure and Chalice for application logic, we achieve a seamless and synchronized development environment.
Architecture Considerations
When using CDK and Chalice together, determining where to manage the IAM roles for your Lambda functions can be a gray area. We will opt to create the IAM role using CDK. This approach allows us to maintain a single SAM template definition that we can use across different environments, simplifying deployment.
For instance, the Lambda function’s memory size and timeout can also be specified within Chalice, as long as these parameters remain constant across environments.
Prerequisites
To get started, make sure you have the CDK CLI installed by running:
bash
npm install -g aws-cdk
Building the Application Logic
-
Setting Up: First, let’s create a directory for our project and set up the Chalice web API.
bash
mkdir users-service
cd users-service
python3 -m venv .venv
source .venv/bin/activate
pip install chalice==1.12.0
chalice new-project web-api
cd web-api -
Adding Dependencies: We will use the Boto3 library alongside Chalice. Update the
requirements.txtfile:bash
echo “boto3==1.10.30” > requirements.txt
echo “chalice==1.12.0” >> requirements.txt
pip install -r requirements.txt -
Writing the Code: Open
app.pyand replace its content with the following code:python
import os
import boto3
from chalice import Chaliceapp = Chalice(app_name=”web-api”)
dynamodb = boto3.resource(‘dynamodb’)
dynamodb_table = dynamodb.Table(os.environ[‘DYNAMODB_TABLE_NAME’])@app.route(‘/users’, methods=[‘POST’])
def create_user():
user = app.current_request.json_body
dynamodb_table.put_item(Item=user)
return user@app.route(‘/users/{username}’, methods=[‘GET’])
def get_user(username):
response = dynamodb_table.get_item(Key={‘username’: username})
return response[‘Item’]@app.route(‘/users/{username}’, methods=[‘DELETE’])
def delete_user(username):
dynamodb_table.delete_item(Key={‘username’: username})
The above code snippet implements a basic web API for managing users in DynamoDB, handling user creation, retrieval, and deletion.
Building the Infrastructure Logic
-
Create a New CDK Project:
Navigate back to the root directory and create a new folder for infrastructure.
bash
cd ..
mkdir infra
cd infra
cdk init –language python –generate-only
rm -rf infra
rm setup.py -
Set Up Dependencies: Update the
requirements.txtfile in the CDK project:bash
echo “aws_cdk.aws_dynamodb==1.19.0” > requirements.txt
echo “aws_cdk.core==1.19.0” >> requirements.txt
echo “cdk-chalice==0.4.0” >> requirements.txt
pip install -r requirements.txt -
Create the Stack: Create a new
stackspackage with theweb_api.pymodule:bash
mkdir stacks
touch stacks/init.py
touch stacks/web_api.py -
Define the Stack Logic: Open
stacks/web_api.pyand add the following code:python
import os
from aws_cdk import (
aws_dynamodb as dynamodb,
aws_iam as iam,
core as cdk
)
from cdk_chalice import Chaliceclass WebApi(cdk.Stack):
def __init__(self, scope: cdk.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) partition_key = dynamodb.Attribute(name="username", type=dynamodb.AttributeType.STRING) self.dynamodb_table = dynamodb.Table(self, 'UsersTable', partition_key=partition_key, removal_policy=cdk.RemovalPolicy.DESTROY) cdk.CfnOutput(self, 'UsersTableName', value=self.dynamodb_table.table_name) lambda_service_principal = iam.ServicePrincipal('lambda.amazonaws.com') self.api_handler_iam_role = iam.Role(self, 'ApiHandlerLambdaRole', assumed_by=lambda_service_principal) self.dynamodb_table.grant_read_write_data(self.api_handler_iam_role) web_api_source_dir = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'web-api') chalice_stage_config = self._create_chalice_stage_config() self.chalice = Chalice(self, 'WebApi', source_dir=web_api_source_dir, stage_config=chalice_stage_config) def _create_chalice_stage_config(self): return { 'api_gateway_stage': 'v1', 'lambda_functions': { 'api_handler': { 'manage_iam_role': False, 'iam_role_arn': self.api_handler_iam_role.role_arn, 'environment_variables': { 'DYNAMODB_TABLE_NAME': self.dynamodb_table.table_name }, 'lambda_memory_size': 128, 'lambda_timeout': 10 } } }
This code defines the infrastructure for our user management service, including the creation of a DynamoDB table and the necessary IAM roles for our Lambda function.
-
Deploying the Stack: Now, alter
app.pyto include the new stack:python
import os
from aws_cdk import core as cdk
from stacks.web_api import WebApiapp = cdk.App()
dev_env = cdk.Environment(account=os.environ[‘CDK_DEFAULT_ACCOUNT’], region=os.environ[‘CDK_DEFAULT_REGION’])
prod_eu_west_1_env = cdk.Environment(account=”123456789012″, region=’eu-west-1′)
prod_us_east_1_env = cdk.Environment(account=”123456789012″, region=’us-east-1′)WebApi(app, ‘WebApiDev’, env=dev_env)
WebApi(app, ‘WebApiProdEuWest1’, env=prod_eu_west_1_env)
WebApi(app, ‘WebApiProdUsEast1’, env=prod_us_east_1_env)app.synth()
Testing Your API
Once all the configurations are in place, it’s time to deploy the development stack:
bash
cdk synth
cdk deploy WebApiDev
Upon successful deployment, you will receive output with the API endpoint. You can test your new user management API using curl:
bash
curl -H “Content-Type: application/json” -X POST -d ‘{“username”:”john”, “email”:”john@example.com”}’ https://n6doqg3ewl.execute-api.eu-west-1.amazonaws.com/v1/users
Local Testing
For local testing, you can start the Chalice local server:
bash
env DYNAMODB_TABLE_NAME=YourTableName chalice local
This will let you test your API endpoints locally against a pre-created DynamoDB table.
Cleanup Resources
Finally, when you no longer need the infrastructure, execute:
bash
cdk destroy WebApiDev
This command will remove all the AWS resources created during your project lifecycle.
Insightful Takeaways
By combining AWS CDK and AWS Chalice, you effectively streamline your development process while adhering to industry standards for best practices in infrastructure as code. This approach makes it easier to manage and deploy serverless applications in a more organized, efficient, and scalable manner, driving down the complexities usually associated with cloud service management.