I recently developed a personal blog website to post web performance content and to use as my own personal sandbox for testing Akamai performance features. Since EdgeWorkers is one of the newest tools in our toolbox, I decided to try doing some personalization using EdgeWorkers.
The Question
All of the EdgeWorkers I’d implemented so far, including the infamous ‘Hello World’ example, served a unique page with the EdgeWorker information. However, I wanted to include some personalized information on my existing home page. Customers may want to personalize the amount of discount on an existing product page based on the user’s geography or make some other modifications based on loyalty status, etc. So the question arose, “Is it possible to modify the body of an existing page with EdgeWorker data”?
The Answer
Fortunately, the answer is yes. The development team has created a helper library called find-replace-stream that, as its name suggests, allows you to search for a section of the response body that you want to replace with some other data. The replacement data can be generated by an EdgeWorker. The documentation, code library, and example for find-replace-stream is in the EdgeWorkers GitHub repository.
An Example
Let’s take a look at how I used find-replace-stream in a simple example on my homepage.
As you can see above, the EdgeWorker gets the user’s city and country from EdgeScape and injects it onto the homepage. Let’s take a look at how this was done.
First, onClientRequest() is used to set a property variable, USER_LOCATION, with the user’s city and country (61). I’ve also added the location to the cache key to prevent caching of the wrong location (63).
Next, responseProvider() is used to do the real work (36). This function essentially allows us to build our own origin, piecing responses together from multiple APIs or content origins. We first set three important variables:
- tosearchfor (37) – the text that we are going to replace. In this case, my original html has the word ‘Maryland’ hardcoded on the page.
- newtext (38) – the new text that will replace the original. We set this to the value of the property variable.
- howManyReplacements (39) – the number of times to execute the replacement on the stream.
Next we make an HTTP request to the origin homepage (www.duanedorch.com in this example) (41), which is then used by createResponse() to return the modified homepage. The magic happens in lines 46-48, where the find-replace-stream library is called to replace ‘Maryland’ with the user’s city and country.
The only other things needed are to
- Import the find-replace-stream library in the main.js (10)
- Include the find-replace-stream.js in your bundle
- % tar cvzf bundle.tgz main.js bundle.json find-replace-stream.js
Things To Consider
- Only text data. find-replace-stream is intended to be used on text streams. So please use match conditions in Property Manager that yield text-based response bodies only.
- Size of the response. The maximum response size HTTP sub-request is 5 MB. Be sure your response body doesn’t cause this limit to be exceeded.
- Execution duration of the subrequest. The maximum wall time per HTTP sub-request during the execution of responseProvider() is 1 second. This limit can easily be exceeded, causing the EdgeWorker to fail.
- Consider using failover techniques. My biggest issue with this EdgeWorker was the HTTP sub-request timed out about 40% of the time. Thanks to Josh Johnson for writing a great blog on using site failover in Property Manager to re-execute the EdgeWorker upon such failure.
Futures
Stay tuned for some nice related enhancements coming to EdgeWorkers, including:
- Enhanced sub-request queuing and wall time limits
- Buffered access to request body for responseProvider()
- Native streaming HTML parser that will extend the capabilities of the current find-replace-stream library.
Summary
A common customer use case will involve modifying the content of an existing page with data derived from an EdgeWorker. The find-replace-stream library simplifies this process. When used in conjunction with responseProvider(), we provide our customers with a powerful personalization capability.