Step 3: Integrate Simple API into Simple web - solargis/cdk-workshop GitHub Wiki
In this step we will integrate API base URL into static web index.html
.
API URL is resolved during the deployment of the API, so we need a custom CloudFormation resource which will get real API URL and patch the x-api-base
meta tag in the intex.html
of the static web, directly in S3 bucket.
This custom resource is called WebIndex
and we will just copy-paste its code and use it in our CDK stack.
We will update index.html
to call simple API and display the response.
git checkout step-3/webindex
npm install
NOTE: if you have uncommited work in project repository it is not possible to checkout new Git branch, you have 3 options:
git stash
- move all local changes into a 'drawer', more info heregit commit -a -m "my change"
- commit all changes to local branchgit reset --hard HEAD
- remove all local changes
Add npm dependencies:
npm install --save @aws-cdk/aws-cloudformation cfn-response
Add WebIndex
lambda code: ./cdk/web-index-lambda.js
const AWS = require('aws-sdk');
const response = require('cfn-response');
const s3 = new AWS.S3();
module.exports.handler = (event, context, callback) => {
const { WebBucketName, ApiBaseUrl } = event.ResourceProperties;
switch (event.RequestType) {
case "Create":
case "Update":
patchIndexHtml().then(
() => send('SUCCESS', { Message: `Resource ${event.RequestType} successful!` }),
err => send('FAILED', { Error: '' + err })
);
break;
case "Delete":
send('SUCCESS', { Message: 'Resource Delete successful!' });
}
async function patchIndexHtml() {
const data = await s3.getObject({ Bucket: WebBucketName, Key: 'index.html' }).promise();
const html = data.Body && data.Body.toString('utf-8');
if (!html) {
throw Error('missing index.html');
} else {
const apiBaseUrlMeta = `<meta name="x-api-base" content="${ApiBaseUrl}">`;
const replacedHtml = html.replace(/<meta name="x-api-base" content=".*">/, apiBaseUrlMeta);
console.log('Updated index.html:', replacedHtml);
return s3.putObject({
Bucket: WebBucketName,
Key: 'index.html',
Body: replacedHtml,
ContentType: 'text/html; charset=UTF-8'
}).promise();
}
}
function send(responseStatus, responseData) {
response.send(event, context, responseStatus, responseData);
}
};
Add WebIndex
CDK construct: ./cdk/web-index.ts
import { CustomResource, CustomResourceProvider } from '@aws-cdk/aws-cloudformation';
import { Code, Runtime, SingletonFunction } from '@aws-cdk/aws-lambda';
import { IBucket } from '@aws-cdk/aws-s3';
import { ISource } from '@aws-cdk/aws-s3-deployment';
import { Construct, Duration } from '@aws-cdk/core';
import { path as rootPath } from 'app-root-path';
import { readFileSync } from 'fs';
import { resolve } from 'path';
export interface WebDeploymentProps {
bucket: IBucket;
source: ISource;
apiBaseUrl: string;
}
export class WebIndex extends Construct {
constructor(scope: Construct, id: string, props: WebDeploymentProps) {
super(scope, id);
const handlerPath = resolve(rootPath, 'cdk/web-index-lambda.js');
const handlerCode = readFileSync(handlerPath, 'utf8');
// custom CloudFormation resource handler
const handler = new SingletonFunction(this, 'WebIndexLambda', {
uuid: '4c84aa14-4077-11e9-bd73-47fe778e69cb',
code: Code.fromInline(handlerCode),
runtime: Runtime.NODEJS_8_10,
handler: 'index.handler',
lambdaPurpose: 'Custom::CDKWebIndex',
timeout: Duration.seconds(30)
});
props.bucket.grantReadWrite(handler);
const { zipObjectKey } = props.source.bind(this);
// Custom CloudFormation resource
new CustomResource(this, 'CustomResource', {
provider: CustomResourceProvider.lambda(handler),
resourceType: 'Custom::CDKWebIndex',
properties: {
// parameters required by
ApiBaseUrl: props.apiBaseUrl,
WebBucketName: props.bucket.bucketName,
zipObjectKey // force run on update dist/web
}
});
}
}
Integrate WebIndex
into the stack: ./cdk/cdk-workshop-stack
import { WebIndex } from './web-index';
const webDeployment = new BucketDeployment // ...
const webIndex = new WebIndex(this, 'WebIndex', {
apiBaseUrl: api.url,
source: webSource,
bucket: webBucket
});
webIndex.node.addDependency(webDeployment);
Update simple web - show message from hello API: ./lib/web/index.html
<html>
<head>
<!-- placeholder for API url -->
<meta name="x-api-base" content="/">
</head>
<body>
Hello from CDK
<pre id="helloResponse"></pre>
</body>
<script>
const apiBase = document.querySelector("meta[name='x-api-base']").getAttribute("content");
const xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE && xmlhttp.status == 200) {
document.getElementById("helloResponse").innerHTML = xmlhttp.responseText;
}
};
xmlhttp.open('GET', apiBase + 'hello', true);
xmlhttp.send();
</script>
</html>
Add CORS header to hello API: ./lib/api/hello-lambda.ts
statusCode: 200,
headers: { "Access-Control-Allow-Origin": "*" },
Build API code and deploy
npm run api:build
cdk deploy
- Test in browser
Or fast-forward to Step 7: Pin Thumbnails