Skip to content

Commit 4df6121

Browse files
authored
Merge pull request #2731 from marcojahn/main
added pattern - cloudfront-keyvaluestore-apigw-routing-cdk
2 parents 1ca8be6 + f8cc299 commit 4df6121

File tree

9 files changed

+587
-0
lines changed

9 files changed

+587
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Amazon CloudFront with Amazon API Gateway Routing using Key Value Store
2+
3+
This pattern demonstrates how to use Amazon CloudFront with CloudFront Functions to dynamically route traffic between multiple Amazon API Gateway endpoints. The routing decisions are based on values stored in CloudFront Key Value Store, allowing for flexible, configuration-driven request routing without redeploying your infrastructure.
4+
This example uses an equal (50:50 distribution) between both API Gateways. For more informations on "cell partitioning" refer to the [AWS Well-Architected Guide - Cell Partition](https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html)
5+
6+
Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk](https://serverlessland.com/patterns/cloudfront-keyvaluestore-apigw-routing-cdk)
7+
8+
Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
9+
10+
## Requirements
11+
12+
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
13+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
14+
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
15+
* [Node.js and npm](https://nodejs.org/) installed
16+
* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html) installed
17+
18+
## Deployment Instructions
19+
20+
1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
21+
22+
```
23+
git clone https://github.com/aws-samples/serverless-patterns
24+
```
25+
26+
1. Change directory to the pattern directory:
27+
28+
```
29+
cd cloudfront-keyvaluestore-apigw-routing-cdk
30+
```
31+
32+
1. Install dependencies:
33+
34+
```
35+
npm install
36+
```
37+
38+
1. Deploy the CDK stack to your default AWS account and region:
39+
40+
```
41+
cdk deploy
42+
```
43+
**The deployment will take a couple of minutes**
44+
45+
1. Note the outputs from the CDK deployment process. These contain the resource URLs and ARNs which are used for testing.
46+
47+
48+
## How it works
49+
50+
![Architecture Diagram](./cloudfront-keyvaluestore-apigw-routing-cdk.png)
51+
52+
1. The client makes a request to the CloudFront distribution
53+
1. The CloudFront Function executes on viewer request events
54+
1. The function retrieves routing information from CloudFront Key Value Store
55+
1. Based on the retrieved value, the function redirects the request to one of the two API Gateway endpoints
56+
1. The selected API Gateway returns its response to the client
57+
58+
In a real-world scenario the available targets would be maintained in the Key Value Store depending on the desired business logic and requirements.
59+
60+
## Testing
61+
62+
1. Get Key Value Store `ETAG` and note it down.
63+
64+
```
65+
aws cloudfront-keyvaluestore describe-key-value-store \
66+
--kvs-arn=[KVSTOREARN]
67+
```
68+
69+
1. Add entries to the Key Value Storereplace. Replace `[KVSTOREARN]`, `[APIGATEWAY1URL]`, and `[APIGATEWAY2URL]` with the noted outputs.
70+
71+
```
72+
aws cloudfront-keyvaluestore update-keys \
73+
--kvs-arn[KVSTOREARN] \
74+
--if-match=[ETAG] \
75+
--puts '[
76+
{
77+
"Key": "APIGW1URL",
78+
"Value": "[APIGATEWAY1URL] "
79+
},
80+
{
81+
"Key": "APIGW2URL",
82+
"Value": "[APIGATEWAY2URL]"
83+
}
84+
]'
85+
```
86+
87+
If you receive something similar to, it worked
88+
```json
89+
{
90+
"ETag": "KV3UN6WX5RRO2AG",
91+
"ItemCount": 2,
92+
"TotalSizeInBytes": 145
93+
}
94+
```
95+
96+
1. Access the CloudFront URL in a browser or via curl, replace `[CLOUDFRONTDOMAINNAME]`:
97+
98+
```
99+
curl -i -L [CLOUDFRONTDOMAINNAME]
100+
```
101+
102+
1. The request should be redirected to either API Gateway 1 or API Gateway 2, and you should see a response like:
103+
104+
```
105+
{"message": "Hello from API 1"}
106+
```
107+
108+
or
109+
110+
```
111+
{"message": "Hello from API 2"}
112+
```
113+
114+
1. Make multiple requests to observe the random routing between the two API Gateway endpoints.
115+
116+
117+
## Cleanup
118+
119+
1. Delete the stack
120+
```bash
121+
cdk destroy
122+
```
123+
----
124+
Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
125+
126+
SPDX-License-Identifier: MIT-0
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env node
2+
import * as cdk from "aws-cdk-lib";
3+
import { PatternStack } from "../lib/pattern-stack";
4+
5+
const app = new cdk.App();
6+
// amazonq-ignore-next-line
7+
new PatternStack(app, "PatternStack", {
8+
env: {
9+
account: process.env.CDK_DEFAULT_ACCOUNT,
10+
region: process.env.CDK_DEFAULT_REGION,
11+
},
12+
});
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"app": "npx ts-node --prefer-ts-exts bin/cloudfront-keyvaluestore-apigw-routing-cdk.ts",
3+
"watch": {
4+
"include": ["**"],
5+
"exclude": [
6+
"README.md",
7+
"cdk*.json",
8+
"**/*.d.ts",
9+
"**/*.js",
10+
"tsconfig.json",
11+
"package*.json",
12+
"yarn.lock",
13+
"node_modules",
14+
"test"
15+
]
16+
},
17+
"context": {
18+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
19+
"@aws-cdk/core:checkSecretUsage": true,
20+
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
21+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
22+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
23+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
24+
"@aws-cdk/aws-iam:minimizePolicies": true,
25+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
26+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
27+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
28+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
29+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
30+
"@aws-cdk/core:enablePartitionLiterals": true,
31+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
32+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
33+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
34+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
35+
"@aws-cdk/aws-route53-patters:useCertificate": true,
36+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
37+
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
38+
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
39+
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
40+
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
41+
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
42+
"@aws-cdk/aws-redshift:columnId": true,
43+
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
44+
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
45+
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
46+
"@aws-cdk/aws-kms:aliasNameRef": true,
47+
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
48+
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
49+
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
50+
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
51+
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
52+
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
53+
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
54+
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
55+
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
56+
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
57+
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
58+
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
59+
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
60+
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
61+
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
62+
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
63+
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
64+
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
65+
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
66+
"@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false,
67+
"@aws-cdk/aws-ecs:disableEcsImdsBlocking": true,
68+
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
69+
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
70+
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
71+
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
72+
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
73+
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
74+
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
75+
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
76+
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
77+
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true,
78+
"@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true,
79+
"@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true,
80+
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
81+
"@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": true
82+
}
83+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
{
2+
"title": "Amazon CloudFront with API Gateway Routing using Key Value Store",
3+
"description": "Route traffic dynamically between API Gateway endpoints using CloudFront Functions and Key Value Store without redeploying infrastructure.",
4+
"language": "TypeScript",
5+
"level": "300",
6+
"framework": "AWS CDK",
7+
"introBox": {
8+
"headline": "How it works",
9+
"text": [
10+
"This pattern demonstrates how to use Amazon CloudFront with CloudFront Functions to dynamically route traffic between multiple Amazon API Gateway endpoints.",
11+
"The routing decisions are based on values stored in CloudFront Key Value Store, allowing for flexible, configuration-driven request routing without redeploying your infrastructure.",
12+
"This example uses an equal (50:50 distribution) between both API Gateways, showcasing how to implement cell partitioning for your applications.",
13+
"The pattern deploys a CloudFront distribution, CloudFront Function, CloudFront Key Value Store, and two API Gateway endpoints."
14+
]
15+
},
16+
"gitHub": {
17+
"template": {
18+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/cloudfront-keyvaluestore-apigw-routing-cdk",
19+
"templateURL": "serverless-patterns/cloudfront-keyvaluestore-apigw-routing-cdk",
20+
"projectFolder": "cloudfront-keyvaluestore-apigw-routing-cdk",
21+
"templateFile": "lib/pattern-stack.ts"
22+
}
23+
},
24+
"resources": {
25+
"bullets": [
26+
{
27+
"text": "AWS Well-Architected Guide - Cell Partition",
28+
"link": "https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/cell-partition.html"
29+
},
30+
{
31+
"text": "CloudFront Key Value Store Documentation",
32+
"link": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html"
33+
},
34+
{
35+
"text": "CloudFront Functions Documentation",
36+
"link": "https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html"
37+
}
38+
]
39+
},
40+
"deploy": {
41+
"text": [
42+
"Clone the repository: <code>git clone https://github.com/aws-samples/serverless-patterns</code>",
43+
"Change directory: <code>cd cloudfront-keyvaluestore-apigw-routing-cdk</code>",
44+
"Install dependencies: <code>npm install</code>",
45+
"Deploy the CDK stack: <code>cdk deploy</code>"
46+
]
47+
},
48+
"testing": {
49+
"text": [
50+
"1. Get Key Value Store ETAG: <code>aws cloudfront-keyvaluestore describe-key-value-store --kvs-arn=[KVSTOREARN]</code>",
51+
"2. Add entries to the Key Value Store: <code>aws cloudfront-keyvaluestore update-keys --kvs-arn=[KVSTOREARN] --if-match=[ETAG] --puts '[{\"Key\": \"APIGW1URL\", \"Value\": \"[APIGATEWAY1URL]\"},{\"Key\": \"APIGW2URL\", \"Value\": \"[APIGATEWAY2URL]\"}]'</code>",
52+
"3. Access CloudFront URL: <code>curl -i -L [CLOUDFRONTDOMAINNAME]</code>",
53+
"4. The request should be redirected to either API Gateway 1 or API Gateway 2, showing a response like <code>{\"message\": \"Hello from API 1\"}</code> or <code>{\"message\": \"Hello from API 2\"}</code>",
54+
"5. Make multiple requests to observe routing between the two API Gateway endpoints."
55+
]
56+
},
57+
"cleanup": {
58+
"text": [
59+
"Delete the stack: <code>cdk destroy</code>"
60+
]
61+
},
62+
"authors": [
63+
{
64+
"name": "Marco Jahn",
65+
"image": "https://sessionize.com/image/e99b-400o400o2-pqR4BacUSzHrq4fgZ4wwEQ.png",
66+
"bio": "Senior Solutions Architect, Amazon Web Services",
67+
"linkedin": "marcojahn"
68+
}
69+
],
70+
"patternArch": {
71+
"icon1": {
72+
"x": 20,
73+
"y": 50,
74+
"service": "cloudfront",
75+
"label": "Amazon CloudFront"
76+
},
77+
"icon2": {
78+
"x": 50,
79+
"y": 50,
80+
"service": "cf-functions",
81+
"label": "CloudFront function"
82+
},
83+
"icon3": {
84+
"x": 80,
85+
"y": 20,
86+
"service": "apigw",
87+
"label": "Amazon API Gateway 1"
88+
},
89+
"icon4": {
90+
"x": 80,
91+
"y": 70,
92+
"service": "apigw",
93+
"label": "Amazon API Gateway 2"
94+
},
95+
"line1": {
96+
"from": "icon1",
97+
"to": "icon2",
98+
"label": ""
99+
},
100+
"line2": {
101+
"from": "icon2",
102+
"to": "icon3",
103+
"label": ""
104+
},
105+
"line3": {
106+
"from": "icon2",
107+
"to": "icon4",
108+
"label": ""
109+
}
110+
}
111+
}
46.4 KB
Loading

0 commit comments

Comments
 (0)