Writing AWS lambdas with Architect
Tags: aws, lambda, node
Published: 2020-08-11 07:48:53

It was one of those days when you ought to write just a lambda. The code and tests were easy to write. The infrastructure was needlessly complicated. After searching for a solution, I found the Architect project. It provides a framework for writing lambdas. You have to focus on the code while the infrastructure is automated by it.

I wanted to experiment with this technology, so I went with building a lambda that would send notifications when someone negatively mentioned me on Twitter. Not a Twitter user, but I found the concept fun. You can find the project at https://github.com/thelexned/tell-me-when-they-are-angry. 

The idea is relatively simple. I ought to write a lambda that gets the latest twitter mentions for my Twitter account. Next, I should analyze the mentions for negative sentiments. When I encounter negative comments, I have to send a notification with these tweets. 

I will not discuss here the lambda's code. I will concentrate on how Architect works and how I used it for building this project.

Architect
Installing the Architect CLI and the AWS SDK would be the first step for working with Architect.
```
npm install -g @architect/architect
npm install -g aws-sdk
```

I created the app.arc file.
```
@app
tell-me-when-they-are-angry

@scheduled
tweet-sentiment-analyzer rate(1 hour)

@tables
mentions
  mentionId *String

@events
they-are-angry
```
Here I'm defining the app name, two lambdas, and one DynamoDB table. The scheduled lambda runs every hour and contains the main logic. The events lambda is present for provisioning the notification infrastructure. Its output is the notification data. The Architect framework created it. 

For working with Twitter's API, you need to create an application on their developer platform. Doing this will get you a set of tokens you can use for accessing the API. You can use the Twitter tokens for setting the environment variables for the lambda.

```
arc env [ENVIRONMENT] TWITTER_CONSUMER_KEY [VALUE]
arc env [ENVIRONMENT] TWITTER_CONSUMER_SECRET [VALUE]
arc env [ENVIRONMENT] TWITTER_ACCESS_TOKEN_KEY [VALUE]
arc env [ENVIRONMENT] TWITTER_ACCESS_TOKEN_SECRET [VALUE]
```
It generates a file called .arc-env.  Make sure you don't version this file as it may contain sensitive data. Also, beware of writing to it directly. It will not work. You have to run the arc env commands for setting the environment variables. 

The Twitter commands mentioned earlier have an ENVIRONMENT parameter and a VALUE parameter. The VALUE setting should get replaced by the values you have from Twitter. The ENVIRONMENT parameter can have one of three values: testing, staging, or production. The testing environment is used by the local Architect sandbox and the other two for deploying to AWS.

Assuming you have everything set up and your lambda code in place, you can deploy the app.
```
arc deploy [ENVIRONMENT]
```
The deployment is going to create CloudFormation templates for the infrastructure.  It then uploads and provisions the CloudFormation stacks. If you are using the project I built, you can go to SNS after the deployment and subscribe to the topic created. Then wait for someone to mention you negatively on Twitter.

App structure
Architect apps have a predefined structure. They need an app.arc file for the app definitions and an src directory for the lambdas. You might also have a directory inside src called shared. Here you can add the code you want to share inside your lambdas. Also, place the package.json file inside the lambda directory for installing the required modules. 

The project I built has the following structure:
```
.
├── app.arc
├── LICENSE.md
├── README.md
└── src
    ├── events
    │   └── they-are-angry
    │       └── index.js
    └── scheduled
        └── tweet-sentiment-analyzer
            ├── handler.js
            ├── handler.test.js
            ├── index.js
            ├── jest.config.js
            ├── jest.setup.js
            ├── mapper
            │   ├── tweets-mapper.js
            │   └── tweets-mapper.test.js
            ├── package.json
            ├── package-lock.json
            ├── repository
            │   ├── mentions-repository.js
            │   ├── mentions-repository.test.js
            │   ├── tweets-repository.js
            │   └── tweets-repository.test.js
            └── service
                ├── notifications-publisher.js
                ├── notifications-publisher.test.js
                ├── sentiment-analyzer.js
                └── sentiment-analyzer.test.js
```
You can notice that the tests are in the src directory. I'm not too fond of this, but I didn't find a way to add them somewhere else. I had this issue because I chose to mock the lambda's dependencies. If I used the sandbox, it would have worked. I didn't fancy that I couldn't assert anything on the sandbox, though.

Issues
During development, I struggled with an issue that prevented the deployment from working.
```
Unable to upload artifact ./src/scheduled/tweet-sentiment-analyzer referenced by CodeUri parameter of TweetSentimentAnalyzer resource.
ZIP does not support timestamps before 1980
```
It occurred because one of the dependencies in node_modules had the directory creation timestamp set to a timestamp that's before 1980. For fixing this, I had to add a hack to package.json.
```
"scripts": {
  "install": "find . -mtime +10950 -exec touch {} \\;",
}
```
The install script changes the timestamp for the files on the local directory when running npm install. The timestamps need to be fixed on every install because Architect runs the install command before deployments.