This is the second part of our series that covers Adobe Target NodeJS SDK with On-Device Decisioning capabilities and how to run it in a serverless/edge compute environment. In this second part, we will be covering AWS Lambda and specifically AWS Lambda@Edge.
This blog is Part 2 in a three-part series that will cover how anyone could use Adobe Target NodeJS SDK to run experimentation and personalization on an edge compute platform. The parts are:
Part 1a: Akamai Edge Workers and Adobe Target NodeJS SDK
Part 1b: Akamai Edge Workers and Adobe Target NodeJS SDK
Part 2: AWS Lambda@Edge and Adobe Target NodeJS SDK
Part 3: Cloudflare Workers and Adobe Target NodeJS SDKStep by step guide
As mentioned in our previous blog, we use Terraform heavily at Adobe Target. In this article, we will show how you can leverage Terraform and Adobe Target NodeJS SDK to create an AWS Lambda@Edge.
AWS Lambda@Edge is a great technology if you intend to run some piece of logic in 200+ points of presence provided by AWS CloudFront. However, it is not trivial to set up, especially if we want to set it up in a secure way. That's why we will be using Terraform to bootstrap all the infrastructure elements.
Before we begin there are a few prerequisites:
AWS account: You will need a valid AWS account and credentials. Terraform relies on these credentials.
Terraform: We will use it to create all the required AWS resources. Please check the official Hashicorp documentation on how to install Terraform on your particular OS. In this article, we will be showing examples using Mac OS X.
Creating the origin S3 bucket resources
In order to use AWS Lambda@Edge we need to create a CloudFront distribution. At the same time, a CloudFront distribution requires an "origin". We don't really need an "origin", because we will use our own code to build an HTTP response. However to make AWS happy we will create a dummy S3 bucket. Here is the Terraform code to create a simple S3 bucket:
Note: This is a bare-bones function, for production use cases you’ll want to make sure that function errors and logs are forwarded to AWS CloudWatch.
Looking at the Terraform code for AWS Lambda function we can se that there is a filename, handlerand runtime fields. Let's see why we need these fields:
filename: This is the path to the ZIP archive containing the AWS Lambda functionsource code.
handler: This is a reference to a NodeJS exported function. Usually, it is something like index.handler, assuming that the main file from the ZIP archive is index.jsand it exports a function named handler.
runtime: This is the NodeJS runtime, we recommend using the latest NodeJS LTS version which is nodejs12.x.
Having all the Terraform code related to AWS Lambda function out of the way, let's see how we can useAdobe Target NodeJS SDKto power the Lambda function.
Note: The RULESconstant references the On-Device Decisioning artifact rules.json file. This file can be downloaded fromhttps://assets.adobetarget.com/<client code>/production/v1/rules.json. This file will be available only after you have enabled On-Device Decisioning for your Adobe Target account.
There is one thing worth mentioning, in the context of AWS Lambda function, Adobe Target NodeJS SDK has been created and tested in a server-side context and it has a few "background processes" like polling for On-Device Decisioning artifact updates, etc, so in order to make sure that AWS Lambda function does not hang and timeouts, we have to use:
context.callbackWaitsForEmptyEventLoop = false;
For more details around context.callbackWaitsForEmptyEventLoop please check the official Amazon documentation, which can be found here.
We have the sample AWS Lambda function handler and we have the On-Device Decisioning artifact aka rules.json. To be able to use this code we need to package it in a ZIP archive. On a UNIX based system this can be done using:
$ zip -r function.zip .
Creating the CloudFront distribution
To connect all the dots, we need to create the CloudFront distribution. Here is the Terraform code to do it:
There is a lot of boilerplate, but the most interesting pieces are:
origin: Here we connect S3 bucket and Origin Access Identity
default_cache_behavior: Here we have to make sure allowed_methods is set to ["HEAD", "DELETE", "POST", "GET", "OPTIONS", "PUT", "PATCH"] otherwise, we won't b able to process POST requests
lambda_function_association: Here we reference AWS Lambda function and ensure that we respond to viewer-request event type, which means that AWS Lambda will generate the response without "origin" being involved.
Testing it out
If everything was set up properly, then you should have a CloudFront distribution domain name. Using the domain name you could run a simple cURLcommand to check that everything is looking good. Here is a sample:
By looking at the sheer amount of Terraformcode one might ask:
Why even bother?
Why should I spend so much time and energy trying to deploy Adobe Target NodeJS SDK on AWS Lambda@Edge?
Here are a few benefits:
Isolation: From a security point of view, sometimes it is quite complicated to add yet another third-party dependency such as Adobe Target NodeJS SDK to your codebase. While deploying a similar code to AWS Lambda is pretty easy and everything is well isolated.
Decoupling: If your codebase depends on Adobe Target NodeJS SDK and there is a bug or security issue, sometimes it might be difficult to have a release, while with AWS Lambda being a serverless platform, this is trivial and less dangerous.
Flexibility: In the provided sample, we are returning a Target Delivery API response, but nothing stops you from adding custom logic and transformation to have a custom JSON output. Also, you could build custom REST APIs on top of AWS Lambda@Edge that is tailored to your domain.
Performance: Not everyone is Amazon or Google or Adobe and even if you have a presence in multiple geographic locations you can't beat CloudFront with its 200+ points of presence. By using AWS Lambda@Edge and Adobe Target NodeJS SDK you get low latency and a lot of flexibility.