AWS Lambda@Edge
Starter Kit hosted on GitHub for running Optimizely Feature Experimentation feature flags and experiments on the AWS Lambda@Edge functions using AWS Lambda and AWS CloudFront.
Lambda@Edge is an extension of AWS Lambda, a compute service that lets you execute functions that customize the content that CloudFront delivers.
Advantages of Lambda@Edge
-
Speed – Extend your compute location closer to our users with CloudFront's reduction in latency.
-
Cost – Consolidate requests and remove transfer out fees from AWS origins.
-
Security – Improved security from DDOS attacks through AWS Standard Shield.
AWS Lambda@Edge use cases
You can build a variety of solutions, such as:
-
Inspect cookies to rewrite URLs to different versions of a site for experimentation.
-
Send different objects to your users based on the User-Agent header, which contains information about the device that submitted the request. For example, you can send images in different resolutions to users based on their devices.
-
Inspect headers or authorized tokens, inserting a corresponding header and allowing access control before forwarding a request to the origin.
-
Add, delete, and modify headers and rewrite the URL path to direct users to different objects in the cache.
-
Generate new HTTP responses to do things like redirect unauthenticated users to login pages or create and deliver static web pages right from the edge. For information, see Using Lambda functions to generate HTTP responses to viewer and origin requests in the Amazon CloudFront Developer Guide.
In this starter kit, you use Lambda@Edge to call your Optimizely Feature Experimentation datafile and make decisions on whether feature flags are enabled or disabled based on an incoming user ID by checking the value of OPTIMIZELY_USER_ID
from the cookies.
AWS Lambda@Edge restrictions and limitations
There are some restrictions to using Lambda@Edge.
- As of July 2022, the maximum compressed size of a Lambda@Edge package cannot exceed 50MB for origin events and 1MB for viewer events.
Further considerations
For even greater performance gains, consider using AWS CloudFront Functions and AWS S3 in tandem with AWS Lambda@Edge.
Optimizely + AWS Lambda@Edge starter kit
The Optimizely Feature Experimentation starter kit for AWS's Lambda@Edge embeds and extends the Javascript SDK to provide a starting point for you to implement experimentation and feature flagging for your experiences at the edge. For a guide to getting started with Optimizely Feature Experimentation more generally, this can be combined with the steps outlined in the JavaScript SDK Quickstart.
Note
This starter kit makes use of the "Lite" version of the JavaScript SDK for Node.js.
External data fetching and caching
This starter kit uses standard ES7 async/await fetch methods to handle external data fetching. After fetching the Optimizely Feature Experimentation datafile, the datafile itself is cached as a JSON object in memory. Large datafiles may cause this method presented in the starter kit to break. If you experience issues with large datafiles breaking in-memory Lambda caching, you can consider one of the alternative methods of caching with Lambda@Edge outlined in the leveraging external data Lambda@Edge documentation.
Alternative methods to in-memory data caching include using a persistent connection to your datafile JSON or caching using CloudFront.
For even faster data fetching, you can store your datafile in an S3 bucket that you own and refactor the datafile fetching mechanism to use Lambda's built-in AWS SDK library and fetch from your S3 bucket instead.
Note
Additional caching mechanisms may be available through your CloudFront distribution's configuration.
Identity management
Optimizely's Feature Experimentation SDKs require passing a user-provided identifier at runtime to drive experiment and feature flag decisions. This example generates a unique ID, stores it in a cookie, and reuses it to make the decisions sticky. Alternatively, you can use an existing unique identifier available within your application and pass it in as the value for the OPTIMIZELY_USER_ID cookie.
Bucketing
For information on how Optimizely Feature Experimentation SDKs assign users to feature flags and experiments, see the documentation on how bucketing works.
How to use
Prerequisites
You will need to complete the following prerequisites to use this template:
- Set up and configure AWS Lambda@Edge.
- Have an Optimizely account. If you do not have an account, you can register for a free account on Optimizely.com.
Get started
- After your Lambda@Edge environment is prepared, clone the starter kit from GitHub to your local development environment and run:
npm install
- Go to
src/index.js
and update the<YOUR_SDK_KEY_HERE>
and<YOUR_FLAG_HERE>
values to the respective values from your Optimizely dashboard. To access your SDK key from the Optimizely app, go to the Settings tab.Note
You can not store environment variables with Lambda@Edge. If you would like to inject the SDK key into your Lambda, an alternative method is outlined in this StackOverflow post detailing how to use CloudFront Origin Custom Headers as a workaround.
- Hook into different lifecycle events by inserting logic to change headers, cookies, and more in the switch-case statement in
src/index.js
. - Utilize Optimizely's JavaScript Lite bundle to make decisions and log events to influence the behavior of that logic.
- Use Rollup to bundle the source code into a neat .zip file to be imported into Lambda:
npm run build
Note
Notice that a
/dist
folder is generated with the new dist.zip file. It should be roughly ~22KB in size assuming you have not made any additional changes. - Upload your function to AWS Lambda using the GUI or CLI.
- Go to to your AWS Lambda console, select the function associated with your Lambda@Edge environment, and import the
dist.zip
file. After you upload it, there should now be a minifiedindex.js
file located inside of your Lambda function's "Code Source" section. - AWS CLI – You can use the AWS CLI to update your AWS Lambda function programmatically. Example command:
aws lambda update-function-code --function-name my-aws-lambda-at-edge-function --zip-file fileb://dist.zip
- Lambda Layers – If you need additional libraries, custom runtimes, or configuration files to use alongside your Lambda function, consider looking into utilizing Lambda Layers.
- Go to to your AWS Lambda console, select the function associated with your Lambda@Edge environment, and import the
- After your Lambda Function is set up, ensure that you have provisioned it with Lambda@Edge permissions and associate it with your CloudFront distribution. Set the CloudFront trigger for this function to "Viewer Request".
Note
CloudFront triggers are associated with only one specific version of your Lambda function. Remember to update the CloudFront trigger assignment as needed when pushing new versions of your Lambda function. You may, for example, need to have one function that handles receiving viewer requests (viewer request trigger) and one function that handles returning a response to the viewer (viewer response trigger).
- Test your Lambda@Edge function. You should see that it returns a simple home page with your feature flag test results.
To see what the starter kit is accomplishing, you can go to your CloudWatch console. UnderLogs
>Log groups
>/aws/lambda/<YOUR_LAMBDA_NAME>
, click into your Lambda's log group and view the test. You can find the entire process of reading the headers, assigning the user ID, fetching the datafile, and making the decision.Note
Also provided are:
- A slightly altered version of
index.js
-viewer_request.js
, which is a file that outlines how to return arequest
object instead of aresponse
object. You can refer to this file when designing functions that only adjust the request to your origin rather than return a response. viewer_response.js
, which is a file that reads the cookie from the request headers and includes them in the return response. You can refer to this file when accommodating for retrieving a user ID generated from the viewer request hook and re-using it in the viewer response hook.
- A slightly altered version of
- Adjust your Lambda's configuration as needed. For example, you may need to increase your function's memory, storage, and timeout threshold to accommodate your function's needs.
- From here, you can use Optimizely Feature Experimentation experimentation features as desired. You can modify the cookies and headers based on experimentation results, add hooks to the "Origin Request", and "Origin Response" CloudFront triggers to do things like origin redirects or dynamic asset manipulation. You can add more services to the pipeline, including your logging systems, databases, CDN origins, and more. Remember that Lambda@Edge has some limitations. See Edge Functions Restrictions.
Additional resources
Updated 8 months ago