DEV Community

Pedram Hamidehkhan
Pedram Hamidehkhan

Posted on

Building on AWS Using Github Copilot and AWS CDK

In this post we are going to have a look at Github's Copilot and how we can leverage it spin up some resources using AWS CDK.
Copilot is still in technical preview. But if you already signed up for it and are lucky enough like me to have access to it, you can enable it as a simple VS Code extension.
Of course, I do not expect for the Copilot to be very mature, as it is not publicly released yet, but I believe it seems very promising and arguably it changes the way we think about software development. Just like driving assistance systems, which are not a replacement for a driver, Github Copilot will also not replace developers, but rather empowers them.
There are already many videos on youtube and blog posts explaining basic functions that Copilot automatagically generates. In this post though, we try to see how it can be used in a serverless/aws world.
As you might already know, copilot uses billions of lines of code on Github, to give you suggestion while coding. It actually is even giving me suggestions a I am writing this post. What I am going to do is to perform the same tasks I did in this post using Copilot.
Some of the things I explain here are a bit difficult to grasp in writing form, so I will explain them in a video in a later parts of this post.

So as always, first thing's first, let's initialize an empty CDK typescript project:

cdk init --language=typescript.

Then we go to package.json to declare the dependencies for our project.
Add the following modules the dependencies:

npm install @aws-cdk/aws-ecs @aws-cdk/aws-ecs-patterns @aws-cdk/aws-ecr-assets --save
Enter fullscreen mode Exit fullscreen mode

By default, CDK deploys the VPC for you, but if you want control over your VPC, go ahead and import EC2 Module and add the VPC.
Then go to the lib folder and the typescript file for your application stack.

Add the following import statements to before class declaration:

import cdk = require("@aws-cdk/core");
import apigateway = require("@aws-cdk/aws-apigateway");
import dynamodb = require("@aws-cdk/aws-dynamodb");
import lambda = require("@aws-cdk/aws-lambda");
import { RemovalPolicy } from "@aws-cdk/core";
import { create } from "domain";
import { BillingMode } from "@aws-cdk/aws-dynamodb";
Enter fullscreen mode Exit fullscreen mode

As I write const dynamodbTable, copilot automatically generates the following part.

new dynamodb.Table(this, "dynamodbTable", {
Enter fullscreen mode Exit fullscreen mode

So it only instantiates the table using the packages I provided. However, as I go to the next line to provide the partition key and other settings for the table, it surprisingly suggested the following code:

partitionKey: { name: "id", type: dynamodb.AttributeType.STRING },
billingMode: BillingMode.PAY_PER_REQUEST,
removalPolicy: RemovalPolicy.DESTROY,
Enter fullscreen mode Exit fullscreen mode

Regarding the lambda functions, I start with the get all function. As I mentioned, it is important that you give meaningful names to your functions, because copilot will use those names to generate the code. So I only type const getAllItemsLambda, copilot generates the following for me:

  const getAllItemsLambda = new lambda.Function(this, "getAllItemsLambda", {
    runtime: lambda.Runtime.NODEJS_12_X,
    code: lambda.Code.fromAsset("lambda"),
    handler: "getAllItems.handler",
    environment: {
      TABLE_NAME: dynamodbTable.tableName,
Enter fullscreen mode Exit fullscreen mode

Very nice so far, regarding the IAM Roles for our lambdas, as I start the name of the db, I get the following suggestions:

Enter fullscreen mode Exit fullscreen mode

This is nice, though, it doesn't respect the principal of least privilege. so I amend the second line to grantReadData.

Now let's go for the API Gateway. As I start typing api, I get the following suggestion:

const api = = new apigateway.RestApi(this, "restApi", {
    restApiName: "cdkwithcopilot",
Enter fullscreen mode Exit fullscreen mode

I also get the following suggestions as I type the name of the resource:

  const root = api.root;
  const getAllItemsApi = new apigateway.LambdaIntegration(getAllItemsLambda);
  root.addMethod("GET", getAllItemsApi);

  const createItemsApi = new apigateway.LambdaIntegration(createItemsLambda);
  root.addMethod("POST", createItemsApi);
Enter fullscreen mode Exit fullscreen mode

All the resources were suggested as expected, only for the usage plan, copilot wrongly suggested api.UsagePlane instead of api.addUsagePlan . Also, it had a little problem when I added the Api Key.

Getting to the lambda functions, I was able to generate the following using copilot:

    const AWS = require('aws-sdk');
    const db = new AWS.DynamoDB.DocumentClient();
    const TablName = process.env.TABLE_NAME;

    export const handler = async (event: any = {}): Promise<any> => {
        const params = {
            TableName: TablName
        const result = await db.scan(params).promise();
        return result.Items;
Enter fullscreen mode Exit fullscreen mode

Also for the other lambda function, which creates the items:

    const AWS = require('aws-sdk');
    const db = new AWS.DynamoDB.DocumentClient();
    const TABLE_NAME = process.env.TABLE_NAME || ''; //added manually

    export const handler = async (event: any = {}): Promise<any> => {

        const params = {
            TableName: TABLE_NAME
            const response = await db.scan(params).promise();
            return {status: 'success', data: response.Items};
            return{status: 'error', data: dbError};
Enter fullscreen mode Exit fullscreen mode

Now the moment of truth: CDK synth
And worked as expected. I was truly amazed by how powerful copilot is. I even wrote a few lines of this blog post with it!
Arguably copilot is not a replacement for developers, but an amazing tool which helps us (developers) build better software.
I hope you enjoyed this post.
Thank you very much for reading.
Pedram and Copilot

Top comments (0)