Localstack + CDK = local AWS development
Tags: aws, serverless, typescript, cloud
Published: 2020-11-17 12:26:20

##### Github: https://github.com/mikigraf/CDK-with-Localstack

# Motivation

During the development cycle a deployment to the cloud is one of the most time consuming tasks, that the developer has no control over - besides making sure that the template will actually deploy. Once the resources get pushed into the cloud there is not much that can be done and the time gets probably wasted. 

There’s also another aspect that gets forgotten when developing for the cloud - there are still areas where internet connection is bad or non-existent at all. So if one decides to live a van life, but still wants to work on a cloud-based project - she’s still able to.

Localstack provides a lower barrier of entry for AWS. It addresses some concerns, that developers may have at first. They often hear horror stories about huge bills and hacked accounts. 

# Setup

Before we begin there are three things we need to have installed:
* [CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_install)
* [CDK Local](https://github.com/localstack/aws-cdk-local)
* [Docker](https://www.docker.com/)

There's also AWS CLI, which is required for interacting with the infrastructure. It's actually possible to specify the endpoint URL, so instead of sending the requests to the cloud directly, we will send them to Localstack.
aws --endpoint-url=http://localhost:4566 apigateway get-rest-apis

# Project: Api Gateway + Lambda as CDK Template

## Project initialization and dependencies
```bash
cdk init --language=typescript
```

Add dependencies to *cdk/package.json*:
```javascript
  "dependencies": {
    "aws-cdk": "1.72.0",
    "@aws-cdk/aws-apigateway": "1.72.0",
    "@aws-cdk/aws-lambda": "1.72.0",
    "@aws-cdk/core": "1.72.0",
  }
```
it’s important to keep the versions of libraries related to CDK on the same version! And Install dependencies:
npm install --save

### CDK Template
In the constructor of your stack (for me in *cdk/lib/cdk-stack.ts*) add:
```typescript
import * as cdk from '@aws-cdk/core';
import { LambdaIntegration, RestApi} from "@aws-cdk/aws-apigateway";
import {Function, Code, Runtime} from "@aws-cdk/aws-lambda";
import {CfnOutput} from "@aws-cdk/core";

export class CdkStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const helloWorldLambda = new Function(this, 'helloWorldLambda', {
      runtime: Runtime.NODEJS_12_X,
      code: Code.fromAsset('lib/helloWorldLambda'),
      handler: 'index.handler',
    });

    const api = new RestApi(this, 'myapi', {});
    const helloWorldLambdaIntegration = new LambdaIntegration(helloWorldLambda);
    const helloResource = api.root.addResource('hello')
    helloResource.addMethod("GET", helloWorldLambdaIntegration)
    new CfnOutput(this, "Endpoint", { value: `http://localhost:4566/restapis/${api.restApiId}/prod/_user_request_${helloResource.path}` })
  }
}
```
We created REST API and added the newly created helloWorldLambda  function to it by creating an integration on the resource (path) ‘hello’ with HTTP method GET. 
*CfnOutput* is just a construct, that will output the url of the API. 
It’s important to note, that the URL of the API in Localstack differs from the URL of the API in AWS. *The Url, that will be outputed to the console by CDK is wrong*. 

## Lambda function
Create new directory for the lambda *lib/helloWorldLambda*, add *index.ts* and paste the following:
```typescript
export const handler: any = async (
  event: any,
  context: any,
  callback: any
): Promise<any> => {
    return {
        statusCode: 200,
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
        body: JSON.stringify("Hello World :)!"),
    };
};
```

Running 
```bash
cdk synth
```
should yield no errors now.

## Deploying CDK to Localstack
Head to the terminal and write:
```bash
docker-compose up
```
and wait for the message in the terminal, that will just say:
```bash
Ready.
```
In another terminal window bootstrap the CDK env:
```bash
cdklocal bootstrap
```
and then deploy to it:
```bash
cdklocal deploy
```

The output after the deployment will include something like this:
```bash
Outputs:
CdkStack.myapiEndpoint8EB17201 = https://eyjiyd1la4.execute-api.eu-central-1.amazonaws.com/prod/
```
Usually it would be the correct url to the api, but since we’re deploying to Localstack, we need to get the other output, that we defined.

Use curl to see if everything has worked fine:
```bash
curl <the url from the output with /hello at the end of the path
```

# Conclusion

Localstack provides a low barrier of entry for developers not comfortable with direct deployments into the cloud. In my opinion it’s a great tool for creating tests, but also for introducing developers to new AWS services.