<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Katsuyuki Omuro</title>
    <description>The latest articles on Forem by Katsuyuki Omuro (@harmony7).</description>
    <link>https://forem.com/harmony7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F926850%2F80810020-949f-4b91-9b8f-8e6437ab3b6a.jpeg</url>
      <title>Forem: Katsuyuki Omuro</title>
      <link>https://forem.com/harmony7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/harmony7"/>
    <language>en</language>
    <item>
      <title>Start your next Fastly Compute JavaScript application with npm init</title>
      <dc:creator>Katsuyuki Omuro</dc:creator>
      <pubDate>Thu, 19 Dec 2024 08:03:51 +0000</pubDate>
      <link>https://forem.com/fastly/start-your-next-fastly-compute-javascript-application-with-npm-init-2h6</link>
      <guid>https://forem.com/fastly/start-your-next-fastly-compute-javascript-application-with-npm-init-2h6</guid>
      <description>&lt;p&gt;The &lt;a href="https://docs.npmjs.com/cli/v10/commands/npm-init" rel="noopener noreferrer"&gt;npm init (or npm create) command&lt;/a&gt; is a popular, platform-standard method of initializing a JavaScript application based on a template. If you’re working with &lt;a href="https://www.fastly.com/documentation/guides/compute/javascript/" rel="noopener noreferrer"&gt;JavaScript&lt;/a&gt; in Fastly Compute, we have exciting news for you – the npm init script for Fastly Compute gives JavaScript authors an additional way to start their Fastly Compute applications using common and standard tools.&lt;/p&gt;




&lt;p&gt;Just over a year ago, &lt;a href="https://www.fastly.com/documentation/reference/changes/2022/12/javascript-sdk-1.0.0/" rel="noopener noreferrer"&gt;JavaScript became a generally available language&lt;/a&gt; for development on Fastly Compute. With this, we have been able to bring the convenience of JavaScript to the power of the edge to developers around the world. JavaScript is a very popular language on the platform, and we want to continue to give it access to as many users as we can.&lt;/p&gt;

&lt;p&gt;In October we announced that we have &lt;a href="https://www.fastly.com/blog/fastly-cli-on-npm-now-at-your-javascript-fingertips" rel="noopener noreferrer"&gt;brought the Fastly CLI to npm&lt;/a&gt;. Today we have additional good news for JavaScript users: we now have an &lt;a href="https://www.npmjs.com/package/@fastly/create-compute" rel="noopener noreferrer"&gt;official npm init script&lt;/a&gt; for Fastly Compute!&lt;/p&gt;

&lt;p&gt;In case you’re not familiar with &lt;code&gt;npm init&lt;/code&gt;, it is &lt;a href="https://docs.npmjs.com/cli/v10/commands/npm-init" rel="noopener noreferrer"&gt;a method of initializing a new JavaScript project&lt;/a&gt; based on an initialization script, built into the npm command. Fastly now provides an initializer for npm init, so it’s possible to create a new Fastly Compute project in JavaScript like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init @fastly/compute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After typing this command, you will be presented with interactive prompts to select a directory, language, and starter kit, to create your Fastly Compute application in JavaScript or TypeScript.&lt;/p&gt;

&lt;p&gt;The initializer called by &lt;code&gt;npm init @fastly/compute&lt;/code&gt; is a Node.js program distributed via npm under the name &lt;code&gt;@fastly/create-compute&lt;/code&gt;. It asks questions relevant to your new JavaScript application, and then under the hood, calls the Fastly CLI to initialize your new application. No global installation of the CLI is required, however, since it &lt;a href="https://www.fastly.com/blog/fastly-cli-on-npm-now-at-your-javascript-fingertips" rel="noopener noreferrer"&gt;declares the dependency as an npm package&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  You’re already working with JavaScript, stay in the JavaScript mindset
&lt;/h2&gt;

&lt;p&gt;Traditionally, the procedure to create a new Fastly Compute application has been to obtain the &lt;a href="https://www.fastly.com/documentation/reference/tools/cli/" rel="noopener noreferrer"&gt;Fastly CLI&lt;/a&gt; and type the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastly compute init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This meant that it was necessary for you to install a global instance of the Fastly CLI just to create a new application for Fastly Compute. You also needed it to run and publish the application. Because this was true even if you just wanted to experiment with the platform, we felt that this was sometimes getting in the way of developers wanting to try out the platform. Additionally, the Fastly CLI would prompt the selection of a programming language when initializing a new project, a potential friction point that could even take you “out of the zone” when you are already thinking in terms of JavaScript.&lt;/p&gt;

&lt;p&gt;As always, we looked for ways to enable developers to get the job done with one fewer tool needed to be installed, with one fewer click, with one fewer dependency. Is there a way to enable JavaScript development to be even simpler? The solution was to use the standard, platform-defined npm init mechanism to allow users to initialize new applications for Fastly Compute in JavaScript.&lt;/p&gt;

&lt;p&gt;Most JavaScript developers are already familiar with npm, since it's the tool used to install dependency packages into your application. Being able to use the npm tool to initialize a new application enables developers to stay with tools within the JavaScript ecosystem from the start of the lifetime of their application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batteries included!
&lt;/h2&gt;

&lt;p&gt;Recently, Fastly’s JavaScript and TypeScript starter kits have been updated to &lt;a href="https://www.fastly.com/blog/fastly-cli-on-npm-now-at-your-javascript-fingertips" rel="noopener noreferrer"&gt;install the Fastly CLI as a dependency package&lt;/a&gt;. This means that for most tasks you no longer need to work with the Fastly CLI directly, to run or publish your application. Instead, you are able to work with it through npm scripts.&lt;/p&gt;

&lt;p&gt;Once your application has been initialized, use the following command to start your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run &lt;code&gt;fastly compute serve&lt;/code&gt;, which will build your application and start it under the development environment.&lt;/p&gt;

&lt;p&gt;When you’re ready to go live, type the following command to deploy to production (for most starter kits):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run &lt;code&gt;fastly compute publish&lt;/code&gt;, which will build your application and publish it to your Fastly account.&lt;/p&gt;

&lt;p&gt;Of course, if you would like to call the Fastly CLI directly, or in the case you need to perform additional functions with the Fastly CLI, such as add backends or work with edge storage, you can invoke it from within your application directory using npx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx fastly &amp;lt;command&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  JavaScript + edge computing = ❤️
&lt;/h2&gt;

&lt;p&gt;At Fastly, we enjoy providing you with the power to run more code at the edge and develop for it with the tools you know and love. One fewer thing to get in the way is one more victory for us in that direction. Our initializer enabling you to create JavaScript applications for Fastly Compute using npm is another item on this journey.&lt;/p&gt;

&lt;p&gt;We love to hear about it when our users get the most out of these tools. Get your &lt;a href="https://www.fastly.com/signup/?tier=free" rel="noopener noreferrer"&gt;free Fastly developer account&lt;/a&gt;, join us on the &lt;a href="https://community.fastly.com/?_gl=1*jqz5dj*_gcl_au*MTI5OTQ3MDg2Ni4xNzMxOTUwNTI1*_ga*MTgxNzM5MzY2Ny4xNzM0NDg3OTQ4*_ga_ETDRC9QJ6S*MTczNDU5Mjg3OS40LjEuMTczNDU5NTA4NC4yNC4wLjA." rel="noopener noreferrer"&gt;Fastly community forum&lt;/a&gt;, and let us know what you’ve been building!&lt;/p&gt;

</description>
      <category>fastly</category>
      <category>compute</category>
      <category>javascript</category>
      <category>npm</category>
    </item>
    <item>
      <title>Fastly CLI on npm: now at your JavaScript fingertips</title>
      <dc:creator>Katsuyuki Omuro</dc:creator>
      <pubDate>Thu, 19 Dec 2024 07:57:10 +0000</pubDate>
      <link>https://forem.com/fastly/fastly-cli-on-npm-now-at-your-javascript-fingertips-19ce</link>
      <guid>https://forem.com/fastly/fastly-cli-on-npm-now-at-your-javascript-fingertips-19ce</guid>
      <description>&lt;p&gt;The &lt;a href="https://www.fastly.com/documentation/reference/cli/" rel="noopener noreferrer"&gt;Fastly CLI&lt;/a&gt; is the recommended tool provided by Fastly for interacting with the &lt;a href="https://www.fastly.com/documentation/reference/api/" rel="noopener noreferrer"&gt;Fastly API&lt;/a&gt; from the command line. It’s &lt;a href="https://github.com/fastly/cli" rel="noopener noreferrer"&gt;an open-source tool&lt;/a&gt; used by developers and in continuous integration pipelines to perform various actions on behalf of a Fastly account, including creating services, managing backends and domains, and deploying Compute packages. If you’re working with &lt;a href="https://www.fastly.com/documentation/guides/compute/javascript/" rel="noopener noreferrer"&gt;Fastly Compute in JavaScript&lt;/a&gt;, we have exciting news that brings the Fastly CLI closer to you – the Fastly CLI is now &lt;a href="https://www.npmjs.com/package/@fastly/cli" rel="noopener noreferrer"&gt;available as a package on npm&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;It’s been an exciting &lt;a href="https://www.fastly.com/press/press-releases/fastly-launches-new-era-of-highly-secure-serverless-javascript-with-zero/" rel="noopener noreferrer"&gt;three years since we announced JavaScript support&lt;/a&gt; in the Fastly Compute edge platform, and almost two years since we released &lt;a href="https://www.fastly.com/documentation/reference/changes/2022/12/javascript-sdk-1.0.0/" rel="noopener noreferrer"&gt;v1.0 of the JavaScript SDK&lt;/a&gt;. It’s a very popular language on the platform and also happens to be my personal favorite. Developing for Fastly Compute using JavaScript enables the creation of edge applications in a quick and fun way, and we want to extend access to it to as many users as we can.&lt;/p&gt;

&lt;p&gt;As a developers-first company, there is one thing we always have on our minds: to remove as many obstacles as possible from getting in the way of actual development. That is, how can we enable developers to get the job done with one fewer click, with one fewer dependency, with one fewer tool needed to be installed? Is there a way to make JavaScript development on Compute simpler? That’s what we set out to tackle this time.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.fastly.com/documentation/reference/cli/" rel="noopener noreferrer"&gt;Fastly CLI&lt;/a&gt; is an open-source tool used to perform actions with your Fastly account. Since it’s used for running and publishing Compute applications, it is one of the requirements for developing for Fastly Compute in JavaScript, even if it’s just to try it out locally. This has traditionally meant a trip to the &lt;a href="https://www.fastly.com/documentation/reference/cli/" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; to obtain a prebuilt package, or installing it through Homebrew on macOS. While these are steps that are generally familiar to developers, we wanted to lower the barrier as much as we can for getting your feet wet in Compute application development.&lt;/p&gt;

&lt;p&gt;That’s why starting with version 10.14.0, we’ve decided to publish the Fastly CLI &lt;a href="https://www.npmjs.com/package/@fastly/cli" rel="noopener noreferrer"&gt;on npmjs.org&lt;/a&gt; as an additional avenue of distribution. If you’re a JavaScript developer in 2024, chances are you’re familiar with &lt;a href="https://npmjs.org/" rel="noopener noreferrer"&gt;npmjs.org&lt;/a&gt; as the de facto package repository from where you install packages into your application, whether you use &lt;a href="https://yarnpkg.com/" rel="noopener noreferrer"&gt;yarn&lt;/a&gt;, &lt;a href="https://pnpm.io/" rel="noopener noreferrer"&gt;pnpm&lt;/a&gt;, or the &lt;a href="https://docs.npmjs.com/cli/v10/commands/npm" rel="noopener noreferrer"&gt;trusty old npm&lt;/a&gt; as your interface. This means npmjs.org is available by default to everyone, making it a great way for us to put this essential tool into our users’ hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the Fastly CLI without needing to install it
&lt;/h2&gt;

&lt;p&gt;One handy feature of npm is &lt;a href="https://docs.npmjs.com/cli/v10/commands/npx" rel="noopener noreferrer"&gt;npx&lt;/a&gt;, which allows you to run commands from npm packages without being required to install them to a project. So long as Node.js and npm are available in your environment, you can now directly invoke the Fastly CLI as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @fastly/cli 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time you do this, you will be prompted by npm to fetch the package; this will add it to your environment’s npm cache, and it will be available immediately in the future.&lt;/p&gt;

&lt;p&gt;Since the Fastly CLI is always called with additional parameters, you just specify them as normal as parameters following the command. For example, to &lt;a href="https://www.fastly.com/documentation/reference/cli/service/list/" rel="noopener noreferrer"&gt;list the services in your Fastly account&lt;/a&gt;, you can type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @fastly/cli service list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fastly CLI as a dependency package
&lt;/h2&gt;

&lt;p&gt;Of course, availability on npmjs.org means you can now add the Fastly CLI to your Compute JavaScript application as a standard dependency:&lt;/p&gt;

&lt;p&gt;npm install @fastly/cli&lt;br&gt;
Alternatively, you can add it to your project’s package.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@fastly/cli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^10.14.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install the dependencies for the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs &lt;code&gt;@fastly/cli&lt;/code&gt; as a dependency into your project’s &lt;code&gt;node_modules&lt;/code&gt; directory. It becomes available as a program called &lt;code&gt;fastly&lt;/code&gt; under the &lt;code&gt;node_modules/.bin&lt;/code&gt; subdirectory, so you will be able to invoke it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx fastly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, to &lt;a href="https://www.fastly.com/documentation/guides/compute/testing/#running-a-local-testing-server" rel="noopener noreferrer"&gt;start your application in the local development environment&lt;/a&gt;, type the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx fastly compute serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, any references to &lt;code&gt;fastly&lt;/code&gt; in the scripts section of the &lt;code&gt;package.json&lt;/code&gt; file will now find this locally installed version of &lt;code&gt;@fastly/cli&lt;/code&gt; instead of requiring a global installation of the Fastly CLI to be available on the system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"js-compute-runtime src/index.js bin/main.wasm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fastly compute serve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fastly compute publish"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  No global installation necessary
&lt;/h2&gt;

&lt;p&gt;Traditionally, it has been necessary for every developer working with Fastly Compute to install a globally available instance of the Fastly CLI to develop applications and publish them to their Fastly account, even when just getting started with the platform to experiment. By making the Fastly CLI available as a standard dependency to a JavaScript application, first-time users of Fastly Compute can experience a Compute application by simply cloning its application repository, installing its dependencies as normal, and typing npm start. In fact, we have updated all our JavaScript and TypeScript starter kits to take this approach, so that they can be experienced by more users, even if they have no prior experience with the Compute platform.&lt;/p&gt;

&lt;p&gt;It’s also great when you work in a team. You, as well as other developers who work with your application, can obtain your application’s code, use the standard procedure to install its dependencies, and get right to work, batteries included.&lt;/p&gt;

&lt;p&gt;This convenience extends to your continuous integration (CI) pipeline as well, enabling your application to have reliable access to the Fastly CLI as part of its build or test process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use new features of the CLI with confidence
&lt;/h2&gt;

&lt;p&gt;The Fastly CLI is &lt;a href="https://www.fastly.com/documentation/reference/changes/cli/" rel="noopener noreferrer"&gt;under active development&lt;/a&gt;, constantly receiving new features and improvements.&lt;/p&gt;

&lt;p&gt;Specifying the Fastly CLI as a standard dependency of your package enables you to prescribe its version using &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;semantic versioning&lt;/a&gt;. This allows your package to safely depend on features of the CLI that have been added recently or whose behavior may have changed, without having to worry about whether other developers who work with your application have a compatible version of the CLI installed on their environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calling the Fastly CLI from Node.js code
&lt;/h2&gt;

&lt;p&gt;If you are writing code intended to run under Node.js that calls out to the Fastly CLI, such as in a tool or utility program, this package provides a very straightforward and reliable way to do so.&lt;/p&gt;

&lt;p&gt;The default export of &lt;code&gt;@fastly/cli&lt;/code&gt; resolves to a string value representing the full path of the executable of the Fastly CLI, appropriate for the operating system, architecture, and how it has been installed. It can be used directly with functions such as &lt;code&gt;spawnSync&lt;/code&gt; in Node.js. Because this is available in this way, you don’t need to have the user of your package obtain a global installation of the Fastly CLI before running your program.&lt;/p&gt;

&lt;p&gt;The following example Node.js program executes the &lt;a href="https://www.fastly.com/documentation/reference/cli/version/" rel="noopener noreferrer"&gt;fastly version&lt;/a&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spawnSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fastly&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@fastly/cli&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spawnSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fastly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Even install the Fastly CLI globally
&lt;/h2&gt;

&lt;p&gt;If you ever do need a global installation of the Fastly CLI, this package also acts as one way of obtaining it for global use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @fastly/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it’s installed, invoke it as you would have done traditionally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, this is no different than invoking the copy of the CLI cached in the environment using &lt;code&gt;npx @fastly/cli&lt;/code&gt;. However, this procedure makes the command available as &lt;code&gt;fastly&lt;/code&gt; on the system path, enabling it to fit with other tools that expect to find it there. The end result is effectively identical to if you had used any of the traditional methods of global installation, but this installation process is a convenient alternative, as npm is widely available on many machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing edge computing to more developers
&lt;/h2&gt;

&lt;p&gt;At Fastly, we strive to provide the tools that give you the power to run more code at the edge and develop for it with the tools you know and love. We want nothing to get in the way of this. As &lt;a href="https://survey.stackoverflow.co/2024/technology#most-popular-technologies" rel="noopener noreferrer"&gt;JavaScript is the most popular language on the planet&lt;/a&gt;, the release of the Fastly CLI on npm moves the needle further along this mission.&lt;/p&gt;

&lt;p&gt;We love to hear about it when our users get the most out of these tools. Get your &lt;a href="https://www.fastly.com/signup/?tier=free" rel="noopener noreferrer"&gt;free Fastly developer account&lt;/a&gt;, join us on the &lt;a href="https://community.fastly.com/?_gl=1*iuh7ro*_gcl_au*MTI5OTQ3MDg2Ni4xNzMxOTUwNTI1*_ga*MTgxNzM5MzY2Ny4xNzM0NDg3OTQ4*_ga_ETDRC9QJ6S*MTczNDU5Mjg3OS40LjEuMTczNDU5NDY3Mi41OS4wLjA." rel="noopener noreferrer"&gt;Fastly community forum&lt;/a&gt;, and let us know what you’ve been building!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>npm</category>
      <category>fastly</category>
    </item>
    <item>
      <title>A modular Edge Side Includes component for JavaScript Compute</title>
      <dc:creator>Katsuyuki Omuro</dc:creator>
      <pubDate>Thu, 19 Dec 2024 07:41:28 +0000</pubDate>
      <link>https://forem.com/fastly/a-modular-edge-side-includes-component-for-javascript-compute-563d</link>
      <guid>https://forem.com/fastly/a-modular-edge-side-includes-component-for-javascript-compute-563d</guid>
      <description>&lt;p&gt;In the summer of 2022, my teammate Kailan worked on a &lt;a href="https://docs.rs/esi/latest/esi" rel="noopener noreferrer"&gt;Rust crate&lt;/a&gt; for &lt;a href="https://developer.fastly.com/learning/compute/" rel="noopener noreferrer"&gt;Fastly Compute&lt;/a&gt;, implementing a subset of the &lt;a href="https://www.w3.org/TR/esi-lang/" rel="noopener noreferrer"&gt;Edge Side Includes (ESI) templating language&lt;/a&gt;, and &lt;a href="https://www.fastly.com/blog/esi-and-the-story-of-libraries-built-for-the-edge" rel="noopener noreferrer"&gt;published a blog post&lt;/a&gt; about it. This article was significant not just because we released a useful library, but because it was a brilliant illustration of what Compute can bring us: a programmable edge with modular functionality. And now that JavaScript has been generally available on Compute for more than a year, it was time we had a similar solution for our JavaScript users. Fastly’s ESI library for JavaScript, &lt;a href="https://www.npmjs.com/package/@fastly/esi" rel="noopener noreferrer"&gt;now available on npm&lt;/a&gt;, allows you to add powerful ESI processing to your application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Programmability and Modularity
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.fastly.com/blog/using-esi-part-1-simple-edge-side-include" rel="noopener noreferrer"&gt;For almost a decade&lt;/a&gt;, Fastly’s CDN has had &lt;a href="https://developer.fastly.com/reference/vcl/variables/esi/" rel="noopener noreferrer"&gt;support for Edge Side Includes (ESI)&lt;/a&gt;, a templating language that works by interpreting special tags in your HTML source. It revolves around the tag &lt;code&gt;&amp;lt;esi:include&amp;gt;&lt;/code&gt;, which instructs the edge server to fetch another document and inline it into the stream.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./header.html"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
Content
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;header.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;Welcome to the web site&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;output&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;Welcome to the web site&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
Content
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When &lt;a href="https://developer.fastly.com/learning/compute/" rel="noopener noreferrer"&gt;Compute&lt;/a&gt; entered the scene, the edge landscape changed in two main ways: programmability and modularity.&lt;/p&gt;

&lt;p&gt;Soon after our platform support for Rust had stabilized, we published a crate for Rust that implemented ESI, and added programmability. You could now configure, using code, how to build additional backend requests, and how to handle response fragments. You could even perform ESI processing on documents that don’t originate from the backend server. This programmability differentiated it from the &lt;a href="https://www.fastly.com/documentation/reference/vcl/statements/esi/" rel="noopener noreferrer"&gt;ESI support we have in VCL&lt;/a&gt;, which was limited to the fixed set of features we offered.&lt;/p&gt;

&lt;p&gt;At the same time, this approach was highly modular, giving the programmer a choice to perform this ESI processing on a per-request basis, and the option to combine the processing with other modules that work with compatible data types, and apply them in any order and/or logical condition specified.&lt;/p&gt;
&lt;h2&gt;
  
  
  Next target: JavaScript
&lt;/h2&gt;

&lt;p&gt;Similar to our Rust release, we wanted this JavaScript library to be programmable. Fastly’s JavaScript support has always embraced the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;Fetch API&lt;/a&gt; and its companion &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Streams_API" rel="noopener noreferrer"&gt;Streams API&lt;/a&gt;. One useful feature of the Streams API is the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/TransformStream" rel="noopener noreferrer"&gt;TransformStream&lt;/a&gt; interface, allowing for data to be “piped” through an object in order to apply a transformation—perfect for ESI. By implementing the ESI processor as an implementation of TransformStream, we were able to fit it right into a Fastly Compute Application written in JavaScript.&lt;/p&gt;

&lt;p&gt;Here's how you can stream through it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transformedBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipeThrough&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;esiTransformStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;transformedBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The transformation, which we call the EsiTransformStream, is applied to the stream directly, alleviating memory and performance concerns. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is no need to buffer the entire upstream response before applying the transformation.&lt;/li&gt;
&lt;li&gt;The transformer consumes the upstream response as quickly as it can, and processes ESI tags as they show up in the stream. As the transformer finishes processing each ESI tag, the results are released immediately downstream, so we are able to hold the minimal possible buffer. This allows the client to receive the first byte of the streamed result as it becomes ready, without having to wait for it to be processed in entirety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition, this design is modular, making the EsiTransformStream just another tool you can use alongside other things. For example, you might have other transformations you want to apply to responses, like compression, and a response can be piped through any number of these transform streams, since it's a completely standard interface.  If you wanted to, you could even conditionally enable ESI for just certain requests or responses, such as by request headers, paths, or response content type.&lt;/p&gt;

&lt;p&gt;Here's how you instantiate EsiTransformStream:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;esiTransformStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EsiTransformStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;origin_0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The constructor takes a URL and a Headers object, and optionally takes some options as a third parameter. As described earlier, the main functionality of ESI is to download additional templates, for inclusion into the resulting stream. Encountering an &lt;code&gt;&amp;lt;esi:include&amp;gt;&lt;/code&gt; tag uses &lt;a href="https://js-compute-reference-docs.edgecompute.app/docs/globals/fetch" rel="noopener noreferrer"&gt;fetch&lt;/a&gt; as the underlying mechanism to retrieve a template, and the main purpose of these parameters is to configure those fetch calls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The URL is used to resolve relative paths in the src of &lt;code&gt;&amp;lt;esi:include&amp;gt;&lt;/code&gt; tags.&lt;/li&gt;
&lt;li&gt;The Headers are used when making the additional requests to fetch the templates.&lt;/li&gt;
&lt;li&gt;The optional configuration object can be used to override the behavior of the fetch that is made, and to apply other custom behavior, such as how to process the fetched template, and custom error handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the simplest case, you’ll use just the fetch value of the configuration object. If you don’t provide it, then it uses the global fetch function instead, but in Compute you’ll need it to &lt;a href="https://developer.fastly.com/learning/compute/javascript/#communicating-with-backend-servers-and-the-fastly-cache" rel="noopener noreferrer"&gt;specify a backend for the fetch&lt;/a&gt; to use when including a template (unless you’re using the &lt;a href="https://www.fastly.com/blog/dynamic-backends-takes-the-pain-out-of-backend-configuration-management" rel="noopener noreferrer"&gt;dynamic backends feature&lt;/a&gt;). The example snippet above assigns the backend named &lt;code&gt;origin_0&lt;/code&gt; before calling the global fetch.&lt;/p&gt;

&lt;p&gt;That’s it! With this simple setup you can have a backend serving ESI tags and a Compute application processing them. Here’s a full example that you can run:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://fiddle.fastly.dev/fiddle/9e82ef57" rel="noopener noreferrer"&gt;
      fiddle.fastly.dev
    &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Support for ESI features
&lt;/h2&gt;

&lt;p&gt;This implementation offers more ESI features than others we have made available in the past.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error handling
&lt;/h3&gt;

&lt;p&gt;Sometimes, a file referenced by an &lt;code&gt;&amp;lt;esi:include&amp;gt;&lt;/code&gt; tag may fail to fetch due to it not existing or causing a server error. It’s possible to ignore the error in these cases by adding the attribute &lt;code&gt;onerror="continue"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/templates/header.html"&lt;/span&gt; &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"continue"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If &lt;code&gt;/templates/header.html&lt;/code&gt; causes an error, the ESI processor silently ignores the error and replaces the entire &lt;code&gt;&amp;lt;esi:include&amp;gt;&lt;/code&gt; tag with an empty string.&lt;/p&gt;

&lt;p&gt;It’s also possible to use more structured error handling by employing an &lt;code&gt;&amp;lt;esi:try&amp;gt;&lt;/code&gt; block, which looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;esi:try&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;esi:attempt&amp;gt;&lt;/span&gt;
    Main header
    &lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/templates/header.html"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/esi:attempt&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;esi:except&amp;gt;&lt;/span&gt;
    Alternative header
  &lt;span class="nt"&gt;&amp;lt;/esi:except&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/esi:try&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The ESI processor first executes the contents of &lt;code&gt;&amp;lt;esi:attempt&amp;gt;&lt;/code&gt;. If an esi:include tag causes an error, then the ESI processor executes the contents of &lt;code&gt;&amp;lt;esi:except&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s important to note that the entire &lt;code&gt;&amp;lt;esi:try&lt;/code&gt;&amp;gt; block is replaced by the entirety of the &lt;code&gt;&amp;lt;esi:attempt&amp;gt;&lt;/code&gt; block if it succeeds or the &lt;code&gt;&amp;lt;esi:except&amp;gt;&lt;/code&gt; if there is an error. In the above example, if &lt;code&gt;/templates/header.html&lt;/code&gt; causes an error, then this also causes the text &lt;em&gt;"Main header"&lt;/em&gt; not to appear in the output; only the text &lt;em&gt;"Alternative header"&lt;/em&gt; is included. See the &lt;a href="https://www.w3.org/TR/esi-lang/" rel="noopener noreferrer"&gt;ESI language specification&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3&gt;
  
  
  Conditionals
&lt;/h3&gt;

&lt;p&gt;ESI also allows conditional execution, by performing runtime checks on variables. The following is an example of such a check:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;esi:choose&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;esi:when&lt;/span&gt; &lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;"$(HTTP_COOKIE{group})=='admin'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/header/admin.html"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/esi:when&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;esi:when&lt;/span&gt; &lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;"$(HTTP_COOKIE{group})=='basic'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/header/basic.html"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/esi:when&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;esi:otherwise&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/header/anonymous.html"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/esi:otherwise&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/esi:choose&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When the processor encounters an &lt;code&gt;&amp;lt;esi:choose&amp;gt;&lt;/code&gt; block, it runs through the &lt;code&gt;&amp;lt;esi:when&amp;gt;&lt;/code&gt; blocks, checking the expressions set in their test attributes. The processor executes the first esi:when block where the expression evaluates to true. If none of the expressions evaluates to true, then it will optionally execute the esi:otherwise block if it’s provided. The entire &lt;code&gt;&amp;lt;esi:choose&amp;gt;&lt;/code&gt; block is replaced by the entirety of whichever &lt;code&gt;&amp;lt;esi:when&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;esi:otherwise&amp;gt;&lt;/code&gt; block that executes.&lt;/p&gt;

&lt;p&gt;The processor makes available a limited set of variables, which are based primarily on request cookies. In the above example, an HTTP cookie by the name “group” is checked for its value. Our implementation is based on the &lt;a href="https://www.w3.org/TR/esi-lang/" rel="noopener noreferrer"&gt;ESI language specification&lt;/a&gt;; refer to it for more details.&lt;/p&gt;
&lt;h3&gt;
  
  
  List of supported tags and features
&lt;/h3&gt;

&lt;p&gt;This implementation supports the following tags of the &lt;a href="https://www.w3.org/TR/esi-lang/" rel="noopener noreferrer"&gt;ESI language specification&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;esi:include&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;esi:comment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;esi:remove&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;esi:try&lt;/code&gt; / &lt;code&gt;esi:attempt&lt;/code&gt; / &lt;code&gt;esi:except&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;esi:choose&lt;/code&gt; / &lt;code&gt;esi:when&lt;/code&gt; / &lt;code&gt;esi:otherwise&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;esi:vars&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;esi:inline&amp;gt;&lt;/code&gt; tag is defined in the spec to be optional, and is not included in this implementation.&lt;/p&gt;

&lt;p&gt;ESI Variables are supported in the attributes of ESI tags, and ESI Expressions are supported in the &lt;code&gt;test&lt;/code&gt; attribute of &lt;code&gt;&amp;lt;esi:when&amp;gt;&lt;/code&gt;. Additionally, the &lt;code&gt;&amp;lt;!--esi ...--&amp;gt;&lt;/code&gt; comment is supported.&lt;/p&gt;
&lt;h2&gt;
  
  
  Custom behavior means endless possibilities
&lt;/h2&gt;

&lt;p&gt;While the feature set is enough to get thrilled about, the truly exciting part of being programmable is that even more things are possible. Bringing in templates is the main use of ESI, but that’s by no means all that it can do. Here’s an example.&lt;/p&gt;

&lt;p&gt;Consider you have a timestamp marked up in your document that you want represented as a relative date when it’s displayed, such as “2 days ago”. There are many ways of doing this, but to have the best performance and memory implications, it would be great to perform a find/replace in streams. Programming this ESI library can actually be used as a good option for doing this.&lt;/p&gt;

&lt;p&gt;We can define timestamps to be encoded in your backend document using a specially-crafted ESI tag in a format such as the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/__since/&amp;lt;unix-timestamp&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For example, this snippet can represent midnight on January 1, 2024, Pacific time:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;This document was written &lt;span class="nt"&gt;&amp;lt;esi:include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/__since/1704034800"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, set up the &lt;code&gt;EsiTransformStream&lt;/code&gt; to serve a synthetic replacement document whenever it sees that URL pattern:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;esiTransformStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EsiTransformStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// If custom URL pattern, return synthetic Response&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sinceMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;__since&lt;/span&gt;&lt;span class="se"&gt;\/(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sinceMatch&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestampString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinceMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestampString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;toRelativeTimeSince&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// This function builds a relative date&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// For all other esi:include tags, add the backend and call the global fetch&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;origin_0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, when the processor encounters the example snippet above, it will emit a result similar to the following (depending on how many days into the future you run it):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;This document was written 22d ago.&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because the backend document is &lt;a href="https://developer.fastly.com/learning/concepts/edge-state/cache" rel="noopener noreferrer"&gt;cacheable by Fastly&lt;/a&gt;, future requests can benefit from a cache HIT, while the processing will continue to display the updated relative time.&lt;/p&gt;

&lt;p&gt;For a live example of this, view the following fiddle:&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://fiddle.fastly.dev/fiddle/64f7ff0c" rel="noopener noreferrer"&gt;
      fiddle.fastly.dev
    &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Take it for a spin!
&lt;/h2&gt;

&lt;p&gt;@fastly/esi is now &lt;a href="https://www.npmjs.com/package/@fastly/esi" rel="noopener noreferrer"&gt;available on npm&lt;/a&gt;, ready to be added to any JavaScript program. Use it at the edge in your Fastly Compute programs, of course, but in fact, it even works outside of Compute, so long as your environment supports the fetch API. The full source code is &lt;a href="https://github.com/fastly/compute-js-esi/tree/main" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Get started by cloning either of the Fiddles above and testing them out with your own origins, even before you have created a Fastly account. When you’re ready to publish your service on our global network, you can sign up for a &lt;a href="https://www.fastly.com/signup/" rel="noopener noreferrer"&gt;free trial of Compute&lt;/a&gt; and then get started right away with the &lt;a href="https://www.npmjs.com/package/@fastly/esi" rel="noopener noreferrer"&gt;ESI library on npm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With Compute, the edge is programmable and modular – choose and combine the solutions that work best for you, or even build your own. We aren’t the only ones who can provide modules like this for Compute. Anyone can contribute to the ecosystem and take from it. And, as always, &lt;a href="https://community.fastly.com" rel="noopener noreferrer"&gt;meet us at the Fastly community forum&lt;/a&gt; and let us know what you’ve been building!&lt;/p&gt;

</description>
      <category>fastly</category>
      <category>javascript</category>
      <category>edge</category>
      <category>esi</category>
    </item>
    <item>
      <title>Test your Compute apps end-to-end with JavaScript</title>
      <dc:creator>Katsuyuki Omuro</dc:creator>
      <pubDate>Mon, 29 Jan 2024 02:45:27 +0000</pubDate>
      <link>https://forem.com/fastly/test-your-compute-apps-end-to-end-with-javascript-3nko</link>
      <guid>https://forem.com/fastly/test-your-compute-apps-end-to-end-with-javascript-3nko</guid>
      <description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Test_automation"&gt;Automated testing&lt;/a&gt; is a vital part of modern application development, and applications built for the edge on &lt;a href="https://developer.fastly.com/learning/compute/"&gt;Fastly’s Compute platform&lt;/a&gt; are no exception. Today, developers can run tests for their Compute applications with custom shell scripts, but we wanted a more convenient and natural way for this to be done. Our new Compute Application Testing library allows you to use JavaScript to write your tests.&lt;/p&gt;

&lt;p&gt;This new library makes writing a new test as easy as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/user.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns 200 status and valid username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/user.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\w&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this post, we'll compare testing via shell script and the new Compute Application Testing library so you can choose the best way to test your code, regardless of the programming language used to write the Compute application.&lt;/p&gt;




&lt;p&gt;Automated testing is a technique used in the software development process to prove that code works as it’s intended, and to ensure that changes made to the code will not break existing functionality. When combined with strategies such as continuous delivery, it helps developers build and deploy reliable software, enabling a team to always have a shippable, high-quality product ready.&lt;/p&gt;

&lt;p&gt;There are a few types of testing: "Unit" testing refers to testing the smallest building blocks of your software in isolation, often at the function, class, or API level. "Integration" tests check the interactions between these various units that make up the software. Finally, "end-to-end" testing refers to a type of test that is applied against an entire application, checking that specified inputs produce an expected output.&lt;/p&gt;

&lt;p&gt;Testing at all of these levels is important. In this post, we cover end-to-end testing, which is one effective way to ensure that your Fastly Compute application continues to produce an expected set of outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can we test Compute applications end-to-end?
&lt;/h2&gt;

&lt;p&gt;Fastly Compute is a platform that executes &lt;a href="https://webassembly.org/"&gt;WebAssembly&lt;/a&gt; modules across &lt;a href="https://docs.fastly.com/en/guides/using-fastlys-global-pop-network"&gt;our global network of edge servers&lt;/a&gt;, compiled from a language of your choice, such as &lt;a href="https://developer.fastly.com/learning/compute/rust/"&gt;Rust&lt;/a&gt;, &lt;a href="https://developer.fastly.com/learning/compute/go/"&gt;Go&lt;/a&gt;, or &lt;a href="https://developer.fastly.com/learning/compute/javascript/"&gt;JavaScript&lt;/a&gt;. For development and testing, Fastly provides a &lt;a href="https://developer.fastly.com/learning/compute/testing/#running-a-local-testing-server"&gt;local testing environment for Compute&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What does an end-to-end test look like for an application running on Fastly Compute? Because Compute applications are served as HTTP APIs, one way to accomplish this would be a &lt;a href="https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs"&gt;GitHub actions&lt;/a&gt; workflow that starts the Compute application in the testing environment and then executes a shell script which runs cURL commands:&lt;/p&gt;

&lt;p&gt;.github/workflows/tests.yaml (excerpt)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;curl-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Compute Edge service, and test with curl&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Fastly CLI&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastly/compute-actions/setup@main&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Edge code dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and start Compute application&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastly compute serve &amp;gt; ./out.txt &amp;amp; (tail -f ./out.txt &amp;amp;) | grep -qF 'Listening on http://127.0.0.1:7676'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./curl-tests/test.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;./curl-tests/test.sh&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#! /bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test: returns 200 and the text &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Hello, World!&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; for /"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-vs&lt;/span&gt; &lt;span class="s2"&gt;"http://127.0.0.1:7676/"&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qF&lt;/span&gt; &lt;span class="s2"&gt;"200 OK"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'status not 200'&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qF&lt;/span&gt; &lt;span class="s2"&gt;"Hello, World!"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'unexpected response content'&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test: returns 200 and json including &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;newField&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;newValue&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; for /json"&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-vs&lt;/span&gt; &lt;span class="s2"&gt;"http://127.0.0.1:7676/json"&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qF&lt;/span&gt; &lt;span class="s2"&gt;"200 OK"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'status not 200'&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qF&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;newField&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;newValue&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'unexpected response content'&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although we have to perform a little bit of text handling in shell scripts, these tests ensure that changes to the program will continue to emit the expected output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests written in JavaScript
&lt;/h2&gt;

&lt;p&gt;However, many programming languages, platforms, and communities offer better options, like utilizing frameworks designed specifically for writing and executing tests.&lt;/p&gt;

&lt;p&gt;We wanted this same functionality for Fastly Compute too, with the ability to write simple tests for a Fastly Compute service like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns 200 and the text "Hello, World!" for /&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns 200 and json including "newField": "newValue" for /json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseJson&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]?.[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newField&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newValue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why we built Compute Application Testing for JavaScript, a library designed specifically for this purpose.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: End-to-end tests only work with the inputs and outputs of a program. Therefore, the tests and the Compute app that they apply to can be written in the same or different languages. This library allows you to specifically use JavaScript to write end-to-end tests against your Compute application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JavaScript is everywhere and super popular. JavaScript offers great support for making web requests, natively parsing JSON,  utilities such as regular expressions,  access to data structures such as objects and arrays, and the ability to work with an &lt;a href="https://www.npmjs.com"&gt;extensive package library available on npm&lt;/a&gt;. Because of these capabilities, it just felt natural to write end-to-end tests using JavaScript. And since we’re just checking the inputs and outputs, we can do this regardless of the language of the Compute application.&lt;/p&gt;

&lt;p&gt;Compute Application Testing for JavaScript is a JavaScript library, available on npm as &lt;a href="https://www.npmjs.com/package/@fastly/compute-testing"&gt;@fastly/compute-testing&lt;/a&gt;. Your test application will be running in &lt;a href="https://nodejs.org"&gt;Node.js&lt;/a&gt;, so you’ll want to make a test application separate from your Compute application. There, you can add it as a development dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @fastly/compute-testing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s take a look at what an individual test would look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns 200 and the text "Hello, World!" for /&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re familiar with the standard Node.js test runner, or a BDD-style runner such as Jest, this should look familiar to you. The test case has the name &lt;em&gt;returns 200 and the text "Hello, World!" for /&lt;/em&gt;. It uses the variable "app" to make a call to the '/' path of the Compute application. It then makes assertions on the status code and the text content of the response.&lt;/p&gt;

&lt;p&gt;What is the variable "app", you may be asking? It’s an instance of ComputeApplication, a class that allows you to instantiate your Compute application in the local test environment. The variable represents an instance of the Compute application, and it’s used like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComputeApplication&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@fastly/compute-testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Edge app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ComputeApplication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// Set 'appRoot' to the directory in which to start the app.  This is usually&lt;/span&gt;
      &lt;span class="c1"&gt;// the directory that contains the 'fastly.toml' file.&lt;/span&gt;
      &lt;span class="na"&gt;appRoot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ...tests here&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the class is instantiated, it calls out to the Fastly CLI to spawn an instance of your Compute application at appRoot. Once it’s running, your tests will be able to make requests to it—simply call .fetch() on it. It takes the same parameters as the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/fetch"&gt;global fetch function&lt;/a&gt;, so you can pass a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URL"&gt;URL object&lt;/a&gt;, a string, or a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Request"&gt;Request object&lt;/a&gt; as its first parameter, and set headers as you need—and you will receive back a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Response"&gt;Response object&lt;/a&gt;, which you can test for its status, headers, and contents. And because it’s a Response, you can also use its other useful features, such as built-in JSON deserialization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns 200 and json including "newField": "newValue" for /json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseJson&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]?.[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newField&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newValue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are plenty of other JavaScript features that are available to you to write expressive tests as well. For example, use Request to send JSON, form data (using FormData), and streaming blobs. And because you get back a Response object, you’re able to access native streaming and JSON parsing, which are useful for checking HTTP responses.&lt;/p&gt;

&lt;p&gt;Of course, using Node.js also opens you to all of the packages available on npm: If you need to parse an HTML DOM, you can look to &lt;a href="https://github.com/jsdom/jsdom"&gt;JSDOM&lt;/a&gt;. Do you need to work with a &lt;a href="https://en.wikipedia.org/wiki/JSON_Web_Token"&gt;JSON Web Token (JWT)&lt;/a&gt;? Look no further than &lt;a href="https://www.npmjs.com/package/jsonwebtoken"&gt;jsonwebtoken&lt;/a&gt;. In this post we’re using &lt;a href="https://nodejs.org/api/test.html"&gt;Node.js's test runner&lt;/a&gt; along with &lt;a href="https://nodejs.org/api/assert.html"&gt;Node.js's assertions&lt;/a&gt;, but do you prefer testing with &lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;, &lt;a href="https://mochajs.org/"&gt;Mocha&lt;/a&gt;, or &lt;a href="https://www.chaijs.com/"&gt;Chai&lt;/a&gt;? These are all available too. Whatever intent your test needs to express, writing your tests in JavaScript for Node.js will help you do that.&lt;/p&gt;

&lt;p&gt;Now, running these tests can be done with a GitHub workflow like this:&lt;/p&gt;

&lt;p&gt;.github/workflows/tests.yaml (excerpt)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;jobs&lt;/span&gt;:
  compute-testing-test:
    name: Test Compute Edge service using compute-testing
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4
      - name: Set up Fastly CLI
        uses: fastly/compute-actions/setup@main
      - name: Install Edge code dependencies
        run: npm &lt;span class="nb"&gt;install&lt;/span&gt;
      - name: Install Test code dependencies
        working-directory: ./test
        run: npm &lt;span class="nb"&gt;install&lt;/span&gt;
      - name: Run tests
        working-directory: ./test
        run: npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Triggering this workflow results in this neat format that gives us access to test names and some stats:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylx8s5czbbn2py5yjiic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylx8s5czbbn2py5yjiic.png" alt="Image description" width="512" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to take a closer look at the code and tests in this example, they are available on my GitHub at &lt;a href="https://github.com/harmony7/compute-testing-demo"&gt;https://github.com/harmony7/compute-testing-demo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on your own code
&lt;/h2&gt;

&lt;p&gt;Using JavaScript to write tests is now a great alternative to using scripts. Compute Application Testing for JavaScript is &lt;a href="https://www.npmjs.com/package/@fastly/compute-testing"&gt;available on npm&lt;/a&gt; for you to try right away. See how easy it is to build and run tests against your Compute application, and to add them to your CI tests.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: &lt;a href="https://www.npmjs.com/package/@fastly/compute-testing"&gt;@fastly/compute-testing&lt;/a&gt; is provided as a Fastly Labs product. Visit the &lt;a href="https://www.fastlylabs.com/"&gt;Fastly Labs&lt;/a&gt; website for terms of use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more information and examples, be sure to &lt;a href="https://github.com/fastly/js-compute-testing"&gt;check out our project page on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At Fastly, we strive to provide the tools that give you the power to run more code at the Edge and develop for it with the tools you know and love. We love to hear about it when our users get the most out of these tools. &lt;a href="https://community.fastly.com"&gt;Join us on the Fastly community forum&lt;/a&gt; and let us know what you’ve been building!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>javascript</category>
      <category>fastly</category>
      <category>edgecompute</category>
    </item>
    <item>
      <title>Host your Remix app on Fastly Compute</title>
      <dc:creator>Katsuyuki Omuro</dc:creator>
      <pubDate>Thu, 02 Mar 2023 02:08:51 +0000</pubDate>
      <link>https://forem.com/fastly/host-your-remix-app-on-fastly-computeedge-1k6f</link>
      <guid>https://forem.com/fastly/host-your-remix-app-on-fastly-computeedge-1k6f</guid>
      <description>&lt;p&gt;&lt;a href="https://remix.run/" rel="noopener noreferrer"&gt;Remix&lt;/a&gt; is a popular, modern JavaScript-based full stack web framework that allows the developer to focus on the user interface and work with web standards, in order to deliver user experiences that are responsive and robust. With our new remix-compute-js libraries, you can now host your Remix application on our Compute platform, allowing you to serve at our world-wide edge network — you don't even need an origin server.&lt;/p&gt;




&lt;p&gt;As our support for JavaScript on Compute grows, we are increasingly able to offer more integrations to work with existing JavaScript libraries and frameworks. We talked about &lt;a href="https://www.fastly.com/blog/no-origin-static-websites-at-the-edge" rel="noopener noreferrer"&gt;serving static websites entirely at the edge&lt;/a&gt; earlier this year, and more recently, running &lt;a href="https://www.fastly.com/blog/run-your-next-js-app-on-fastly" rel="noopener noreferrer"&gt;your Next.js site at the edge&lt;/a&gt;. And today, we’d like to share with you some work we’ve done to support running Remix.&lt;/p&gt;

&lt;p&gt;Remix is a popular JavaScript-based full stack web framework with an emphasis on web standards and progressive enhancement.  It’s a modern framework that fully embraces the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;Fetch API interfaces&lt;/a&gt;, making it a great fit for Compute, which &lt;a href="https://developer.fastly.com/learning/compute/javascript/#composing-requests-and-responses" rel="noopener noreferrer"&gt;does the same&lt;/a&gt;.  And best of all, Remix has a bring-your-own-runtime design, written from the ground up to work with various &lt;a href="https://remix.run/docs/en/v1/api/remix" rel="noopener noreferrer"&gt;server runtimes&lt;/a&gt; and &lt;a href="https://remix.run/docs/en/v1/other-api/adapter" rel="noopener noreferrer"&gt;server adapters&lt;/a&gt;, so that it can run on a multitude of platforms.&lt;/p&gt;

&lt;p&gt;As a result we’ve been able to build a Remix runtime, an entry point adapter, and a project template to give you full support to target Compute as an environment to run Remix.&lt;/p&gt;

&lt;p&gt;You now have the full power of Remix available to your Compute application, with its &lt;a href="https://remix.run/docs/en/v1/pages/philosophy#web-standards-http-and-html" rel="noopener noreferrer"&gt;rich support of Web Standards&lt;/a&gt; and &lt;a href="https://remix.run/docs/en/v1/pages/philosophy#progressive-enhancement" rel="noopener noreferrer"&gt;progressive enhancement&lt;/a&gt;. You’re free to build your Remix project as you wish, using its handy features: defining &lt;a href="https://remix.run/docs/en/v1/api/conventions#file-name-conventions" rel="noopener noreferrer"&gt;convention-based routing&lt;/a&gt;, harnessing the &lt;a href="https://remix.run/docs/en/v1/other-api/react-router" rel="noopener noreferrer"&gt;power of React Router&lt;/a&gt;, &lt;a href="https://remix.run/docs/en/v1/guides/mdx" rel="noopener noreferrer"&gt;rendering MDX&lt;/a&gt;, and building &lt;a href="https://remix.run/docs/en/v1/guides/api-routes" rel="noopener noreferrer"&gt;API Routes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And of course, you can take advantage of Compute features too, such as &lt;a href="https://remix.run/docs/en/v1/other-api/fetch" rel="noopener noreferrer"&gt;making fetch calls&lt;/a&gt; to &lt;a href="https://developer.fastly.com/learning/compute/javascript/#communicating-with-backend-servers-and-the-fastly-cache" rel="noopener noreferrer"&gt;backends&lt;/a&gt;, or &lt;a href="https://developer.fastly.com/learning/compute/javascript/#module-bundling" rel="noopener noreferrer"&gt;using polyfills to integrate&lt;/a&gt; with compatible modules available from NPM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your Remix project
&lt;/h2&gt;

&lt;p&gt;A template for Fastly Compute exists on GitHub at &lt;a href="https://github.com/fastly/remix-compute-js/tree/main/packages/remix-template" rel="noopener noreferrer"&gt;https://github.com/fastly/remix-compute-js/tree/main/packages/remix-template&lt;/a&gt;. This means you just need Node.js installed locally, and then you can create a Remix project by using the &lt;a href="https://remix.run/docs/en/v1/other-api/dev#remix-create" rel="noopener noreferrer"&gt;official &lt;code&gt;create-remix&lt;/code&gt; command-line tool&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-remix@latest ./my-app &lt;span class="nt"&gt;--template&lt;/span&gt; https://github.com/fastly/remix-compute-js/tree/main/packages/remix-template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll be asked if you want to create your project using JavaScript or TypeScript (my personal preference is TypeScript!). The rest of the process will happen automatically.&lt;/p&gt;

&lt;p&gt;If you switch to the &lt;code&gt;./my-app&lt;/code&gt; directory, you’ll see that you’ll have a project structure built out. In addition to being a Remix project, it will also be a Compute JavaScript application complete with a &lt;code&gt;fastly.toml&lt;/code&gt; file that describes your application to Fastly, and an entry point at &lt;code&gt;src/index.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your application is preconfigured to work on &lt;a href="https://developer.fastly.com/learning/compute/testing/#running-a-local-testing-server" rel="noopener noreferrer"&gt;Fastly’s Development Server&lt;/a&gt;, which will be available just by having the &lt;a href="https://developer.fastly.com/reference/cli/" rel="noopener noreferrer"&gt;Fastly CLI&lt;/a&gt; installed. Simply run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up &lt;a href="http://127.0.0.1:7676" rel="noopener noreferrer"&gt;http://127.0.0.1:7676&lt;/a&gt; in your web browser, and you will see the Remix template application showing up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0ou38dcdscl18fdqlcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0ou38dcdscl18fdqlcl.png" alt="Remix Project Template Screenshot" width="512" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This file is built from the &lt;code&gt;app/routes/index.jsx&lt;/code&gt; file (&lt;code&gt;.tsx&lt;/code&gt; if you’re using TypeScript!). You can go ahead and make changes to this file, and you’ll see that your application will be rebuilt, and your browser window will refresh automatically.&lt;/p&gt;

&lt;p&gt;There are actually two processes running here: Remix’s compiler and Fastly’s development server, both running in watch mode.  Whenever you make changes to files in the &lt;code&gt;app/&lt;/code&gt; directory, the Remix compiler will trigger a rebuild, causing output files to update in the &lt;code&gt;build/&lt;/code&gt; and &lt;code&gt;public/build/&lt;/code&gt; directories. Whenever this happens, or whenever you update files in the &lt;code&gt;public/&lt;/code&gt; directory, Fastly’s development server will rebuild. In effect, you now have a self-refreshing development environment for your Remix application running on Compute.&lt;/p&gt;

&lt;h2&gt;
  
  
  The server adapter
&lt;/h2&gt;

&lt;p&gt;Remix’s powerful architecture keeps two concerns separate: the Remix application itself, which is your code written within the Remix framework; and the interface between Remix and the server platform. This provides for flexibility in how the server platform interfaces with Remix - great news for platform providers like us (thanks Remix team!).&lt;/p&gt;

&lt;p&gt;The simplest usage, which is also the one implemented in the project template, is to use the &lt;code&gt;createEventHandler&lt;/code&gt; function Fastly exports from the server adapter package. It expects &lt;code&gt;staticAssets&lt;/code&gt;, imported from the &lt;code&gt;./statics&lt;/code&gt; file that is generated by the Static Publisher (also installed automatically when using the template):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createEventHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fastly/remix-server-adapter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;staticAssets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./statics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;createEventHandler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;staticAssets&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if you have a case where you want more granular control over the &lt;code&gt;ServerBuild&lt;/code&gt; module to use with Remix, or how to handle requests for static assets, we also provide lower-level functions to help you do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRequestHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleAsset&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fastly/remix-server-adapter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;staticAssets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./statics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;staticAssets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/build/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRequestHandler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;  

&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;  
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;staticAssets&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;requestHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Going Live
&lt;/h2&gt;

&lt;p&gt;When you're ready to ship, create a production build and view your site to make sure that it runs well.&lt;/p&gt;

&lt;p&gt;Stop the development server, then run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a production build of your application, generating optimized JavaScript files. Then refresh your browser to see the production-build version of your app running in the development server (live reloading is not available in production builds).&lt;/p&gt;

&lt;p&gt;Once you’ve seen that the production build of your site looks good, you're ready to deploy to Fastly. If you don't already have an account, start by &lt;a href="https://www.fastly.com/signup/edge-compute" rel="noopener noreferrer"&gt;creating one&lt;/a&gt; and then &lt;a href="https://developer.fastly.com/learning/tools/cli#configuring" rel="noopener noreferrer"&gt;configure Fastly CLI with your new account credentials&lt;/a&gt;.  Once that's done, you can deploy your app to your Fastly account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll be prompted to create a new Fastly service. If you want to use an existing service, modify &lt;code&gt;fastly.toml&lt;/code&gt; and set the &lt;code&gt;service_id&lt;/code&gt; value to your service ID before running the commands above.&lt;/p&gt;

&lt;p&gt;And that’s really all there is to using Remix on Compute: Develop as normal using &lt;code&gt;npm run dev&lt;/code&gt;. Test with &lt;code&gt;npm run build&lt;/code&gt; and &lt;code&gt;npm start&lt;/code&gt;, and when you’re ready to go to production, deploy to the edge with &lt;code&gt;npm run build&lt;/code&gt; and &lt;code&gt;npm run deploy&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Already have a Remix application? Let’s bring it to Compute
&lt;/h2&gt;

&lt;p&gt;Do you already have a Remix application that you’ve started?  Perhaps one already hosted on a Node.js backend?  So long as your project doesn’t attempt to do something that isn’t conceptually impossible on Fastly, such as trying to interact with the filesystem, it’s usually not too hard to adapt the application to work on Compute.&lt;/p&gt;

&lt;p&gt;The easiest way to do this is to create an empty Remix project using the template above. That way, all of the dependencies will be installed for you, and the server adapter will be created as part of the process as well.  Install into your new project any dependency packages referenced by your original project.  Then, move your existing project’s files into your new project.  In Remix, your application’s files are found in app/ and public/. For most of the files in these directories, you can usually simply copy them into your new project, keeping the following points in mind.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Some of the source files will reference Remix runtime packages such as &lt;code&gt;@remix-run/node&lt;/code&gt;, &lt;code&gt;@remix-run/cloudflare&lt;/code&gt;, or &lt;code&gt;@remix-run/deno&lt;/code&gt;. Modify these references to &lt;code&gt;@fastly/remix-server-runtime&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, you may have the following:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@remix-run/node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;You’ll want to change this to:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fastly/remix-server-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For &lt;code&gt;entry.client.tsx&lt;/code&gt;, &lt;code&gt;entry.server.tsx&lt;/code&gt;, and &lt;code&gt;root.tsx&lt;/code&gt; in the &lt;code&gt;app/&lt;/code&gt; directory, you’ll generally want to start with the ones found in the new project.  Examine these files in your source project.  Look for any changes that you had made to them in your source project, and then make the equivalent changes in the new project as necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Skip &lt;code&gt;/public/build/&lt;/code&gt;.  This is a directory that is generated during the build step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Depending on the dependencies used by your project, you may need to &lt;a href="https://developer.fastly.com/learning/compute/javascript/#module-bundling" rel="noopener noreferrer"&gt;add polyfills to run in Compute&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that’s all there is to it!  Build and deploy your application to the edge as we did in the previous section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Remix application, now on Compute
&lt;/h2&gt;

&lt;p&gt;The integration between Remix and Compute is here – you can now host your Remix application entirely at the edge, without even needing an origin server. Benefit from the robust architecture of Remix, and then go to Compute to run your app on thousands of servers across Fastly's network.&lt;/p&gt;

&lt;p&gt;At Fastly, we strive to provide the tools that give you the power to run more code at the Edge and develop for it with the tools you know and love. We love to hear it when our users get the most out of these tools.  &lt;a href="https://twitter.com/fastlydevs" rel="noopener noreferrer"&gt;Reach out to us on Twitter&lt;/a&gt; and let us know what you’ve been building!&lt;/p&gt;

</description>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Supercharge your API with realtime push powers using Fastly Fanout</title>
      <dc:creator>Katsuyuki Omuro</dc:creator>
      <pubDate>Tue, 22 Nov 2022 08:01:46 +0000</pubDate>
      <link>https://forem.com/fastly/supercharge-your-api-with-realtime-push-powers-using-fastly-fanout-3ba0</link>
      <guid>https://forem.com/fastly/supercharge-your-api-with-realtime-push-powers-using-fastly-fanout-3ba0</guid>
      <description>&lt;p&gt;More than ever before, users expect applications and websites to be “realtime”—to see new information as soon as it becomes available on the server side, without needing to ask for a refresh. Fastly Fanout is here to augment your API with the power of realtime push, enabling you to power these applications using your existing HTTP origin instead of needing to maintain a complicated and dedicated messaging infrastructure.&lt;/p&gt;




&lt;p&gt;Realtime is the thing these days. Users are in a world where websites and apps constantly show updated data in a dynamic and interactive way. Your website users need to see the newest chat message, the newest stock price, and the newest in-stock item—and they need to see these updates right now, without needing to request a refresh.&lt;/p&gt;

&lt;h2&gt;
  
  
  Realtime apps need realtime APIs
&lt;/h2&gt;

&lt;p&gt;APIs are the future of the world. We already live in a world where you can &lt;a href="https://tinyurl.com/app/dev" rel="noopener noreferrer"&gt;shorten a URL&lt;/a&gt;, &lt;a href="https://cloud.google.com/translate/docs/reference/rest" rel="noopener noreferrer"&gt;translate text into various languages&lt;/a&gt;, &lt;a href="https://developers.facebook.com/docs/instagram-api/" rel="noopener noreferrer"&gt;get social media updates&lt;/a&gt;, and even &lt;a href="https://zincapi.com" rel="noopener noreferrer"&gt;order stuff from retailers&lt;/a&gt; and &lt;a href="https://api.developer.lifx.com/reference/introduction" rel="noopener noreferrer"&gt;control the lights in your house&lt;/a&gt;, all using web APIs. As demand for realtime web applications continues to grow, it only makes sense that support for realtime push in APIs will become essential to meet this need.&lt;/p&gt;

&lt;p&gt;At first glance, the requirements of realtime push seem to be at odds with the traditional “one-request, one-response” model of HTTP messaging. However, by utilizing long-lived TCP connections, realtime APIs can and do exist. For example, &lt;a href="https://developer.twitter.com/en/docs/tutorials/stream-tweets-in-real-time" rel="noopener noreferrer"&gt;Twitter’s streaming endpoint&lt;/a&gt; is such an API. With HTTP streaming, the response from the server is sent without a &lt;code&gt;Content-Length&lt;/code&gt; header, and instead uses &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding#chunked" rel="noopener noreferrer"&gt;&lt;code&gt;Transfer-Encoding: chunked&lt;/code&gt;&lt;/a&gt;. The response stays open forever, and the server continues to append to the response as data becomes ready on the server.&lt;/p&gt;

&lt;pre&gt;
GET /1.1/statuses/simple.json HTTP 1.1
Host: stream.twitter.com
Authorization: OAuth [...]
&lt;/pre&gt;

&lt;pre&gt;
HTTP/1.1 200 OK
connection: close
content-type: application/json
date: Wed, 19 Oct 2022 00:32:07 GMT
server: tsa
transfer-encoding: chunked
x-connection-hash: 6028cbda...6f0443a6

{"created_at": "Wed Oct 19 00:32:06 +0000 2022", "id": 588862464202297345, "text": "tweet 1", ... }
{"created_at": "Wed Oct 19 00:32:07 +0000 2022", "id": 588862468392255488, "text": "tweet 2", ... }

&lt;em&gt;... (ad infinitum)&lt;/em&gt;
&lt;/pre&gt;

Twitter’s streaming API hangs open after the request and adds data about tweets to the end of the response as they become ready.




&lt;p&gt;Streaming HTTP responses are arguably a hack that subverts the conventional one-request, one-response model that HTTP was designed to serve. It’s also the underlying mechanism of the popular &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" rel="noopener noreferrer"&gt;Server-Sent Events&lt;/a&gt; technology, supported &lt;a href="https://caniuse.com/eventsource" rel="noopener noreferrer"&gt;by all modern browsers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://javascript.info/long-polling" rel="noopener noreferrer"&gt;HTTP long-polling&lt;/a&gt; is another common technique, used for example &lt;a href="https://stackoverflow.com/questions/43944729/how-does-facebook-real-time-works" rel="noopener noreferrer"&gt;by Facebook until very recently for its chat platform&lt;/a&gt;, where the server’s response hangs open only until the next "event" happens or a timeout period passes. At that point the server responds and ends the connection. The client then initiates another request right away, waiting for the next event. This effectively creates a data channel for the server to send push updates, but in a way that uses HTTP more conventionally:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcj6o1jmgm5mx2bgodm7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcj6o1jmgm5mx2bgodm7.png" alt="Long-polling flow diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avoiding the use of HTTP protocols may be an option as well. Recently, for example, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket" rel="noopener noreferrer"&gt;WebSocket&lt;/a&gt; support can be found &lt;a href="https://caniuse.com/websockets" rel="noopener noreferrer"&gt;in all modern browsers&lt;/a&gt;, which does away with the creative use of HTTP and adds support for messages to flow in both directions. HTTP techniques are still handy though, so we'll focus on those in this post, and will cover WebSockets in the next post.&lt;/p&gt;

&lt;p&gt;So how can we implement an HTTP API to support realtime push?&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing a realtime API
&lt;/h2&gt;

&lt;p&gt;There are lots of off-the-shelf components that are designed to push data from the server to the client, like Ably, Firebase, and Pusher. While these are all fine solutions for their use case, they tend to be end-to-end, that is, they are hosted solutions, or they provide a server library to use on your stack. They may use some private protocol and sometimes they require the use of their client SDKs. These may, indeed, be the product of choice if you’re using it to build an entire application stack yourself and don’t plan to expose your APIs publicly.&lt;/p&gt;

&lt;p&gt;However, if it’s an API that you’re trying to offer, this is not the best strategy for you. You need control over your API surface. You want to speak HTTP directly and perhaps even &lt;a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md" rel="noopener noreferrer"&gt;document the API using tools such as OpenAPI&lt;/a&gt;, rather than require the use of a specific SDK.&lt;/p&gt;

&lt;p&gt;Of course, you can go ahead and roll your own stack, but that often means maintaining a complicated and dedicated messaging infrastructure. And with all these long-lived connections, the problem of server resources is one you’ll need to tackle, not to mention scalability as demand for your product increases. If you’re Twitter or Facebook, you may have the resources to build such a project and maintain it, but even if you do, it often ends up not being very CDN-friendly, as you’ll still have to handle the traffic at your origin.&lt;/p&gt;

&lt;p&gt;With realtime becoming an imminent need worldwide, it was clear that we needed a better solution for APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The realtime solution for APIs: Fastly Fanout
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmwur9szooel0igoz0im.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmwur9szooel0igoz0im.png" alt="Fanout - now part of Fastly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Earlier this year, Fastly announced that Fanout (&lt;a href="https://fanout.io" rel="noopener noreferrer"&gt;https://fanout.io&lt;/a&gt;) would be joining Fastly, and that we would be offering Fanout as a product. I actually joined Fastly as part of that acquisition, and now that we've wired it all up, I can show you how to use Fanout with a Fastly service.&lt;/p&gt;

&lt;p&gt;Under the hood, Fastly Fanout is &lt;a href="https://pushpin.org" rel="noopener noreferrer"&gt;based on Pushpin&lt;/a&gt;, an open source project that Fanout started (and is &lt;a href="https://github.com/fanout/pushpin" rel="noopener noreferrer"&gt;still under active development&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Fanout runs on Fastly's edge servers across &lt;a href="http://fastly.com/network" rel="noopener noreferrer"&gt;our global network&lt;/a&gt; and forwards requests to your origin that serves your API. It’s your API, so you get full control over its API surface, and we don’t require your users to use a specific server or client SDK. It runs at the edge, so it solves the problems with persistent connections and with scalability of origin traffic. And it supports HTTP streaming (e.g., Server-Sent Events), HTTP long-polling, and WebSocket connections (more on WebSocket in the next article).&lt;/p&gt;

&lt;p&gt;But more fundamentally, Fanout has a different design philosophy to other push services, which makes it really great for building APIs. Not only did we want to give you full control over your API, we wanted to use standard, interoperable components. We also wanted to make sure our solution maintained the proper separation of concerns on your team.&lt;/p&gt;

&lt;p&gt;Suppose your API design team has defined a realtime API at &lt;code&gt;/stream&lt;/code&gt;. Then you have engineer roles build the backend (and perhaps the frontend too), as well as operation roles who decide on things like your infrastructure and caching.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faq17br7rjbgwk8g7dfwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faq17br7rjbgwk8g7dfwy.png" alt="Your API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As Fastly Fanout, we enter this diagram in the middle like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9j4rvaiyjmurq2vavlx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9j4rvaiyjmurq2vavlx.png" alt="Fanout Proxy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This architecture allows you to keep your original API design. Fanout handles the long-lived connections and the publishing of data, and lets the origin focus on the business logic. Your API consumers don’t even need awareness of Fanout.&lt;/p&gt;

&lt;p&gt;It also frees you from technological and vendor lock-in. As all of these components are standard and interoperable, you’re free to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make API design changes as you wish&lt;/li&gt;
&lt;li&gt;make engineering decisions, such as to switch your origin's programming language or framework, or to switch database engines&lt;/li&gt;
&lt;li&gt;make operations decisions, like moving your origin to a cloud provider, or switching from Fastly Fanout to running Pushpin on-prem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The off-the-shelf, end-to-end solutions we mentioned earlier would have encroached on every layer here, but Fanout’s design allows for each of the concerns to stay with their respective roles.&lt;/p&gt;

&lt;p&gt;This is all possible because we define the backend interaction with Fanout to use an open integration protocol. We call this protocol &lt;a href="https://pushpin.org/docs/protocols/grip/" rel="noopener noreferrer"&gt;GRIP (Generic Realtime Intermediary Protocol)&lt;/a&gt;, which we publish as an open standard for anyone to implement. Fastly Fanout is simply one such implementation. Fastly and Fanout have always been &lt;a href="https://www.fastly.com/open-source/" rel="noopener noreferrer"&gt;dedicated to open-source and open standards&lt;/a&gt;, and our common stance here is one of the reasons we have always gotten along so well! Fastly will continue to support both Pushpin and GRIP.&lt;/p&gt;

&lt;p&gt;This architecture is truly empowering! Because Fanout runs at the edge, realtime activity can happen at any of our numerous POPs nearest the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7x2j1twb93scd2q35ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7x2j1twb93scd2q35ag.png" alt="Fanout holds all the connections"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We take on all of the heavy lifting of holding your long-lived connections around the world and publishing messages to individual clients, &lt;em&gt;so your origin doesn’t have to&lt;/em&gt;. We free you from the scalability issues of having to sustain numerous connections, as well as from the burden of having to maintain a complex messaging infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling Fanout
&lt;/h2&gt;

&lt;p&gt;Fanout is a feature of Fastly that can be enabled per service. Once it’s enabled for your service, handle the request in Compute@Edge, and call &lt;a href="https://docs.rs/fastly/latest/fastly/struct.Request.html#method.handoff_fanout" rel="noopener noreferrer"&gt;&lt;code&gt;req.handoff_fanout("backend")&lt;/code&gt;&lt;/a&gt; for each request that you wish to pass through the Fanout proxy to your backend (Supported in Rust programs for now, other languages coming soon). This design gives you flexibility and granular control over which requests you wish to handle with Fanout.&lt;/p&gt;

&lt;p&gt;If you simply want to pass all requests through Fanout, then we have a &lt;a href="https://github.com/fastly/compute-starter-kit-rust-fanout-forward" rel="noopener noreferrer"&gt;Fanout forward starter kit&lt;/a&gt; that you can use. &lt;a href="https://developer.fastly.com/learning/tools/cli" rel="noopener noreferrer"&gt;Obtain the Fastly CLI&lt;/a&gt;, and create a Compute@Edge project using the starter kit as follows, and put it in front of your origin server. See &lt;a href="https://developer.fastly.com/solutions/starters/" rel="noopener noreferrer"&gt;Compute@Edge Starter Kits&lt;/a&gt; for more information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-project
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-project
&lt;span class="nv"&gt;$ &lt;/span&gt;fastly compute init &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/fastly/compute-starter-kit-rust-fanout-forward
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Realtime response lifecycle with GRIP
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, Fanout and your origin interact using GRIP. When a client makes a request through the Fanout proxy, Fanout forwards it to the origin. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwplqiednmlghc8r6kzzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwplqiednmlghc8r6kzzz.png" alt="Flow diagram - Request lifecycle with Fanout"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The origin response is what makes Fanout interesting. The origin is &lt;em&gt;free to make a runtime decision&lt;/em&gt; about whether the response should use realtime push. Of course it may elect to always use realtime, but it can, for example, check a query parameter, an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept" rel="noopener noreferrer"&gt;&lt;code&gt;Accept&lt;/code&gt;&lt;/a&gt; header, or for any condition you wish. The origin uses GRIP to indicate to the proxy what to do (such as to &lt;em&gt;hold the response as HTTP streaming&lt;/em&gt;, and to &lt;em&gt;subscribe the connection to channels foo and bar&lt;/em&gt;), and may also provide an initial payload depending on the circumstances. After that, the origin is free to drop this connection, and if the GRIP instruction contains a hold, Fanout will now hold that connection open.&lt;/p&gt;

&lt;p&gt;You may be wondering how complicated this GRIP is.  It’s actually super simple:&lt;/p&gt;

&lt;pre&gt;
HTTP/1.1 200 OK
Content-Type: text/plain
&lt;strong&gt;Grip-Hold: stream&lt;/strong&gt;
&lt;strong&gt;Grip-Channel: foo, bar&lt;/strong&gt;

[start stream]
&lt;/pre&gt;

&lt;p&gt;The bolded HTTP headers above are the instructions that instruct Fanout to hold the response as HTTP streaming, and to subscribe the connection to channels foo and bar.&lt;/p&gt;

&lt;p&gt;If you call this endpoint now, you’ll see the following, and the response will hang open.&lt;/p&gt;

&lt;pre&gt;
$ curl https://your-service-name.edgecompute.app/stream
[start stream]
&lt;em&gt;(response hangs)&lt;/em&gt;
&lt;/pre&gt;

&lt;p&gt;At any later time, when the origin is ready to publish a message to one of these channels, it can signal to the proxy using a GRIP Push:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cv0qk4yaji5h03zh0hg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cv0qk4yaji5h03zh0hg.png" alt="Flow diagram - POST a push"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is done by making an HTTP POST to the GRIP publishing endpoint:&lt;/p&gt;

&lt;pre&gt;
POST /service/&lt;u&gt;ABCDEFGHI0123456789&lt;/u&gt;/publish/ HTTP/1.1
Host: api.fastly.com
Fastly-Key: &lt;u&gt;YOUR_FASTLY_KEY&lt;/u&gt;
Content-Type: application/json

{
  "items": [
    {
      &lt;strong&gt;"channel": "foo",&lt;/strong&gt;
      "formats": {
        &lt;strong&gt;"http-stream": {
          "content": "hello\n"
        }&lt;/strong&gt;
      }
    }
  ]
}
&lt;/pre&gt;

&lt;p&gt;On Fastly Fanout, the publishing endpoint is at &lt;code&gt;api.fastly.com/service/&amp;lt;service-id&amp;gt;/publish/&lt;/code&gt;. Make a POST to it using your &lt;a href="https://docs.fastly.com/en/guides/finding-and-managing-your-account-info#finding-your-service-id" rel="noopener noreferrer"&gt;Fastly Service ID&lt;/a&gt;, and provide a &lt;a href="https://docs.fastly.com/en/guides/using-api-tokens" rel="noopener noreferrer"&gt;Fastly API token&lt;/a&gt; that has the &lt;a href="https://developer.fastly.com/learning/concepts/real-time-messaging/fanout/#publishing" rel="noopener noreferrer"&gt;&lt;code&gt;publish&lt;/code&gt; scope for your service&lt;/a&gt;. The endpoint accepts up to 64kb of POST data per call, and we accept an array of items, so that you can publish multiple items at once. Each item must specify the channel (foo in this example) as well as the data.&lt;/p&gt;

&lt;p&gt;The data is provided as a keyed object, and we allow the data to be specified in any number of formats. This example is for the http-stream format (HTTP streaming), but we also support http-response (HTTP long-polling) and ws-message (WebSocket). This design allows various clients to listen on different transports for updates on the same channel.&lt;/p&gt;

&lt;p&gt;Performing the publish shown above would insert the following bolded line into the output stream of our example stream, and the response will again continue to hang.&lt;/p&gt;

&lt;pre&gt;
$ curl https://your-service-name.edgecompute.app/stream
[start stream]
&lt;strong&gt;hello&lt;/strong&gt;
&lt;em&gt;(response hangs)&lt;/em&gt;
&lt;/pre&gt;

&lt;h2&gt;
  
  
  Example: Adding realtime to an API endpoint
&lt;/h2&gt;

&lt;p&gt;Now that we’ve talked about how Fanout works, it’s a great opportunity to show how it can be used to take an existing API endpoint and add realtime powers.&lt;/p&gt;

&lt;p&gt;Imagine an example API that represents a counter, identified by an ID. You can get the current value of a counter by calling it with the GET method, and you can increment it by calling with the POST method. This is implemented using the following PHP code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#^/counter/(\d+)/?#'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$counter_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCounterValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$counter_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;incrementCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$counter_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll put Fastly Fanout in front of it, and then we can invoke it like this:&lt;/p&gt;

&lt;pre&gt;
$ curl https://fastly-fanout-blog.edgecompute.app/counter/1
&lt;strong&gt;{"value": 1}&lt;/strong&gt;
$ curl -X POST https://fastly-fanout-blog.edgecompute.app/counter/1
&lt;strong&gt;{"value": 2}&lt;/strong&gt;
$ curl https://fastly-fanout-blog.edgecompute.app/counter/1
&lt;strong&gt;{"value": 2}&lt;/strong&gt;
$ 
&lt;/pre&gt;

&lt;p&gt;Now let’s add realtime to this GET endpoint. As discussed earlier, Fanout lets us do this conditionally. So this time, let’s check for the existence of an &lt;code&gt;Accept&lt;/code&gt; header with the value &lt;code&gt;text/event-stream&lt;/code&gt;. In that case we use GRIP to ask Fanout to hold an HTTP stream open and subscribe the connection to a channel named after the counter’s ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCounterValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$counter_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// -- Start of new code&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'HTTP_ACCEPT'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'text/event-stream'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: text/event-stream'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Grip-Hold: stream'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Grip-Channel: counter-'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$counter_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'data: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// -- End of new code&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what happens if we call this endpoint now. Note that if we call it with the &lt;code&gt;Accept: text/event-stream&lt;/code&gt; header, we see the “data” prefix and the response will hang open.&lt;/p&gt;

&lt;pre&gt;
% curl https://fastly-fanout-blog.edgecompute.app/counter/1
&lt;strong&gt;{"value": 2}&lt;/strong&gt;
% curl -H "Accept: text/event-stream" https://fastly-fanout-blog.edgecompute.app/counter/1
&lt;strong&gt;data: {"value": 2}&lt;/strong&gt;
&lt;em&gt;(response hangs)&lt;/em&gt;
&lt;/pre&gt;

&lt;p&gt;We’ll add publishing to our POST endpoint now. In the code that handles POST, we build the data structure for publishing, and send it to the GRIP publishing endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;incrementCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$counter_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// -- Start of new code&lt;/span&gt;
    &lt;span class="nv"&gt;$headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s1"&gt;'Fastly-Key: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'FASTLY_KEY'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s1"&gt;'items'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'channel'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'counter-'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$counter_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'formats'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'http-stream'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'data: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt; 
    &lt;span class="nv"&gt;$context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;stream_context_create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="s1"&gt;'http'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'method'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'header'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$headers&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;'https://api.fastly.com/service/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'FASTLY_SERVICE_ID'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/publish/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nv"&gt;$context&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;    
    &lt;span class="c1"&gt;// -- End of new code&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s try calling this! It’s best to leave the previous request hanging, and use a new terminal window.&lt;/p&gt;

&lt;pre&gt;
$ curl -X POST https://fastly-fanout-blog.edgecompute.app/counter/1
&lt;strong&gt;{"value": 3}&lt;/strong&gt;
$ curl -X POST https://fastly-fanout-blog.edgecompute.app/counter/1
&lt;strong&gt;{"value": 4}&lt;/strong&gt;
$ 
&lt;/pre&gt;

&lt;p&gt;The updates stream through to the first window (new lines bolded):&lt;/p&gt;

&lt;pre&gt;
$ curl -H "Accept: text/event-stream" https://fastly-fanout-blog.edgecompute.app/counter/1
data: {"value": 2}

&lt;strong&gt;data: {"value": 3}&lt;/strong&gt;

&lt;strong&gt;data: {"value": 4}&lt;/strong&gt;

&lt;em&gt;(response hangs)&lt;/em&gt;
&lt;/pre&gt;

&lt;p&gt;So there we have it! We’ve used GRIP to add realtime to an API by expanding on the existing origin code. This was done in a PHP codebase—a language that we’d usually never expect to be doing realtime. With Fanout, we’re able to do this without needing to hold our own connections, without needing to build a messaging architecture, using an API that we’ve defined, using all standard components, all standard protocols, and an open standard, GRIP, to tie it all together.&lt;/p&gt;

&lt;p&gt;Check out the code for this example, available for viewing on Glitch at &lt;a href="https://glitch.com/edit/#!/fanout-blog-demo" rel="noopener noreferrer"&gt;https://glitch.com/edit/#!/fanout-blog-demo&lt;/a&gt;, and behind Fanout at &lt;a href="https://fastly-fanout-blog.edgecompute.app/" rel="noopener noreferrer"&gt;https://fastly-fanout-blog.edgecompute.app/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As more of the world demands apps that update in real time, your web APIs need realtime push as well. Fastly Fanout is here to help you add realtime push to your APIs, allowing you to leverage your existing HTTP origin and design principles, while also relieving you from having to sustain numerous connections and having to maintain a complex messaging infrastructure.&lt;/p&gt;

&lt;p&gt;We’re certain that this opens a new world of possibility and we can’t wait to see what you’ll be creating with your new realtime powers. &lt;a href="https://developer.fastly.com/learning/concepts/real-time-messaging/" rel="noopener noreferrer"&gt;Try it out&lt;/a&gt; and let us know what you think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional reading
&lt;/h2&gt;

&lt;p&gt;Fanout on Fastly Developer Hub &lt;br&gt;
&lt;a href="https://developer.fastly.com/learning/concepts/real-time-messaging/fanout" rel="noopener noreferrer"&gt;https://developer.fastly.com/learning/concepts/real-time-messaging/fanout&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fanout Documentation &lt;br&gt;
&lt;a href="https://docs.fanout.io/" rel="noopener noreferrer"&gt;https://docs.fanout.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pushpin - Open Source GRIP Proxy&lt;br&gt;
 &lt;a href="https://pushpin.org/" rel="noopener noreferrer"&gt;https://pushpin.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pushpin on GitHub&lt;br&gt;
&lt;a href="https://github.com/fanout/pushpin" rel="noopener noreferrer"&gt;https://github.com/fanout/pushpin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GRIP - Generic Realtime Intermediary Protocol &lt;br&gt;
&lt;a href="https://pushpin.org/docs/protocols/grip/" rel="noopener noreferrer"&gt;https://pushpin.org/docs/protocols/grip/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>realtime</category>
      <category>fastly</category>
      <category>fanout</category>
      <category>messaging</category>
    </item>
  </channel>
</rss>
