DEV Community

Cover image for Transform web pages at the edge
Sue Smith for Fastly

Posted on

Transform web pages at the edge

You can now transform your web pages at the edge more easily with the Fastly HTML Rewriter in your JavaScript Compute apps. Ideal for customizations like personalization and A/B testing you want to perform at the network edge, the rewriter uses familiar DOM manipulation patterns. Let's run through how to switch up your page HTML with a Fastly Compute app in just a few steps.

Start a new project

In your terminal, create a new directory:

mkdir htmlrewrite && cd htmlrewrite
Enter fullscreen mode Exit fullscreen mode

Initialize a new Compute app:

npx @fastly/cli compute init
Enter fullscreen mode Exit fullscreen mode

Accept the defaults, choosing JavaScript and selecting the empty starter kit.

empty starter

Enter project metadata

In your new project, open the package.json and change the Compute JS SDK dependency as follows:

"@fastly/js-compute": "^3.35.1"
Enter fullscreen mode Exit fullscreen mode

Run npm install to get your environment ready.

In the fastly.toml file, add the details for the website you want to transform at the edge (changing the addresses to use your own or using the included demo site):

[local_server]

  [local_server.backends]

    [local_server.backends.website]
      override_host = "www.goldengirls.codes"
      url = "https://www.goldengirls.codes/"

[setup]

  [setup.backends]

    [setup.backends.website]
      address = "www.goldengirls.codes"
Enter fullscreen mode Exit fullscreen mode

Write your logic

In src/index.js we'll be writing our Compute logic to transform the HTML at the edge.

our js code

Start by importing a couple of dependencies:

import { getGeolocationForIpAddress } from "fastly:geolocation";
import { HTMLRewritingStream } from 'fastly:html-rewriter';
Enter fullscreen mode Exit fullscreen mode

We're going to build geolocation into our processing.

Inside the handleRequest function, add try catch blocks:

try {

  //rewriter processing here

} catch (error) {
  console.error(error);
  return new Response("Internal Server Error", { status: 500 });
}
Enter fullscreen mode Exit fullscreen mode

Grab geolocation info

Inside the try block, let's first get some info from the request:

let ip = new URL(event.request.url).searchParams.get("ip") || event.client.address;
let geo = getGeolocationForIpAddress(ip);
Enter fullscreen mode Exit fullscreen mode

We're going to use the time of day at the user location to tailor the display:

let displayTime = new Date().getHours();
let offset = geo.utc_offset;
displayTime += offset / 100;

let emoji =
  displayTime > 4 && displayTime < 12
    ? " πŸŒ‡"
    : displayTime >= 12 && displayTime < 18
    ? " πŸ™οΈ"
    : " πŸŒƒ";
Enter fullscreen mode Exit fullscreen mode

We'll insert this daft emoji into the page to personalize it to the user.

Specify rewrite behavior

Still inside the try block, use an HTMLRewritingStream to specify what you want your Compute app to do to the HTML from your origin website. In this example we append the emoji to the h1 element and replace every second image with a different one – change yours to suit your website!

let transformer = new HTMLRewritingStream()
  .onElement("h1", (e) => e.append(emoji) )
  .onElement("div.code:nth-child(even) img", (e) =>
    e.setAttribute("src", "https://shadypinesmiami.github.io/sophia.jpg"));
Enter fullscreen mode Exit fullscreen mode

Fetch the response from the website specified as a backend in your toml and carry out the transformation on it:

let backendResponse = (
  await fetch("https://www.goldengirls.codes/", { backend: "website" })
).body.pipeThrough(transformer);
Enter fullscreen mode Exit fullscreen mode

Finally, return the new response to the user:

return new Response(backendResponse, {
  status: 200,
  headers: new Headers({ "content-type": "text/html" }),
});
Enter fullscreen mode Exit fullscreen mode

Your script should now look something like this:

/// <reference types="@fastly/js-compute" />

import { getGeolocationForIpAddress } from "fastly:geolocation";
import { HTMLRewritingStream } from "fastly:html-rewriter";

addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));

async function handleRequest(event) {

  try {
    let ip =
      new URL(event.request.url).searchParams.get("ip") || event.client.address;
    let geo = getGeolocationForIpAddress(ip);

    let displayTime = new Date().getHours();
    let offset = geo.utc_offset;
    displayTime += offset / 100;

    let emoji =
      displayTime > 4 && displayTime < 12
        ? " πŸŒ‡"
        : displayTime >= 12 && displayTime < 18
        ? " πŸ™οΈ"
        : " πŸŒƒ";

    let transformer = new HTMLRewritingStream()
      .onElement("h1", (e) => e.append(emoji) )
      .onElement("div.code:nth-child(even) img", (e) =>
        e.setAttribute("src", "https://shadypinesmiami.github.io/sophia.jpg")
      );

    let backendResponse = (
      await fetch("https://www.goldengirls.codes/", { backend: "website" })
    ).body.pipeThrough(transformer);

    return new Response(backendResponse, {
      status: 200,
      headers: new Headers({ "content-type": "text/html" }),
    });
  } catch (error) {
    console.error(error);
    return new Response("Internal Server Error", { status: 500 });
  }
}
Enter fullscreen mode Exit fullscreen mode

Test your app

Time to run your app! In the terminal, enter the start command:

npm run start
Enter fullscreen mode Exit fullscreen mode

With any luck you'll see a local address to try the app at.

test site

You might notice that the time of day emoji doesn't actually match the time of day at your location.. πŸ€” That's because your app is just running locally on your computer – you'll need to deploy it to the edge to see that functionality working reliably.

Deploy your app

Everything working the way you want it to? Great, time to deploy to Fastly.

Sign up for a Fastly account if you haven't already.

Grab an API token from your account:

  • Go to Account > API tokens > Personal tokens
  • Click Create Token:
    • Name: anything you like
    • Type: Automation
    • Role: Engineer
    • Scope: Global (deselect the Read-only access box)
    • Access: All services
    • Expiration: Never expire
  • Copy the token value and save it on your computer

In your terminal, create a Fastly profile, entering the API token you copied from your account:

fastly profile create 
Enter fullscreen mode Exit fullscreen mode

Now you can go ahead and enter the deploy command:

npm run deploy
Enter fullscreen mode Exit fullscreen mode

Accept the defaults and let the CLI create a new service for you, confirming the backend details from your toml. Once it's deployed, the output will include the address to try your new app at, which will end edgecompute.app:

deployed app details

Open your app in a browser to see the correct time of day indicator:

app in browser

It is indeed afternoon where I am!

Keep going

Add more processing to your Compute app! You can use the HTML Rewriter to make all sorts of changes through DOM manipulation, like adding attributes to your elements to change the site behavior.

If you get stuck please feel free to post on the Fastly Community Forum. πŸ›Ÿ

Top comments (0)