A Serverless App with AWS SAM to Rediscover Liked Tweets


aws lambda aws sam serverless java twitter

Check here for cross post of this write-up on dev.to

Introduction

Following the tradition of solving our little Twitter problems with serverless, this post describes an app that would help you rediscover tweets that you have liked in the past. Of course, with the goal of learning how to do serverless along the way. We will be building this simple yet interesting app using AWS Lambda and related services. For configuring and gluing together all the components we will be making use of AWS SAM.

On Twitter, you may be following a bunch of smart folks and often tap the like icon for interesting thoughts they share. However, once you have liked a tweet, it's quite hard to rediscover that after a while. Twitter's native option for that is a chronological display of all liked Tweets with an endless scroll. That, let's just say, isn't that cool.

The serverless app we are going to build will show you back a liked tweet of yours with some serendipity added to the mix. Demo page is here. Though, once Twitter API rate limit is reached, it may not work :-( Below is a snapshot of how it should look though. Also, as we shall see, you may follow along the steps and deploy the same for yourself from the source here on Github.


Prerequisites

Before starting out building this app, here's a list of things you need to have. You may find further details in the readme as well.

Components

Here's a view of different components of the app and how they interact:

  1. The flow gets triggered when a user, via the browser, invokes the API Gateway endpoint URL passing along their Twitter username.
  2. API Gateway, in turn in an event driven fashion, invokes the Lambda function with a request object containing the username.
  3. Lambda function on invocation fetches Twitter Authentication keys from AWS SSM Parameter Store. Followed by using these keys to invoke Twitter API to fetch a batch of tweets liked by the user. Finally, one random tweet out of this list is returned as response and eventually gets shown back to the user.

In the upcoming sections of this post, let's look at each of these components one by one.

Parameter Store

For invoking the Twitter APIs, we need to first obtain authentication keys using a Twitter Developer account. Once we have the keys, to be able to securely store and access them from Lambda function, we will make use of AWS SSM Parameter Store. To keep things simple and fetch all authentication related details in a single call to SSM, we shall be storing them as a comma separated StringList param as follows:




Next, we need to define this parameter in the SAM template. This, as we shall see later in the SAM config, will be referred to as an environment variable by the Lambda function. This practice helps in achieving customization of stacks during deployment using SAM CLI. We may pass along different parameter names at the time of deployment, that correspond to respective values for different environments.

For example, let's say that, here we have defined a parameter resource TwitterAuthParam with default value as TWITTER_AUTH for all non prod environments. When we need to run this app on prod environment, we may override the same during deployment with value TWITTER_AUTH_PROD. Accordingly, the Lambda function with no change in code will be able to access the respective SSM param depending on the environment that it's running in.


1
2
3
4
Parameters:
  TwitterAuthParam:
    Type: String
    Default: TWITTER_AUTH

Lambda

AWS Lambda function, defined for this app, contains the code that for a given username invokes Twitter APIs to get a list of liked tweets for the user. This is followed by simple randomization to choose one liked tweet and return it back as a response. Lambda function is written in Java and makes use of the awesome Twitter4J library. Of course, the same can be achieved in any language using a similar Twitter library.

Lambda function code follows the best practice of performing initializations in the constructor code like fetching authentication keys from Parameter Store and creating Twitter connection instance. Keeping these initializations outside the handler function means that they get carried out only once during initial Cold Start for a given instance. This helps in making faster all subsequent handler function invocations on the same object.

Here's the part of the SAM Template defining config for the Lambda function:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
RandomLikesFunction:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: RandomLikesFunction
    Handler: com.hs.randomlikes.App::handleRequest
    Runtime: java8
    MemorySize: 512
    Timeout: 15
    Environment:
      Variables:
        TWITTER_AUTH_PARAM: !Ref TwitterAuthParam
    Policies:
      - SSMParameterReadPolicy:
          ParameterName: !Ref TwitterAuthParam
    Events:
      GetResource:
        Type: HttpApi
        Properties:
          Method: get
          Path: /u/{name}
          ApiId: !Ref RandomLikesHttpApi

API Gateway

Frontend for the app uses vanilla JS which invokes the API Gateway URL with user's Twitter username as a parameter. It's using HTTP flavor of API which is faster and simpler to use compared to REST (pun intended :-)

Further, as we are using HTTP proxy integration, API Gateway converts the HTTP request into a JSON and passes this to the Lambda function. In Java world, this translates into an event of type APIGatewayProxyRequestEvent as an input to the Lambda function. On the way back Lambda function returns APIGatewayProxyResponseEvent as output, which eventually gets converted by API Gateway into an HTTP response eventually returned back to the caller.

API Gateway, other than providing an interface to the Lambda function, also helps secure the app. This is required as the API endpoint is not authenticated and we need to define throttling limits to avoid any abuse. Or, in other words, to avoid ending up with a big AWS bill due to numerous Lambda invocations.

Finally, we need to enable CORS config to ensure that the browser allows our domain to invoke API Gateway endpoint and is able to access the response.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  RandomLikesHttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      DefaultRouteSettings:
        ThrottlingBurstLimit: 5
        ThrottlingRateLimit: 20
      CorsConfiguration:
        AllowMethods:
          - GET
        AllowOrigins:
          - "https://random-likes.harprit.dev"

SAM Template

Finally, let's have a look at the complete AWS SAM configuration template for the app. Here's a visual of the same that describes different sections of the template and shows how SAM glues together all the components discussed above.




Conclusion

You may may find further details like how to build and deploy the app from the source code and README. As mentioned above, purpose behind this app was to build something fun and a bit useful while learning to build serverless apps with AWS SAM along the way. I hope it met that goal somewhat.