Single Repo: AWS CDK, Route53, APIGateway, Lambda, Cloudfront, Flask & Nuxt

TL;DR Step-by-step tutorial building a full-stack application using AWS-CDK, Flask & Nuxt using a unified code repository for all components: frontend, backend, and infrastructure.

Velocity

Every business is looking to accelerate product-driven businesses, many would say it is the most stable determinant of success.

Shift left the deployment pipeline

AWSCDK

The AWS Cloud Development Kit (AWS CDK) is an open-source software development framework to define your cloud application resources using familiar programming languages.

Fullstack

To demonstrate a real-world environment, I used several technologies to build a full-stack environment, including backend and frontend components. Although all the technologies are combined into the same code-base, let’s take a look at each layer separately. It will help us create a baseline for what we are already familiar with: the infrastructure and application layers.

Init

This tutorial requires the installation of the AWS CDK library. The CDK is written in Typescript and requires node and npm to be installed.

npm install -g aws-cdk
git clone https://gitlab.com/eilon2/spa-flask-aws-cdk
python3 -m venv awscdk
. awscdk/bin/activate
pip install -r requirements.txt

Code Walkthrough

Directory tree

A quick look at AWS CDK project will highlight how easy it is:

.
├── app.py # AWSGEN
├── backend # The backend code of the project
│ ├── app.py
│ ├── Dockerfile
│ └── requirements.txt
├── cdk.json # AWSGEN
├── cdk.out # AWSGEN
├── frontend # The frontend code of the project
│ ├── package.json
│ └── pages
│ ├── about.vue
│ └── index.vue
├── package-lock.json
├── README.md
├── requirements.txt
├── setup.py # AWSGEN
├── source.bat # AWSGEN
└── spa_flask_aws_cdk # AWSGEN
├── __init__.py
├── spa_flask_aws_cdk.egg-info
│ ├── dependency_links.txt
│ ├── PKG-INFO
│ ├── requires.txt
│ ├── SOURCES.txt
│ └── top_level.txt
└── spa_flask_aws_cdk_stack.py
6 directories, 20 files

DNS & Certificates

Let’s start with making sure our users can browse the application using a domain name. I assume that there is already an existing root hosted zone — ROOT_ZONE; in our case, it will be saasment.com

# File: spa_flask_aws_cdk_stack.pyROOT_ZONE = 'saasment'# Create interface to already existing hosted zone
root_hosted_zone = route53.HostedZone.from_hosted_zone_attributes(self,
'RootHostedZone',
hosted_zone_id='Z05422531LZAQAHZETW5S',
zone_name=ROOT_ZONE)
  1. Creating an NS record in the root hosted zone to point our new hosted zone
  2. Creating & validating certificates in ACM for both frontend ‘cdk-spa.demo.saasment.com’ and backend ‘cdk-spa-api.demo.saasment.com’ domains
# File: spa_flask_aws_cdk_stack.pyZONE = 'demo.saasment.com'
DOMAIN_NAME_FRONT = 'cdk-spa.demo.saasment.com'
DOMAIN_NAME_BACKEND = 'cdk-spa-api.demo.saasment.com'
# Create a new zone
hosted_zone = route53.HostedZone(self,
'HostedZone',
zone_name=ZONE)
# Create NS record in the root zone, it will point to our new zone
route53.NsRecord(self,
'HostedZoneNS',
values=hosted_zone.hosted_zone_name_servers,
zone=root_hosted_zone,
record_name=ZONE)
# Create & validate SSL certificates for frontend & backend domains
front_certificate = acm.Certificate(self,
'staticWebCert',
domain_name=DOMAIN_NAME_FRONT,
validation=acm.CertificateValidation.from_dns(hosted_zone))
backend_certificate = acm.Certificate(self,
'apiCert',
domain_name=DOMAIN_NAME_BACKEND,
validation=acm.CertificateValidation.from_dns(hosted_zone))

Static Frontend

One of the most exciting features of AWS CDK is the option to use standard development/build/deployment patterns.

Our frontend infro + app layers
# File: spa_flask_aws_cdk_stack.py# Create bucket for static pagesstatic_bucket = s3.Bucket(self,
'staticWebBucket',
website_index_document='index.html',
website_error_document='index.html',
public_read_access=True)
# Upload nuxt static pages to S3
core_image=core.BundlingDockerImage.from_registry(image='node:lts')
build_cmd=['bash', '-c', ' && '.join(['ls -la ', 'npm install', 'npm run build', 'npm run generate', 'cp -r /asset-input/dist/* /asset-output/'])]
s3deployment = s3deploy.BucketDeployment(self,
'staticDeployment',
destination_bucket=static_bucket,
sources=[s3deploy.Source.asset('./frontend',
bundling=core.BundlingOptions(image=core_image,
command=build_cmd))])
# Init view policy
view_policy = cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS
# Init error responses
error_403 = cloudfront.ErrorResponse(http_status=403, response_http_status=200, response_page_path='/index.html')
error_404 = cloudfront.ErrorResponse(http_status=404, response_http_status=200, response_page_path='/index.html')distribution = cloudfront.Distribution(self,
'staticWebBucketDistribution',
default_behavior={
'origin': origins.S3Origin(static_bucket),
'viewer_protocol_policy': view_policy
},
domain_names=[DOMAIN_NAME_FRONT],
certificate=front_certificate,
error_responses=[error_403, error_404])
# Init alias
front_target = route53.RecordTarget.from_alias(targets.CloudFrontTarget(distribution))
# Create A & AAA records to point cloudfront and serves frontend
frond_r53_record = route53.AaaaRecord(self,
'frontendAlias',
zone=hosted_zone,
target=front_target,
record_name=DOMAIN_NAME_FRONT)

Serverless Backend

Flask is THE most popular Python web application development framework because of its simplicity, extensibility, and community. For the same reasons, AWS Lambda is THE most popular function as a service framework.

Our backend infra + app layers
# File: spa_flask_aws_cdk_stack.py# Pack backend code as docker
backend = lambda_.DockerImageFunction(self,
"ECRFunction",
code=lambda_.DockerImageCode.from_image_asset("./backend")
)
# Create API Gateway instance
apigwdomain = apigateway.DomainNameOptions(
certificate=backend_certificate,
domain_name=DOMAIN_NAME_BACKEND)
# Trigger lambda by APIGateway
apigw = apigateway.LambdaRestApi(self,
"myapi",
handler=backend,
domain_name=apigwdomain)
backend_target = route53.RecordTarget.from_alias(targets.ApiGateway(apigw))backend_alias = route53.AaaaRecord(self,
'backendAlias',
zone=hosted_zone,
target=backend_target,
record_name=DOMAIN_NAME_BACKEND)

Leapfrogging

Probably the most famous leapfrog is the mobile revolution, which put phones in the hands of millions of people while allowing developing nations to skip directly to mobile phones without the need to invest in landline infrastructure.

You live at least once

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store