Step 5: Pin API - solargis/cdk-workshop GitHub Wiki
In this step we will implement Pin API - API for saving and managing Pins in the map.
Pin is a point in the map containing map coordinates, geocoding information and image metadata. For storing Pins we will use DynamoDB database and for storing pin images we will use S3 bucket.
Pin API will have following endpoints:
GET /pin
- get all pins with resolved public URLs to pin imagesPOST /pin
- store new pin with imageGET /pin/{pointUrl}
- get single pin with resolved public URL to imageDELETE /pin/{pointUrl}
- delete pin and its image
Pin API will be backed by single lambda handler pin-lambda
, which will recognize operation by HTTP method and path.
We will setup webpack build for API code, as pin-lambda
uses other npm dependencies which needs to be deployed together with Lambda code.
Fast-forward to this step (optional)
git checkout step-5/pin-api
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
5.1 Lambda API code (skip if fast-forwarded)
Add npm dependencies:
npm install --save uuid @aws-cdk/aws-dynamodb
npm install --save-dev @types/uuid webpack webpack-cli ts-loader
Extract API code from step5-pin-api.zip to ./
./lib/api/pin-lambda.ts - Lambda handler code for Pin API
./lib/api/utils/s3.utils.ts - Helper functions for S3 operations
./lib/shared/ - root of shared code between API and Angular
./lib/shared/types/nominatim.types.ts - Types for Nominatim geocoder responses
./lib/shared/types/pin.types.ts - Types used in Pin API
./lib/shared/utils/point.utils.ts - Helper functions for LatLng points
./pin-request.json - example request body for: POST /pin
5.2 Setup API build with Webpack
Add webpack config file for API build: ./lib/webpack.api.js
const { path: rootPath } = require('app-root-path');
const { resolve } = require('path');
const awsModules = ['aws-sdk', 'aws-sdk/clients/dynamodb', 'aws-sdk/clients/s3'];
const config = {
mode: 'none',
context: resolve(rootPath),
entry: {
'hello-lambda': './lib/api/hello-lambda.ts',
'pin-lambda': './lib/api/pin-lambda.ts'
},
externals: [...awsModules],
module: {
rules: [
{
test: /\.ts$/,
exclude: [/node_modules/],
use: [{
loader: 'ts-loader',
options: { configFile: 'tsconfig.api.json' }
}]
}
]
},
resolve: { extensions: ['.ts', '.js'] },
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: resolve(rootPath, 'dist/api')
},
target: 'node',
devtool: 'cheap-source-map'
};
module.exports = config;
Update api:build
script in ./package.json
"api:build": "trash dist/api && webpack --config lib/webpack.api.js",
- Build API code:
npm run api:build
- Check API production code in
./dist/api
5.3 Define cloud resources for Pin API
Add CORS utils into ./cdk/cors.utils.ts
import { IResource, MockIntegration, PassthroughBehavior } from '@aws-cdk/aws-apigateway';
export function addCorsOptions(apiResource: IResource, ...customHeaders: string[]) {
apiResource.addMethod('OPTIONS', new MockIntegration({
integrationResponses: [{
statusCode: '200',
responseParameters: {
'method.response.header.Access-Control-Allow-Headers':
"'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent"
+ (customHeaders && customHeaders.length ? ',' + customHeaders.join(',') : '') + "'",
'method.response.header.Access-Control-Allow-Origin': "'*'",
'method.response.header.Access-Control-Allow-Credentials': "'false'",
'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,GET,PUT,POST,PATCH,DELETE'",
},
}],
passthroughBehavior: PassthroughBehavior.NEVER,
requestTemplates: {
"application/json": "{\"statusCode\": 200}"
},
}), {
methodResponses: [{
statusCode: '200',
responseParameters: {
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
'method.response.header.Access-Control-Allow-Credentials': true,
'method.response.header.Access-Control-Allow-Origin': true,
},
}]
})
}
Define S3 Bucket and DynamoDB table for Pin API: ./cdk/cdk-workshop-stack.ts
import { CfnOutput, Construct, RemovalPolicy, Stack, StackProps } from '@aws-cdk/core';
import { AttributeType, BillingMode, Table } from '@aws-cdk/aws-dynamodb';
// ...
// S3 bucket for hosting pin images
const imageBucket = new Bucket(this, 'ImageBucket');
// DynamoDB table to store pins
const pinTable = new Table(this, 'PinTable', {
partitionKey: {
name: 'pointUrl',
type: AttributeType.STRING
},
billingMode: BillingMode.PAY_PER_REQUEST,
removalPolicy: RemovalPolicy.DESTROY
});
Define CDK Lambda handler for Pin API: ./cdk/cdk-workshop-stack.ts
const pinHandler = new Function(this, 'PinHandler', {
code: apiCode,
runtime: Runtime.NODEJS_10_X,
handler: 'pin-lambda.handler',
environment: { // pass environment variables from CDK resources
IMAGE_BUCKET: imageBucket.bucketName,
PIN_TABLE: pinTable.tableName
}
});
// grant access to image bucket and pin table
imageBucket.grantReadWrite(pinHandler);
pinTable.grantReadWriteData(pinHandler);
Define Pin API Endpoints: ./cdk/cdk-workshop-stack.ts
import { addCorsOptions } from './cors.utils';
const api = // ...
const pinApi = api.root.addResource('pin');
// OPTIONS /pin
addCorsOptions(pinApi);
// ANY /pin
pinApi.addMethod('ANY', new LambdaIntegration(pinHandler));
const pinPointApi = pinApi.addResource('{pointUrl}');
// OPTIONS /pin/{pointUrl}
addCorsOptions(pinPointApi);
// ANY /pin/{pointUrl}
pinPointApi.addMethod('ANY', new LambdaIntegration(pinHandler));
Build API and deploy
npm run api:build
cdk deploy
Test
-
Test in browser: https://XXXXXXXXXX.execute-api.eu-central-1.amazonaws.com/prod/pin
-
[Linux / MAC only] Post new pin: POST /pin
curl 'https://XXXXXXXXXX.execute-api.eu-central-1.amazonaws.com/prod/pin' -H 'Content-Type: application/json' -d @pin-request.json
-
Test again in browser: https://XXXXXXXXXX.execute-api.eu-central-1.amazonaws.com/prod/pin
-
Check resources in AWS Console: S3 imagebucket, DynamoDB pintable