DEV Community

scl
scl

Posted on

A reference CDK stack


import { Construct, IConstruct } from 'constructs';
import * as cdk from 'aws-cdk-lib/core';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

export interface MyStackProps extends cdk.StackProps {
  stageName: string;
  prefix: string;
  retainData: boolean;
}

export class CdkApiSqsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: MyStackProps) {
    super(scope, id, props);

    // Create an SQS queue
    const queue = new sqs.Queue(this, 'BooksQueue', {
      visibilityTimeout: cdk.Duration.seconds(300),
      queueName: `${props.prefix}-books-queue-${props.stageName}`,
    });

    // Create a DynamoDB table
    const carsTable = new dynamodb.Table(this, 'CarsTable', {
      tableName: `${props.prefix}-cars-${props.stageName}`,
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      partitionKey: { name: 'carId', type: dynamodb.AttributeType.STRING },
      removalPolicy: props.retainData ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY, // NOT recommended for production code
    });

    // Create a REST API
    const api = new apigateway.RestApi(this, 'Api', {
      restApiName: `${props.prefix}-api-${props.stageName}`,
      deployOptions: {
        stageName: props.stageName,
      },
      retainDeployments: false,
    });

    // Define the /books resource
    const books = api.root.addResource('books');

    // Add a POST method to the /books resource
    books.addMethod('POST', new apigateway.AwsIntegration({
      service: 'sqs',
      path: `${cdk.Aws.ACCOUNT_ID}/${queue.queueName}`,
      integrationHttpMethod: 'POST',
      options: {
        credentialsRole: new iam.Role(this, 'ApiGatewaySqsRole', {
          assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
          managedPolicies: [
            iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSQSFullAccess')
          ]
        }),
        requestParameters: {
          'integration.request.header.Content-Type': "'application/x-www-form-urlencoded'"
        },
        requestTemplates: {
          'application/json': `Action=SendMessage&MessageBody=$util.urlEncode($input.body)`
        },
        integrationResponses: [
          {
            statusCode: '200',
            responseTemplates: {
              'application/json': `{"status": "message queued"}`
            }
          },
          {
            statusCode: '500',
            responseTemplates: {
              'application/json': `{"status": "error"}`
            }
          }
        ]
      }
    }), {
      methodResponses: [
        {
          statusCode: '200'
        },
        {
          statusCode: '500'
        }
      ]
    });

    // Grant API Gateway permissions to send messages to the SQS queue
    queue.grantSendMessages(new iam.ServicePrincipal('apigateway.amazonaws.com'));

    // Define the /cars resource
    const cars = api.root.addResource('cars');

    const carSchema = {
      type: 'object',
      required: ['name', 'age'],
      properties: {
        name: { type: 'string' },
        age: { type: 'number' }
      }
    };

    // Create a model for request validation
    const requestModel = api.addModel('RequestModel', {
      contentType: 'application/json',
      modelName: 'RequestModel',
      schema: {
        schema: apigateway.JsonSchemaVersion.DRAFT4,
        type: apigateway.JsonSchemaType.OBJECT,
        title: 'MyRequest',
        required: ['name', 'age'],
        additionalProperties: false,
        properties: {
          "name": { type: apigateway.JsonSchemaType.STRING },
          "age": { type: apigateway.JsonSchemaType.INTEGER }
        }
      }
    });

    // Create a request validator
    const requestValidator = new apigateway.RequestValidator(this, 'RequestValidator', {
      restApi: api,
      validateRequestBody: true,
      requestValidatorName: 'request-validator'
    });

    // Add a POST method to the /cars resource
    cars.addMethod('POST', new apigateway.AwsIntegration({
      service: 'dynamodb',
      action: 'PutItem',
      integrationHttpMethod: 'POST',
      options: {
        credentialsRole: new iam.Role(this, 'ApiGatewayDynamoDbRole', {
          assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
          managedPolicies: [
            iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonDynamoDBFullAccess')
          ]
        }),
        requestTemplates: {
          'application/json': `{"TableName": "${carsTable.tableName}",
            "Item": {
              #foreach($entry in $input.path('$').entrySet())
              #if($entry.value != '')
                "$entry.key": { "S": "$entry.value" },
                #end
    #end
                  "carId": { "S": "$context.requestId" }
            }
        }`,
        },
        integrationResponses: [
          {
            statusCode: '200',
            responseTemplates: {
              'application/json': `{"status": "car added"}`
            },
            selectionPattern: '200',
          },
          {
            statusCode: '400',
            responseTemplates: {
              'application/json': `{"status": "client error"}`
            },
            selectionPattern: '4..',
          }
          ,
          {
            statusCode: '500',
            responseTemplates: {
              'application/json': `{"status": "server error"}`
            }
          }
        ]
      }
    }), {
      methodResponses: [
        {
          statusCode: '200'
        },
        {
          statusCode: '400'
        },
        {
          statusCode: '500'
        }
      ],
      requestModels: {
        'application/json': requestModel
      },
      requestValidator: requestValidator
    });

    // Grant API Gateway permissions to put items into the DynamoDB table
    carsTable.grantWriteData(new iam.ServicePrincipal('apigateway.amazonaws.com'));
  }
}
Enter fullscreen mode Exit fullscreen mode

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more