<?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: Fastly</title>
    <description>The latest articles on Forem by Fastly (@fastly).</description>
    <link>https://forem.com/fastly</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%2Forganization%2Fprofile_image%2F2298%2F27608760-4be1-4e12-b054-b8a2748d978d.png</url>
      <title>Forem: Fastly</title>
      <link>https://forem.com/fastly</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fastly"/>
    <language>en</language>
    <item>
      <title>Transform web pages at the edge</title>
      <dc:creator>Sue Smith</dc:creator>
      <pubDate>Mon, 03 Nov 2025 16:57:50 +0000</pubDate>
      <link>https://forem.com/fastly/transform-web-pages-at-the-edge-1lop</link>
      <guid>https://forem.com/fastly/transform-web-pages-at-the-edge-1lop</guid>
      <description>&lt;p&gt;Edge computing apps allow you to enhance the behavior of a website by manipulating the request and response at the network edge, near the user. You can now transform your web page content at the Fastly edge more easily with the &lt;a href="https://www.fastly.com/blog/rewriting-html-with-the-fastly-javascript-sdk" rel="noopener noreferrer"&gt;HTML Rewriter&lt;/a&gt; in your JavaScript Compute apps. Ideal for customizations like personalization and A/B testing, the rewriter uses familiar DOM manipulation patterns. &lt;/p&gt;

&lt;p&gt;Let's run through how to switch up your page HTML with a Fastly Compute app in just a few steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start a new project
&lt;/h2&gt;

&lt;p&gt;In your terminal, create a new directory:&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;mkdir &lt;/span&gt;htmlrewrite &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;htmlrewrite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize a new Compute app:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Accept the defaults, choosing &lt;strong&gt;JavaScript&lt;/strong&gt; and selecting the empty starter kit.&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%2Fajp232915f821hx8tnwc.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%2Fajp232915f821hx8tnwc.png" alt="empty starter" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter project metadata
&lt;/h2&gt;

&lt;p&gt;In your new project, open the &lt;code&gt;package.json&lt;/code&gt; and change the Compute JS SDK dependency as follows:&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="s2"&gt;"@fastly/js-compute"&lt;/span&gt;: &lt;span class="s2"&gt;"^3.35.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;npm install&lt;/code&gt; to get your environment ready.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;fastly.toml&lt;/code&gt; file, add the details for the website you want to transform at the edge (changing the addresses to use your own or using the included demo site):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[local_server]&lt;/span&gt;

  &lt;span class="nn"&gt;[local_server.backends]&lt;/span&gt;

    &lt;span class="nn"&gt;[local_server.backends.website]&lt;/span&gt;
      &lt;span class="py"&gt;override_host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"www.goldengirls.codes"&lt;/span&gt;
      &lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://www.goldengirls.codes/"&lt;/span&gt;

&lt;span class="nn"&gt;[setup]&lt;/span&gt;

  &lt;span class="nn"&gt;[setup.backends]&lt;/span&gt;

    &lt;span class="nn"&gt;[setup.backends.website]&lt;/span&gt;
      &lt;span class="py"&gt;address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"www.goldengirls.codes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Write your logic
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;src/index.js&lt;/code&gt; we'll be writing our Compute logic to transform the HTML at the edge. &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%2Flaa3upagld76dcaoukvt.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%2Flaa3upagld76dcaoukvt.png" alt="our js code" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start by importing a couple of dependencies:&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;getGeolocationForIpAddress&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:geolocation&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;HTMLRewritingStream&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:html-rewriter&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;/div&gt;



&lt;p&gt;We're going to build geolocation into our processing.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;handleRequest&lt;/code&gt; function, add &lt;code&gt;try&lt;/code&gt; &lt;code&gt;catch&lt;/code&gt; blocks:&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;//rewriter processing here&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal Server Error&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;500&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;h3&gt;
  
  
  Grab geolocation info
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;code&gt;try&lt;/code&gt; block, let's first get some info from the request:&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;let&lt;/span&gt; &lt;span class="nx"&gt;ip&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;event&lt;/span&gt;&lt;span class="p"&gt;.&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;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&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;geo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getGeolocationForIpAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're going to use the time of day at the user location to tailor the display:&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;let&lt;/span&gt; &lt;span class="nx"&gt;displayTime&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getHours&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;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utc_offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;emoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 🏙️&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="s2"&gt; 🌃&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;/div&gt;



&lt;p&gt;We'll insert this daft emoji into the page to personalize it to the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Specify rewrite behavior
&lt;/h3&gt;

&lt;p&gt;Still inside the &lt;code&gt;try&lt;/code&gt; block, use an &lt;code&gt;HTMLRewritingStream&lt;/code&gt; to specify what you want your Compute app to do to the HTML from your origin website. In this example we append the emoji to the &lt;code&gt;h1&lt;/code&gt; element and replace every second image with a different one – change yours to suit your website!&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;let&lt;/span&gt; &lt;span class="nx"&gt;transformer&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;HTMLRewritingStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h1&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;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&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;onElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div.code:nth-child(even) img&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;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src&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="s2"&gt;https://shadypinesmiami.github.io/sophia.jpg&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;/div&gt;



&lt;p&gt;You can use standard CSS selectors to indicate the elements you want to transform.&lt;/p&gt;

&lt;p&gt;Fetch the response from the website specified as a &lt;code&gt;backend&lt;/code&gt; in your &lt;code&gt;toml&lt;/code&gt; and carry out the transformation on 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;let&lt;/span&gt; &lt;span class="nx"&gt;backendResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;await&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="s2"&gt;https://www.goldengirls.codes/&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;backend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;website&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;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;transformer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, return the new response to the user:&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;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;backendResponse&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="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;text/html&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;Your script should now look something 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="c1"&gt;/// &amp;lt;reference types="@fastly/js-compute" /&amp;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;getGeolocationForIpAddress&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:geolocation&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;HTMLRewritingStream&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:html-rewriter&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="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="k"&gt;try&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;ip&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;event&lt;/span&gt;&lt;span class="p"&gt;.&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;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&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;geo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getGeolocationForIpAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ip&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;displayTime&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getHours&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;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utc_offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;emoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;displayTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; 🏙️&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="s2"&gt; 🌃&lt;/span&gt;&lt;span class="dl"&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;transformer&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;HTMLRewritingStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h1&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;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&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;onElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div.code:nth-child(even) img&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;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src&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="s2"&gt;https://shadypinesmiami.github.io/sophia.jpg&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;backendResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;await&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="s2"&gt;https://www.goldengirls.codes/&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;backend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;website&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;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;transformer&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;backendResponse&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="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;text/html&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal Server Error&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;500&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;h2&gt;
  
  
  Test your app
&lt;/h2&gt;

&lt;p&gt;Time to run your app! In the terminal, enter the start 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 start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With any luck you'll see a local address to try the app at.&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%2Fdtkkwuyqi25zjy2o2gyh.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%2Fdtkkwuyqi25zjy2o2gyh.png" alt="test site" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might notice that the time of day emoji doesn't actually match the time of day at your location.. 🤔 That's because your app is just running locally on your computer – you'll need to deploy it to the edge to see that functionality working reliably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your app
&lt;/h2&gt;

&lt;p&gt;Everything working the way you want it to? Great, time to deploy to Fastly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.fastly.com/signup" rel="noopener noreferrer"&gt;Sign up for a Fastly account&lt;/a&gt; if you haven't already.&lt;/p&gt;

&lt;p&gt;Grab an API token from your account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Account&lt;/strong&gt; &amp;gt; &lt;strong&gt;API tokens&lt;/strong&gt; &amp;gt; &lt;a href="https://manage.fastly.com/account/personal/tokens" rel="noopener noreferrer"&gt;&lt;strong&gt;Personal tokens&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Token&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; anything you like&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; Automation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; Engineer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; Global (deselect the Read-only access box)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access:&lt;/strong&gt; All services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration:&lt;/strong&gt; Never expire&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Copy the token value and save it on your computer&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In your terminal, create a Fastly profile, entering the API token you copied from your account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fastly profile create 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can go ahead and enter the deploy 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 deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accept the defaults and let the CLI create a new service for you, confirming the &lt;code&gt;backend&lt;/code&gt; details from your &lt;code&gt;toml&lt;/code&gt;. Once it's deployed, the output will include the address to try your new app at, which will end &lt;code&gt;edgecompute.app&lt;/code&gt;:&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%2Fkd4oovo1csxicz71l1ug.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%2Fkd4oovo1csxicz71l1ug.png" alt="deployed app details" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open your app in a browser to see the correct time of day indicator:&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%2Fx9lto4d587sftd6lpaag.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%2Fx9lto4d587sftd6lpaag.png" alt="app in browser" width="800" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is indeed afternoon where I am!&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep going
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.fastly.com/blog/rewriting-html-with-the-fastly-javascript-sdk" rel="noopener noreferrer"&gt;Rewriting HTML with the Fastly JavaScript SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://js-compute-reference-docs.edgecompute.app/docs/fastly:html-rewriter/HTMLRewritingStream/" rel="noopener noreferrer"&gt;HTML Rewriter docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fastly.com/documentation/guides/getting-started/domains/securing-domains/tls-quick-start/" rel="noopener noreferrer"&gt;Add a domain to your service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/fastly/an-easy-intro-to-edge-computing-3ced"&gt;An easy intro to edge computing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fastly.com/documentation/guides/compute/developer-guides/javascript/" rel="noopener noreferrer"&gt;Learn more about building Compute apps in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If you get stuck please feel free to post on the &lt;a href="https://community.fastly.com/" rel="noopener noreferrer"&gt;Fastly Community Forum&lt;/a&gt;. 🛟&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>webdev</category>
      <category>cloud</category>
      <category>learning</category>
    </item>
    <item>
      <title>Enabling developers in GitHub Codespaces</title>
      <dc:creator>Sue Smith</dc:creator>
      <pubDate>Tue, 07 Oct 2025 14:09:43 +0000</pubDate>
      <link>https://forem.com/fastly/enabling-developers-in-github-codespaces-1l3a</link>
      <guid>https://forem.com/fastly/enabling-developers-in-github-codespaces-1l3a</guid>
      <description>&lt;p&gt;Over the last few months I’ve been exploring GitHub codespaces for developer enablement. I needed to replace our Fastly onboarding projects that had been hosted on Glitch, and ideally wanted to avoid the need for learners to install local tooling or even sign up for an account before trying our Compute platform. In this post I’ll run through what I’ve discovered about using codespaces for dev product learning.&lt;/p&gt;

&lt;p&gt;Here’s an overview of what our codespace projects do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install dependencies and automatically run an app&lt;/li&gt;
&lt;li&gt;Open previews of the app UI in the codespace&lt;/li&gt;
&lt;li&gt;Deploy apps at the click of a button using bash scripts&lt;/li&gt;
&lt;li&gt;Hide UI elements to minimize distractions&lt;/li&gt;
&lt;li&gt;Use an extension to show custom buttons in the editor for actions like splitting views, sharing app previews, opening the terminal, formatting code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This daft video runs through how they work:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/BJZ3niThD2s"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, what is a codespace?
&lt;/h2&gt;

&lt;p&gt;A codespace is a web editing environment you can open in the browser from a GitHub repository. Codespaces use a version of the VS Code editor, so you can specify editor settings much like you would in a local VS Code installation. Using codespaces lets you carry out development activities without downloading or installing anything onto your local machine – it all happens in the cloud.&lt;/p&gt;

&lt;p&gt;You can access codespaces from the homepage of a GitHub repo, using the &lt;strong&gt;Code&lt;/strong&gt; button to open an environment for editing the codebase on a specific repo branch. &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%2F1095rn65n0a5mlvt4jxp.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%2F1095rn65n0a5mlvt4jxp.png" alt="code button on a repo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Edits made in a codespace are automatically kept for a limited period of time you can set in your GitHub account – you’ll receive a warning if a codespace is about to be deleted and you can export your changes to a branch &lt;a href="https://docs.github.com/en/codespaces/setting-your-user-preferences/configuring-automatic-deletion-of-your-codespaces" rel="noopener noreferrer"&gt;if you aren’t ready to lose them&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Containers
&lt;/h2&gt;

&lt;p&gt;A codespace runs in a Docker container, and you can specify requirements in the repo &lt;code&gt;devcontainer.json&lt;/code&gt; file. I usually include system features like Node (mostly I’m targeting JavaScript projects in the Node ecosystem), but the codespace will guess some dependencies based on the repo content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s an example container spec I’m using for edge computing projects: &lt;a href="https://github.com/glitchdotcom/learn-edge-computing/blob/main/.devcontainer/devcontainer.json" rel="noopener noreferrer"&gt;~learn-edge-computing/devcontainer.json&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;You can attach commands to container lifecycle events – these run when the codespace opens, so you can use them to carry out setup tasks and get the codespace in the state you want for your learners when they open the project. I’ve been using the &lt;code&gt;updateContentCommand&lt;/code&gt; and &lt;code&gt;postAttachCommand&lt;/code&gt; events to install NPM packages and run &lt;code&gt;package.json&lt;/code&gt; scripts I want to execute straight away.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 The &lt;a href="https://containers.dev/implementors/json_reference/" rel="noopener noreferrer"&gt;devcontainer metadata reference&lt;/a&gt; lists the options.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is part of a container config I used for projects originally developed on Glitch that I’d exported to GitHub:&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="nl"&gt;"updateContentCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm install"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"postAttachCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run start || npx --yes serve"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Previews and ports
&lt;/h2&gt;

&lt;p&gt;You can run apps “locally” in the codespace and they’ll be exposed through specific ports. With the Simple Browser you can automatically open these apps right inside the editor at an &lt;code&gt;app.github.dev&lt;/code&gt; address. The &lt;strong&gt;PORTS&lt;/strong&gt; area in the &lt;strong&gt;Terminal&lt;/strong&gt; at the bottom of the editor provides access to port settings, so you can open previews, change visibility, and share URLs with collaborators.&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%2Fctx1omb5piadh9gl4m3v.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%2Fctx1omb5piadh9gl4m3v.png" alt="ports with a preview open"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the container config, I include a section specifying that specific ports should automatically open in the preview. For my edge compute learning experience, it sets both the origin website and edge apps to open from their default ports, with custom labels making them more obvious to the learner:&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="nl"&gt;"portsAttributes"&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;"3000"&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;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🚧 Origin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"onAutoForward"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openPreview"&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;"7676"&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;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🌎 Compute"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"onAutoForward"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openPreview"&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;I also use a custom 🔗 &lt;strong&gt;Share&lt;/strong&gt; button to set a port to public and provide the URL so that the user can copy it to share with collaborators – they can see the preview as long as they’re logged into GitHub.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 If your project is a fully static site and doesn’t use a framework, you can still run a preview using &lt;a href="https://www.npmjs.com/package/serve" rel="noopener noreferrer"&gt;npx serve&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Extensions
&lt;/h2&gt;

&lt;p&gt;You can list required VS Code extensions in your codespace container config and they’ll automatically be installed when someone opens the project. I include &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; because my projects typically use JavaScript, and the extension for custom buttons then runs the format command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom buttons
&lt;/h3&gt;

&lt;p&gt;I’ve found the &lt;a href="https://marketplace.visualstudio.com/items?itemName=jkearins.action-buttons-ext" rel="noopener noreferrer"&gt;VS Code Action Buttons&lt;/a&gt; extension extremely helpful when it comes to customizing the UI for my learning projects. The extension lets you specify buttons that appear along the bottom of the editor. Your buttons can call VS Code editor commands and CLI commands, so I use them to give users an easy way to toggle views, and run bash scripts or &lt;strong&gt;Terminal&lt;/strong&gt; processes.&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%2F39njdw9q97h4c3udz6ut.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%2F39njdw9q97h4c3udz6ut.png" alt="action buttons in the ui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s an excerpt of one of my button lists:&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="nl"&gt;"actionButtons"&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;"commands"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🌈 Prettify"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"singleInstance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"useVsCodeApi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor.action.formatDocument"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tooltip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tidy up your code"&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;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🚀 Publish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"singleInstance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash helpers/publish.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tooltip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Publish your content to Fastly Compute"&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;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;You can include whatever &lt;strong&gt;Terminal&lt;/strong&gt; commands you need for your target frameworks – I tend to separate them into bash scripts so that the user can tweak them more readily if they like.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📋 Here are the commands you can run on button clicks: &lt;a href="https://code.visualstudio.com/api/references/commands" rel="noopener noreferrer"&gt;Built-in Commands&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bash scripts
&lt;/h2&gt;

&lt;p&gt;With the projects I’m working on being experimental, I wanted users to be able to see and tailor the CLI code that automates processing. For that reason I included the bash scripts in a &lt;code&gt;helpers&lt;/code&gt; folder, calling those scripts from the container config and button clicks. The Fastly CLI commands have a little bit of control flow complexity that really needs a lot of testing, so I need to be able to make changes easily.&lt;/p&gt;

&lt;p&gt;Here’s a more basic bash script I use for projects users have imported (and I therefore don’t know for sure which scripts they have in their &lt;code&gt;package.json&lt;/code&gt;):&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;# We're making some guesses about the package scripts lolol&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; package.json &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;printf&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;🤔 No package.json found – try the Open button instead!&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else 
  &lt;/span&gt;&lt;span class="nv"&gt;OOPS&lt;/span&gt;&lt;span class="o"&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;🤬 hmm maybe check what's in your package.json scripts and try "&lt;/span&gt;npm run script-name&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;
  npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OOPS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  npm run start &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OOPS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also use scripts like this to write out instructional information to the user in the &lt;strong&gt;Terminal&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;The Fastly projects support deployment to the edge, so I typically use a 🚀 &lt;strong&gt;Publish&lt;/strong&gt; button that runs the Fastly CLI tooling and deploys the app. Here's  publish script for our Compute Static Publisher: &lt;a href="https://github.com/glitchdotcom/11ty-to-compute/blob/main/helpers/publish.sh" rel="noopener noreferrer"&gt;publish.sh&lt;/a&gt; &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%2Fpks3vezt2f0v582mzhbd.jpg" 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%2Fpks3vezt2f0v582mzhbd.jpg" alt="publish button and terminal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Codespaces make a great editing environment for simple static website projects, and you can publish from the editor, for example if you have a blog. You can deploy to GitHub Pages at the click of a button by including the &lt;a href="https://www.npmjs.com/package/gh-pages" rel="noopener noreferrer"&gt;gh-pages&lt;/a&gt; package in your project, and a script in your &lt;code&gt;package.json&lt;/code&gt;, like this Vite example:&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="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;"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;"vite"&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;"vite build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"serve"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite preview"&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;"gh-pages -d dist"&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;To publish using a button, just call the &lt;code&gt;deploy&lt;/code&gt; command from the custom button extension covered above. I’m using this flow for my own blog and static site projects that I migrated from Glitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment variables
&lt;/h2&gt;

&lt;p&gt;If your platform requires an API key, you can instruct users to acquire and add one inside the codespace. In the &lt;code&gt;README&lt;/code&gt; I include links and steps for grabbing a Fastly API key and adding it using the command palette. When a user adds an environment variable, the codespace will prompt them to reload, which they’ll need to do in order to use the variable in subsequent commands.&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%2Frj3ug477ytochmj8gybw.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%2Frj3ug477ytochmj8gybw.png" alt="manage user secrets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  File and feature visibility
&lt;/h2&gt;

&lt;p&gt;You can specify settings for the VS Code editing experience in your container config just like you would in a local environment. I tend to hide some features to minimize distractions, like the minimap, and files / folders like &lt;code&gt;node_modules&lt;/code&gt;, so that the learner can focus on the core parts of the project I’m trying to familiarize them with.&lt;/p&gt;

&lt;p&gt;You can include these in the settings object in your container JSON:&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="nl"&gt;"settings"&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;"files.exclude"&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;"package-lock.json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"node_modules/"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;"editor.minimap.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;
  
  
  Source control
&lt;/h2&gt;

&lt;p&gt;Although a codespace automatically stores your edits until it’s deleted, it’s a good idea to encourage users to regularly commit their changes to the underlying repo in case they lose them. You can do this in the &lt;strong&gt;Source Control&lt;/strong&gt; area on the left.&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%2F3zaenwy6a386y45czzx2.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%2F3zaenwy6a386y45czzx2.png" alt="source control area"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things can get a bit confusing depending on whether your users have started the codespace from your repo or from a fork of it – I encourage users to create their own fork so that it’s clearer where their changes should be saved.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤔 &lt;em&gt;Something that gets a bit messy with codespaces for dev product onboarding is that the flow seems to be primarily optimized for devs making contributions to a repo rather than forking it to make their own project. If you don’t plan to publish changes you want your users to be able to sync their forks with, you can use template repos instead. This is one place I’ve felt the tension of what I was able to support users to do on Glitch vs this workflow.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Optimizing your repos for codespaces
&lt;/h2&gt;

&lt;p&gt;When you first open a codespace with a custom container config, it can be pretty slow to start up. You can reduce this with &lt;a href="https://docs.github.com/en/codespaces/prebuilding-your-codespaces" rel="noopener noreferrer"&gt;prebuilds&lt;/a&gt; that target specific regions you expect your users to be in. Although the free tier is generous, you might want to keep an eye on the costs for storage and compute associated with your codespaces, and definitely set spending limits in your GitHub account.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 Some of the setup for codespaces can translate to users cloning your repos locally and opening them in VS Code on their computers – this is something I want to explore soon, as well as supporting other web based VS Code environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I found this post from &lt;a href="https://dev.to/blackgirlbytes"&gt;Rizèl Scarlett&lt;/a&gt; super helpful when I was figuring out what I could do: &lt;a href="https://dev.to/github/how-to-run-a-frontend-workshop-in-codespaces-2ede"&gt;How to run a frontend workshop in Codespaces&lt;/a&gt;&lt;/strong&gt; 🙌&lt;/p&gt;

&lt;p&gt;Some official docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/codespaces/quickstart" rel="noopener noreferrer"&gt;Quickstart for GitHub Codespaces&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/docs/devcontainers/create-dev-container" rel="noopener noreferrer"&gt;Create a Dev Container&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of my codespace-optimized projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One I used for people migrating from Glitch: &lt;a href="https://github.com/SueSmith/glitchy-editing/blob/main/.devcontainer/devcontainer.json" rel="noopener noreferrer"&gt;~glitchy-editing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Publishing an 11ty blog to the edge: &lt;a href="https://github.com/glitchdotcom/11ty-to-compute/" rel="noopener noreferrer"&gt;~11ty-to-compute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edge computing intro: &lt;a href="https://github.com/glitchdotcom/learn-edge-computing/" rel="noopener noreferrer"&gt;~learn-edge-computing&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>github</category>
      <category>vscode</category>
      <category>learning</category>
    </item>
    <item>
      <title>Develop an edge computing app in the browser</title>
      <dc:creator>Sue Smith</dc:creator>
      <pubDate>Fri, 19 Sep 2025 09:22:44 +0000</pubDate>
      <link>https://forem.com/fastly/develop-an-edge-computing-app-in-the-browser-1kh5</link>
      <guid>https://forem.com/fastly/develop-an-edge-computing-app-in-the-browser-1kh5</guid>
      <description>&lt;p&gt;When I try a new web technology, I really want to play around with it upfront before installing anything locally or setting up a developer environment. Over the last few months I’ve been exploring GitHub Codespaces for this purpose, working on a set of projects that guide you through building and deploying apps to the network edge straight from the browser. In this post I’ll introduce our new Fastly Compute learning experience that you can try out in seconds.&lt;/p&gt;

&lt;p&gt;With a Compute app, you can enhance the user experience for a website at the edge. The app can manipulate the request from the user and the response from the origin website. You can write your code in various languages and the Fastly SDKs will compile it into Web Assembly (WASM) that can run at the edge, between your users and your website host. In this guide we’re writing our edge application logic in JavaScript.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/A087xT_R6lY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Read on for a more detailed walkthrough of the flow but here’s the gist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You fork a GitHub repo
&lt;/li&gt;
&lt;li&gt;Open your fork in a codespace
&lt;/li&gt;
&lt;li&gt;The app runs and automatically opens a preview
&lt;/li&gt;
&lt;li&gt;The project includes a sample origin website that runs too
&lt;/li&gt;
&lt;li&gt;You tweak the Compute app code and see the preview update as you work
&lt;/li&gt;
&lt;li&gt;When you’re ready, you grab a Fastly API key, copy it into your project, and publish&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fork the project
&lt;/h2&gt;

&lt;p&gt;Getting your own app running takes a couple of clicks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sign into GitHub and visit the &lt;a href="https://github.com/glitchdotcom/learn-edge-computing" rel="noopener noreferrer"&gt;~learn-edge-computing&lt;/a&gt; repo.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fork the repo into your account (best to leave the name unchanged)
&lt;/li&gt;
&lt;li&gt;Open your fork in a codespace by clicking &lt;strong&gt;Code&lt;/strong&gt; and creating a codespace on your main branch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the codespace opens it’ll attempt to do a few things automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build, run, and open a preview for your example origin website
&lt;/li&gt;
&lt;li&gt;Build, run, and preview your initial Compute app
&lt;/li&gt;
&lt;li&gt;You’ll find some buttons along the bottom of the editor to make the process easier

&lt;ul&gt;
&lt;li&gt;Use the 🔎 &lt;strong&gt;Split&lt;/strong&gt; button to separate your preview from your code so that you can edit and see the results at the same time&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get to know your new app
&lt;/h2&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%2F6njbi0j78xckl1anw5y8.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%2F6njbi0j78xckl1anw5y8.png" alt="project in codespace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚧 Your project includes a demo origin website – the code for it is in the &lt;code&gt;origin&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;🌎 The logic for your Compute app is in the &lt;code&gt;src&lt;/code&gt; folder. Check out the content of the &lt;code&gt;index.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;In the Terminal area, select &lt;strong&gt;PORTS&lt;/strong&gt; to see the origin and edge versions of your site – use the buttons to toggle between them in the preview. Check out the differences in functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The style is different because the Compute app switches the stylesheet at the edge.
&lt;/li&gt;
&lt;li&gt;There’s a cookie written into the page that includes geolocation information passed in at the edge.
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ohno&lt;/code&gt; link returns a synthetic HTML page at the edge instead of a default server error.
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;data&lt;/code&gt; link also returns a synthetic page instead of raw JSON.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Develop in the browser
&lt;/h2&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%2F81cbot35q3okzbasyfed.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%2F81cbot35q3okzbasyfed.png" alt="app preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make a change to your Compute app. In the README you’ll find a suggested edit you can copy and paste right into the &lt;code&gt;index.js&lt;/code&gt; file. With any luck your app will automatically rebuild and update in the preview. The edit adds information to the geolocation cookie indicating the time of day at the user location.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the geolocation info won’t be accurate until you deploy to the edge, because your app is initially running “locally” inside the codespace which is running in a container hosted by GitHub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Make changes to your origin app too in the origin folder, like changing the HTML and CSS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can share your draft app with collaborators using the 🔗 &lt;strong&gt;Share&lt;/strong&gt; button at the bottom of the editor – they’ll just need to be signed into GitHub to see it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deploy to the edge
&lt;/h2&gt;

&lt;p&gt;Sign up for a &lt;a href="https://fastly.com/signup" rel="noopener noreferrer"&gt;free Fastly account&lt;/a&gt; and &lt;a href="https://www.fastly.com/documentation/guides/account-info/account-management/using-api-tokens/" rel="noopener noreferrer"&gt;grab an API key&lt;/a&gt;. Pop your key into your codespace with the name &lt;code&gt;FASTLY_API_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Click the 🚀 &lt;strong&gt;Publish&lt;/strong&gt; button at the bottom of the editor and follow the prompts. A few things will happen at this point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Fastly tooling will build your app and package it for deployment
&lt;/li&gt;
&lt;li&gt;It’ll create a new service in your Fastly account and upload the package
&lt;/li&gt;
&lt;li&gt;The scripts in the codespace will also deploy your origin website to a GitHub pages site for your repo
&lt;/li&gt;
&lt;li&gt;The GitHub pages site will be set as the origin for your Compute app&lt;/li&gt;
&lt;/ul&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%2Fifuj7o9edwd9bmmr7972.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%2Fifuj7o9edwd9bmmr7972.png" alt="deploy output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When everything’s ready, you’ll see an &lt;code&gt;edgecompute.app&lt;/code&gt; address in the Terminal output. Go ahead and click it to check out your deployed app. How does it behave differently at the edge?&lt;/p&gt;

&lt;p&gt;You can carry on making edits to your app and hit that 🚀 &lt;strong&gt;Publish&lt;/strong&gt; button whenever you want to go live with them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Remember to commit your changes to your fork of the GitHub repo if you want to keep them – use the &lt;strong&gt;Source Control&lt;/strong&gt; area on the left of the codespace editor.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep going
&lt;/h2&gt;

&lt;p&gt;Curious how the app works? Check out the &lt;code&gt;helpers&lt;/code&gt; and &lt;code&gt;.devcontainer&lt;/code&gt; folders!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.fastly.com/documentation/solutions/tutorials/enhance-ux/" rel="noopener noreferrer"&gt;Learn more including local development&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/fastly/an-easy-intro-to-edge-computing-3ced"&gt;An easy intro to edge computing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.fastly.com/documentation/solutions/tutorials/introduction-to-compute/" rel="noopener noreferrer"&gt;Follow a more detailed intro to Compute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.fastly.com/documentation/solutions/examples/javascript/" rel="noopener noreferrer"&gt;Check out code examples in JavaScript&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.fastly.com/documentation/guides/getting-started/domains/securing-domains/tls-quick-start" rel="noopener noreferrer"&gt;Point your own domain at your site&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>serverless</category>
      <category>javascript</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Publish your website without a host</title>
      <dc:creator>Sue Smith</dc:creator>
      <pubDate>Fri, 29 Aug 2025 10:17:26 +0000</pubDate>
      <link>https://forem.com/fastly/publish-your-website-without-a-host-3c3n</link>
      <guid>https://forem.com/fastly/publish-your-website-without-a-host-3c3n</guid>
      <description>&lt;p&gt;When you use a CDN, cached copies of your website’s static assets are stored at the network edge for fast delivery to your visitors. Well, if your website &lt;em&gt;is&lt;/em&gt; just those assets, why not just dump the whole thing at the edge and serve it directly from there instead of storing it on a web hosting service in the first place? &lt;/p&gt;

&lt;p&gt;That's what the JavaScript Static Publisher for Fastly Compute does. It delivers your static site straight from the network edge using a serverless app to handle user requests, and an edge data store for your content. This kind of &lt;em&gt;originless&lt;/em&gt; setup is well suited to generated static sites made up of HTML, CSS, and client side JavaScript, like blogs and react apps.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/BJZ3niThD2s"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump to:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Try a simple static site&lt;/li&gt;
&lt;li&gt;Publish a project from your computer&lt;/li&gt;
&lt;li&gt;Write and publish a blog from a codespace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can deploy existing sites to Fastly Compute using a few simple commands in your IDE. You can also create a new site, edit, and publish it without installing a thing on your computer by following a guided experience in a GitHub codespace – fork a demo project, open it in the browser, pop in an API key, and publish at the click of a button. &lt;/p&gt;

&lt;h2&gt;
  
  
  Try a simple static site
&lt;/h2&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%2Fjt1ml2vdg8np7rhw8y5f.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%2Fjt1ml2vdg8np7rhw8y5f.png" alt="Website project in codespace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="https://github.com/glitchdotcom/website-to-compute" rel="noopener noreferrer"&gt;~website-to-compute&lt;/a&gt;&lt;/strong&gt; project demonstrates how to publish a simple static site to the edge from a GitHub codespace. &lt;/p&gt;

&lt;p&gt;The steps are in the repo &lt;code&gt;README&lt;/code&gt;, here’s the TL;DR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fork the repo&lt;/li&gt;
&lt;li&gt;Open your forked version for editing in the browser by clicking &lt;strong&gt;Code &amp;gt; Codespaces&lt;/strong&gt; and creating a new codespace on your main branch

&lt;ul&gt;
&lt;li&gt;The codespace config will run a few setup tasks when it first opens, it might take a couple of minutes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Your website preview will open in a tab – click the &lt;strong&gt;🔎 Split&lt;/strong&gt; button at the bottom to see the site side by side with your code

&lt;ul&gt;
&lt;li&gt;Edit your site HTML and CSS! Your preview will update in the editor… 👀&lt;/li&gt;
&lt;li&gt;Share your draft site with collaborators by clicking &lt;strong&gt;🔗 Share&lt;/strong&gt; and copying the URL to your clipboard 📋&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;When you’re ready to publish to Fastly, &lt;a href="http://fastly.com/signup" rel="noopener noreferrer"&gt;sign up for an account&lt;/a&gt; and grab an API key, copying it into your codespace project

&lt;ul&gt;
&lt;li&gt;Click the &lt;strong&gt;🚀 Publish&lt;/strong&gt; button along the bottom of the editor and follow the prompts&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Your &lt;code&gt;edgecompute.app&lt;/code&gt; address will appear in the terminal output&lt;/li&gt;

&lt;li&gt;&lt;em&gt;Make sure you save your edits to GitHub using the &lt;strong&gt;Source Control&lt;/strong&gt; area on the left of the editor&lt;/em&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Publish a project from your computer
&lt;/h2&gt;

&lt;p&gt;If you already have a static site on your computer that you want to deploy, open it in an IDE.&lt;/p&gt;

&lt;p&gt;Import the &lt;a href="https://github.com/fastly/compute-js-static-publish" rel="noopener noreferrer"&gt;Static Publisher&lt;/a&gt; and pass it the root directory to scaffold a Compute app for your site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @fastly/compute-js-static-publish@latest &lt;span class="nt"&gt;--root-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./_site  &lt;span class="nt"&gt;--kv-store-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;site-content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up dependencies:&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;cd &lt;/span&gt;compute-js &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🧪 If you want to test your site locally before deploying, use &lt;code&gt;npm run dev:publish&lt;/code&gt; and &lt;code&gt;npm run dev:start&lt;/code&gt; then open it in the browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you're ready to publish, &lt;a href="https://www.fastly.com/documentation/guides/account-info/account-management/using-api-tokens/" rel="noopener noreferrer"&gt;grab a Fastly API key&lt;/a&gt; and set it as the value of the &lt;code&gt;FASTLY_API_TOKEN&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;Deploy your app:&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 fastly:deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fquvad946wcitbhlc9o8p.jpg" 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%2Fquvad946wcitbhlc9o8p.jpg" alt="Site deploying"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;edgecompute.app&lt;/code&gt; address in the terminal output – it won't show the content yet so don't worry if you see an error message. 😅&lt;/p&gt;

&lt;p&gt;Publish your content to the KV Store:&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 fastly:publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fhpriqy851xaak4g8jk6m.jpg" 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%2Fhpriqy851xaak4g8jk6m.jpg" alt="Publish flow in the cli"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reload your &lt;code&gt;edgecompute.app&lt;/code&gt; URL in the browser and with any luck your content will be there!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Each time you have new content to publish, just run the &lt;code&gt;fastly:publish&lt;/code&gt; command again.&lt;/p&gt;

&lt;p&gt;📚 For more detail &lt;a href="https://www.fastly.com/documentation/solutions/tutorials/publish-static-site/" rel="noopener noreferrer"&gt;check out the tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Write and publish a blog from a codespace
&lt;/h2&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%2F47jxmcnruom7t0mq1gfh.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%2F47jxmcnruom7t0mq1gfh.png" alt="11ty in codespace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="https://github.com/glitchdotcom/11ty-to-compute" rel="noopener noreferrer"&gt;~11ty-to-compute&lt;/a&gt;&lt;/strong&gt; project publishes an Eleventy blog to the edge. You can draft new blog posts in the codespace and publish whenever you’re ready to share them with the world.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/QZM7jJTgZP0"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Check out the &lt;code&gt;README&lt;/code&gt; for steps but here's the gist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fork the repo and open it in a codespace&lt;/li&gt;
&lt;li&gt;Make changes and add blog posts&lt;/li&gt;
&lt;li&gt;Grab a Fastly API key and add it in the codespace&lt;/li&gt;
&lt;li&gt;Hit the &lt;strong&gt;🚀 Publish&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Tell the world!&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 You can share your WIP blog posts for feedback from collaborators using the button at the bottom of the editor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.fastly.com/documentation/guides/getting-started/domains/securing-domains/tls-quick-start/" rel="noopener noreferrer"&gt;Point your own domain at your new site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.to/fastly/an-easy-intro-to-edge-computing-3ced"&gt;Learn about edge computing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Find out more about the &lt;a href="https://github.com/fastly/compute-js-static-publish" rel="noopener noreferrer"&gt;JavaScript Static Publisher for Fastly Compute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learn more about the publication flow in the &lt;a href="https://www.fastly.com/documentation/solutions/tutorials/publish-static-site/" rel="noopener noreferrer"&gt;static site publisher tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🛟 Need help? Post on the &lt;a href="https://community.fastly.com" rel="noopener noreferrer"&gt;Fastly Community forum&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>serverless</category>
      <category>javascript</category>
      <category>devex</category>
    </item>
    <item>
      <title>Go OTEL Traces without the Globals</title>
      <dc:creator>Grant Stephens</dc:creator>
      <pubDate>Thu, 29 May 2025 16:42:09 +0000</pubDate>
      <link>https://forem.com/fastly/go-otel-traces-without-the-globals-f3g</link>
      <guid>https://forem.com/fastly/go-otel-traces-without-the-globals-f3g</guid>
      <description>&lt;p&gt;So I've been doing some work adding OTEL Traces to a Go service and have some notes. The service in question receives requests with trace headers, but also makes calls to other services and so should be injecting headers into subsequent requests too.&lt;/p&gt;

&lt;p&gt;Firstly, the OTEL SDK is vast and takes a while to get your head around. Also, don't think you can just add one plugin for your HTTP router and be done- no such luck unfortunately. The SDK examples make use of global variables in various places which I really don't like. It actually feels like the Prometheus library from a few years ago. Anyway, this guide aims to avoid all the global variables and do things properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;First things first- you're going to need a bunch of things that you instantiate and then pass around your app. I found that the 3 things I needed to pass around were the &lt;code&gt;trace.Tracer&lt;/code&gt;, &lt;code&gt;trace.TracerProvider&lt;/code&gt; and the &lt;code&gt;propagation.TextMapPropagator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The actual setup of each of those looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;otel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;noop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTracerProvider&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;otlptracehttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWithAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SchemaURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myService"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;semconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceVersionKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myVersion"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;traceProvider&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithBatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;sdktrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;traceProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithInstrumentationVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;textMapPropagator&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCompositeTextMapPropagator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceContext&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Baggage&lt;/span&gt;&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;What I then ended up doing is creating a struct with these things in it, plus some other observability items that all get passed around the app together which makes thing a lot cleaner&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;O11y&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Logger&lt;/span&gt;            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;zerolog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;
    &lt;span class="n"&gt;Metrics&lt;/span&gt;           &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PromMetrics&lt;/span&gt;
    &lt;span class="n"&gt;Tracer&lt;/span&gt;            &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tracer&lt;/span&gt;
    &lt;span class="n"&gt;TraceProvider&lt;/span&gt;     &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TracerProvider&lt;/span&gt;
    &lt;span class="n"&gt;TextMapPropagator&lt;/span&gt; &lt;span class="n"&gt;propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextMapPropagator&lt;/span&gt;
&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;This setup will use some environment variables to actually get the OTEL endpoint details. More detail on these can be found in the &lt;a href="https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#general-sdk-configuration" rel="noopener noreferrer"&gt;Environment Variable Specification&lt;/a&gt;. I like this pattern because an endpoint change can happen without an actual code change.&lt;/p&gt;

&lt;p&gt;Now we're ready to start using these somewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP Server
&lt;/h2&gt;

&lt;p&gt;Let's assume that you're using some kind of router package- I'd highly recommend that you then use the OTEL plugin for that package. You can find these plugins in the &lt;a href="https://opentelemetry.io/ecosystem/registry/?component=instrumentation&amp;amp;language=go" rel="noopener noreferrer"&gt;OTEL registry&lt;/a&gt;. In this case we're using the &lt;a href="https://github.com/julienschmidt/httprouter" rel="noopener noreferrer"&gt;Julien Schmidt HTTPRouter&lt;/a&gt; and the plugin is from Splunk (nothing to do with Splunk logging mind you)- &lt;a href="https://github.com/signalfx/splunk-otel-go/tree/main/instrumentation/github.com/julienschmidt/httprouter/splunkhttprouter" rel="noopener noreferrer"&gt;SplunkHTTPRouter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The import path for this is a bit ridiculous, but I like to alias it to know that we're working with the trace version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;  
&lt;span class="n"&gt;tracerouter&lt;/span&gt; &lt;span class="s"&gt;"github.com/signalfx/splunk-otel-go/instrumentation/github.com/julienschmidt/httprouter/splunkhttprouter"&lt;/span&gt;
&lt;span class="o"&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 you actually need to make the router and add it to the server struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tracerouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Router&lt;/span&gt;
    &lt;span class="n"&gt;O11y&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;o11y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;O11y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tracerouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceProvider&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPropagators&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextMapPropagator&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="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One small note here- you may want to exclude some paths from having any tracing on them. This is done as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;OtelReqFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"/healthcheck"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"/metrics"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then pass this in as another option in the &lt;code&gt;tracerouter.New()&lt;/code&gt;, i.e.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tracerouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OtelReqFilter&lt;/span&gt;&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;Now we’re finally getting somewhere and can start adding some spans.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;myRouteHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;    
    &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpanFromContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more thing- error handling is a two step operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"some error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Counter-intuitively the first operation does not actually set the span status. &lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP Client
&lt;/h2&gt;

&lt;p&gt;Now the client is a lot simpler, however because we are not using the global values in the OTEL SDK, we still need to pass in some things in order for trace headers to be injected on outgoing requests.&lt;br&gt;&lt;br&gt;
For each client that you create it has to look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TraceProvider&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="n"&gt;otelhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPropagators&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextMapPropagator&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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Don't use global variables&lt;/li&gt;
&lt;li&gt;Pass around the things your need- there are more than you think for OTEL.&lt;/li&gt;
&lt;li&gt;Use environment variables to set OTEL parameters like sampling percentage. This means you don't need code changes to update them.&lt;/li&gt;
&lt;li&gt;Filter out requests for &lt;code&gt;/metrics&lt;/code&gt; and &lt;code&gt;/healthchecks&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Building an actually secure MCP Server with Fastly Compute</title>
      <dc:creator>Kay Sawada</dc:creator>
      <pubDate>Wed, 28 May 2025 03:56:39 +0000</pubDate>
      <link>https://forem.com/fastly/building-an-actually-secure-mcp-server-with-fastly-compute-33go</link>
      <guid>https://forem.com/fastly/building-an-actually-secure-mcp-server-with-fastly-compute-33go</guid>
      <description>&lt;p&gt;We all know LLMs can do amazing things by now, but deploying them in the real world can mean running into real problems. For example, MCP (Model Context Protocol) is justifiably getting a lot of attention right now for the incredibly creative scenarios it can enable. But the security vulnerabilities MCP can expose should be a genuine nightmare for any organization, not to mention other major concerns like scaling or managing an MCP endpoint over time. But hey, good news: security, scaling, resiliency, and manageability is what we do! So we're going to show you how to use Fastly Compute to make an MCP Server that's ready for the real world — secure, scalable, and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MCP?
&lt;/h2&gt;

&lt;p&gt;The Model Context Protocol (MCP) is an open protocol designed to standardize how applications provide essential context to Large Language Models (LLMs). MCP acts as a universal interface, enabling AI applications to seamlessly connect with diverse data sources.&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%2Fu9x30vmt63t6hwol0vqo.jpg" 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%2Fu9x30vmt63t6hwol0vqo.jpg" alt="MCP architecture diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MCP also provides the flexibility to switch between LLM providers and prioritizes data security within a user's infrastructure by employing a client-server architecture where applications (MCP Hosts) connect to data sources (Local Data Sources and Remote Services) through lightweight MCP Servers and Clients. This unified approach offers pre-built integrations, allowing developers to quickly leverage various data sources. &lt;/p&gt;

&lt;p&gt;We'll cover how to build an MCP server using Fastly Compute, but if you're interested in accessing Fastly's platform API through MCP, the &lt;a href="https://www.npmjs.com/package/fastly-mcp-server" rel="noopener noreferrer"&gt;fastly-mcp-server package&lt;/a&gt; can help, and we'll be talking more about that in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Fastly’s platform makes MCP run better
&lt;/h2&gt;

&lt;p&gt;As an enterprise serverless architect, I often find myself advising customers on the best approach for their workloads, specifically whether they could benefit from edge computing. There are several factors I consider, such as the expected level of concurrency and potential spikes in the workload, and the need for low latency in user-facing applications. And, as you might have guessed, MCP, which we’ll delve into further in this article, is a really good example of a workload that benefits significantly from the unique design of Fastly’s platform and Fastly Compute. The following is a demonstration of the MCP Client in action.&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%2Ffqsqdpf5gshzl31y5pnz.gif" 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%2Ffqsqdpf5gshzl31y5pnz.gif" alt="Demo of running mcp server connecting to Fastly REST API endpoints" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll notice that the LLM agent is performing multiple concurrent calls to MCP tools, such as "GetHistoricalStats" and "GetServiceDetail." If you were responsible for running this MCP server, how much load would you estimate and how would you provision the server infrastructure? For a relatively new environment like MCP, having a massive and complex infrastructure isn't ideal. While serverless is convenient for starting small, you naturally need to avoid common issues like cold starts (without much hassle, of course). Given the possibility of spikes, where multiple users might simultaneously call the MCP tools, it's crucial to choose a scalable environment. Additionally, because communication often occurs via chat, it’s critical to keep response latency as low as possible. &lt;/p&gt;

&lt;p&gt;A noteworthy advantage of using Fastly Compute for cases like this is that its &lt;a href="https://bytecodealliance.org/articles/wasmtime-1-0-fast-safe-and-production-ready" rel="noopener noreferrer"&gt;WebAssembly-based runtime leverages isolation sandbox technology&lt;/a&gt;, enabling safer execution of code both locally and remotely. This means your MCP server is safer running on Fastly. As we’ve seen, Fastly Compute’s design addresses many of MCP’s inherent architectural weaknesses. In the following sections, I'll walk you through the specific process of deploying a remote MCP server using this platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to build a remote MCP server with Fastly Compute
&lt;/h2&gt;

&lt;p&gt;We'll start by deploying a Streamable HTTP endpoint that doesn't include legacy SSE endpoints. This can be easily accomplished using the following steps. Please note that you need to replace the API token in two places within the main.go file before publishing. (For information on obtaining your API token, refer to &lt;a href="https://www.fastly.com/documentation/solutions/tutorials/introduction-to-compute/2-getting-started-with-compute/#creating-a-new-api-token-for-compute" rel="noopener noreferrer"&gt;this guide document&lt;/a&gt;.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git clone https://gist.github.com/d829a6a58ce359b1aa99ecae12ba79f1.git fastly-compute-mcp-server
$ cd fastly-compute-mcp-server
$ vi main.go # Replace __PUT_YOUR_FASTLY_API_TOKEN__ with your own TOKEN
$ fastly compute publish 
...
✓ Activating service (version 1)

Manage this service at:
    https://manage.fastly.com/configure/services/mMnYw4qeGq81xga89Mq8O0

View this service at:
    https://highly-proper-orange.edgecompute.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the publishing process, you'll be asked several questions. Answer "yes" to any y/n prompts, and for all other items, you can simply press “Enter” key to accept the default (empty) values. After a short while, you'll see a message indicating that your service is now available, as shown above. &lt;/p&gt;

&lt;p&gt;Next, let's use the &lt;code&gt;npx&lt;/code&gt; command to launch the MCP inspector verification tool and check if our deployed server is functioning correctly. Once the &lt;code&gt;npx @modelcontextprotocol/inspector&lt;/code&gt; command completes successfully, access &lt;code&gt;http://127.0.0.1:6274&lt;/code&gt; to open the official tester interface. From the left sidebar, select "streamable http" as the connection type and enter the service address (&lt;code&gt;https://*.edgecompute.app&lt;/code&gt;) shown as a result from the publish command.&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%2Fyqy70hde2ypfaksqcfby.gif" 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%2Fyqy70hde2ypfaksqcfby.gif" alt="Demo screen recording of how mcp inspector works" width="1280" height="695"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you see "Connected" below the Connect button in the left sidebar, it means the connection was successful. In the control pane on the right, click on the Tools tab and try actions like List Tools to test the functionality. This means you've successfully operated a remote MCP server that communicates using non-SSE Streamable-HTTP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding support for legacy clients with SSE
&lt;/h2&gt;

&lt;p&gt;As of this writing in May 2025, many MCP clients still do not support Streamable HTTP, and some remote servers only offer SSE transport. To ensure backward compatibility with these clients, we’ll take the MCP server we’ve created a step further by adding SSE support. (Note that in this sample, we will use &lt;a href="https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/" rel="noopener noreferrer"&gt;Fanout&lt;/a&gt; for establishing SSE connections. This is a Fastly push messaging service that requires its own signup.)&lt;/p&gt;

&lt;p&gt;First, create a new service following the same steps as before. However, this time when publishing, create two backends at the following stage of the screen. For the first backend, specify the address of &lt;code&gt;*.edgecompute.app&lt;/code&gt; in the Domain field and name it "self". For the second backend, register &lt;code&gt;api.fastly.com&lt;/code&gt; with the name "fastly" as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ Creating service

Domain: [put-your-favorite-name-here.edgecompute.app] 

Backend (hostname or IP address, or leave blank to stop adding backends): put-your-favorite-name-here.edgecompute.app
Backend port number: [443] 
Backend name: [backend_1] self

Backend (hostname or IP address, or leave blank to stop adding backends): api.fastly.com
Backend port number: [443] 
Backend name: [backend_2] fastly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've published successfully, execute the command shown below as an additional step to enable Fanout. When you see the SUCCESS notification, your setup is ready to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ fastly products --enable=fanout
SUCCESS: Successfully enabled product 'fanout'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, configure your MCP client (like &lt;a href="https://claude.ai/download" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt; or &lt;a href="https://www.cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;) to check functionality. For clients that natively support SSE, there's little difficulty—just follow the app's configuration instructions. In this case, we’ll set up LibreChat, an MCP client that only supports STDIO, as an example. (The following is an example of YAML configuration for LibreChat, but the method of executing the npx command is consistent for any client. Don't forget to use your edgecompute.app service URL when you configure "mcp-remote" command).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mcpServers:
  fastly-mcp-server:
    command: npx
    args:
      - -y
      - "mcp-remote"
      - https://highly-adequate-adder.edgecompute.app/sse
      - --transport
      - sse-only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of May 2025, the mcp-remote package defaults to Streamable-HTTP connections unless you use the &lt;code&gt;--transport sse-only&lt;/code&gt; option. To ensure a connection via SSE, you need to include this option.&lt;/p&gt;

&lt;p&gt;When you launch LibreChat with this configuration, you'll see a screen like the one below. This completes the setup.&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%2Fxmfqof38lra3kprepr77.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%2Fxmfqof38lra3kprepr77.png" alt="The screenshot of the LibreChat UI showing fastly-mcp-server as an available remote MCP server" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This sample shows an MCP tool that downloads the VCL from a Fastly VCL Service, but you can easily extend it with additional features. It’s important to note that &lt;a href="https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/#limits" rel="noopener noreferrer"&gt;Fanout's specification limits message size to about 64KB&lt;/a&gt;. So, when supporting legacy SSE, make sure the result messages from MCP tool calls don't become too large.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: What’s next?
&lt;/h2&gt;

&lt;p&gt;In this article, we've introduced how the flexible and efficient development environment of Fastly Compute simplifies the process of setting up a high-performance MCP server quickly. As a next step, I plan to explore building a more advanced server based on the OAuth specification. Stay tuned!&lt;/p&gt;

&lt;p&gt;We would also love to hear how your organization is leveraging LLMs and AI, and how you are addressing the associated challenges. Please share your thoughts and join the &lt;a href="https://community.fastly.com/?_gl=1*n401xr*_gcl_au*ODgwMjI2NDMuMTc0NTQ3NjU3Mw.." rel="noopener noreferrer"&gt;community&lt;/a&gt; discussions. We welcome all kinds of feedback.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>mcp</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>We should still teach coding</title>
      <dc:creator>Sue Smith</dc:creator>
      <pubDate>Mon, 31 Mar 2025 13:28:22 +0000</pubDate>
      <link>https://forem.com/fastly/we-should-still-teach-coding-3cjh</link>
      <guid>https://forem.com/fastly/we-should-still-teach-coding-3cjh</guid>
      <description>&lt;p&gt;Software written using generative AI is all over the web. Performance and security issues abound. Open source projects are being &lt;a href="https://arstechnica.com/ai/2025/03/devs-say-ai-crawlers-dominate-traffic-forcing-blocks-on-entire-countries/" rel="noopener noreferrer"&gt;overwhelmed by bot traffic&lt;/a&gt;. There's a lot of harm being caused, but as an educator who cares about lowering barriers to software creation, I can't ignore the &lt;a href="https://dev.to/glitch/what-is-worth-learning-41e3"&gt;democratizing potential&lt;/a&gt; of these tools either.&lt;/p&gt;

&lt;p&gt;In this post I’d like to explore the opportunities for learning that this paradigm shift presents – from how to make your projects &lt;a href="https://www.fastly.com/products/ai" rel="noopener noreferrer"&gt;more efficient&lt;/a&gt; and &lt;a href="https://www.fastly.com/products/ddos-protection" rel="noopener noreferrer"&gt;secure&lt;/a&gt;, to learning about the &lt;a href="https://hbr.org/2023/04/generative-ai-has-an-intellectual-property-problem" rel="noopener noreferrer"&gt;intellectual property&lt;/a&gt; and &lt;a href="https://www.technologyreview.com/2022/04/20/1050392/ai-industry-appen-scale-data-labels/" rel="noopener noreferrer"&gt;labor exploitation&lt;/a&gt; that might have gone into training the models you used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding incentives
&lt;/h2&gt;

&lt;p&gt;One thing I have learned working in education is the value of accepting people’s motivations – having a goal that a skill will help you achieve is the most effective motivator for learning. Clearly many people are finding ways to use AI to help them achieve goals.&lt;/p&gt;

&lt;p&gt;Motivations are determined by complex socioeconomic factors beyond our ability to reason away. Let’s instead keep the door to the good internet open by empowering people to make responsible choices, equipping them with knowledge of the technical systems they’re creating within. &lt;/p&gt;

&lt;p&gt;Gen AI might &lt;a href="https://techcrunch.com/2025/03/06/a-quarter-of-startups-in-ycs-current-cohort-have-codebases-that-are-almost-entirely-ai-generated/" rel="noopener noreferrer"&gt;help you spin up an MVP&lt;/a&gt;, but eventually you will have to dig into the code. You’ll either learn software engineering skills, or have to bring in the experts. Far from making engineers obsolete, I worry that less investment in these skills will make the barriers around the profession more extreme. &lt;strong&gt;&lt;em&gt;A select few having insight into how critical systems work is not a future I’m enthusiastic about.&lt;/em&gt;&lt;/strong&gt; Instead I’d like to find learning paths that these new ways of building software present.&lt;/p&gt;

&lt;h2&gt;
  
  
  The conditions for learning are in place
&lt;/h2&gt;

&lt;p&gt;Projects developed using gen AI can support a range of good practices in education, including some key activities that we often neglect in both formal learning and the workplace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Personalization&lt;/strong&gt;: We can work on something meaningful to the learner rather than dragging them through a one-size-fits-all experience.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast feedback loops&lt;/strong&gt;: Automation can fuel adaptive learning through short iterative cycles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reflection&lt;/strong&gt;: Reflecting on an experience helps you internalize skills you’ve acquired and reapply them in different contexts. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Code generated using LLMs enables and requires these perhaps more than “traditional” ways of making software. There are also some &lt;a href="https://teachcomputing.org/pedagogy" rel="noopener noreferrer"&gt;coding pedagogy techniques&lt;/a&gt; that gen AI projects can lend themselves to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reading before writing&lt;/strong&gt; – code comprehension is a crucial software engineering skill, consider an experienced engineer performing code reviews and mentoring junior teammates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting from a functioning application&lt;/strong&gt; instead of a blank slate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running&lt;/strong&gt; an app to see what it does, then &lt;strong&gt;investigating&lt;/strong&gt; how it did it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Taking gradual ownership&lt;/strong&gt; of a project by making a first edit, then turning it into something new.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Generative AI is not an appropriate substitute for the many interpersonal aspects of effective learning IMO. “Mentoring” powered by &lt;a href="https://hai.stanford.edu/news/covert-racism-ai-how-language-models-are-reinforcing-outdated-stereotypes" rel="noopener noreferrer"&gt;biased LLM content&lt;/a&gt; is incredibly dangerous.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Encouraging inquiry and independent thought
&lt;/h2&gt;

&lt;p&gt;I wonder about a gen AI coding exercise that gives you a broken project to fix, or prompts you to explore where the code came from – this kind of inquiry is being used in the humanities to support the development of &lt;a href="https://www.cambridge.org/elt/blog/2023/03/30/enhancing-learners-critical-thinking-skills-with-ai-assisted-technology/" rel="noopener noreferrer"&gt;critical thinking skills&lt;/a&gt;, &lt;em&gt;especially important since these tools might by default &lt;a href="https://www.microsoft.com/en-us/research/publication/the-impact-of-generative-ai-on-critical-thinking-self-reported-reductions-in-cognitive-effort-and-confidence-effects-from-a-survey-of-knowledge-workers/" rel="noopener noreferrer"&gt;erode those very skills&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Then we have the wealth of problems that arise when people deploy applications with little to no understanding of their implementation. What happens over the long term, what happens if your project becomes a &lt;em&gt;real thing&lt;/em&gt; people depend on, what happens when it breaks, what happens when it causes harm, how do we navigate the obfuscation of accountability? These are learning opportunities we’ll embrace if we want people to make more informed choices about how they use technology.&lt;/p&gt;

&lt;p&gt;Software engineering is a very privileged profession, largely because it requires access to education. &lt;a href="https://simonwillison.net/2025/Mar/19/vibe-coding/" rel="noopener noreferrer"&gt;Vibe coding&lt;/a&gt; creates new paths into building with tech. The starting point may be different, but we might even manage to invite more people into the spaces where we shape the future of the web.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Learn how &lt;a href="https://www.fastly.com/blog/ai-innovation-sustainability-key-takeaways-from-ai-action-summit" rel="noopener noreferrer"&gt;Fastly is making AI more sustainable&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Cover image is &lt;a href="https://publicdomainreview.org/collection/march-of-the-intellect/" rel="noopener noreferrer"&gt;The March of Intellect&lt;/a&gt; by Robert Seymour.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>Enhance an 11ty site at the edge</title>
      <dc:creator>Sue Smith</dc:creator>
      <pubDate>Fri, 21 Mar 2025 14:01:26 +0000</pubDate>
      <link>https://forem.com/fastly/enhance-an-11ty-site-at-the-edge-5cgc</link>
      <guid>https://forem.com/fastly/enhance-an-11ty-site-at-the-edge-5cgc</guid>
      <description>&lt;p&gt;In this post we're going to customize the UX in an &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt; site with a &lt;a href="https://www.fastly.com/documentation/solutions/tutorials/enhance-ux/" rel="noopener noreferrer"&gt;Compute app&lt;/a&gt; running on the Fastly network at locations near your users around the world. We'll use the &lt;a href="https://glitchdotcom.github.io/my-site/" rel="noopener noreferrer"&gt;Eleventy Base Blog deployed to GitHub Pages&lt;/a&gt;, with some tweaks to its RSS feed to let us do some fun stuff at the edge. We'll write a JavaScript app that we'll compile into Web Assembly (Wasm) that can run securely on the Fastly network.&lt;/p&gt;

&lt;h2&gt;
  
  
  The website and feed
&lt;/h2&gt;

&lt;p&gt;Eleventy (11ty) generates your website in the form of a bunch of HTML files and some CSS. This makes it a super fast, performant experience for the user – it also makes it perfect for playing with edge computing. You can do lots of cool things with 11ty plugins, like exposing an RSS feed of the posts in your site. We'll set our feed to return JSON so that we can mess with the data at the edge before sending it back to the user. &lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your site
&lt;/h2&gt;

&lt;p&gt;Our fork of the Eleventy Base Blog &lt;a href="https://github.com/glitchdotcom/my-site/blob/d93a9d99d1fd5922a970adca585ada62385fb5e8/eleventy.config.js#L50" rel="noopener noreferrer"&gt;includes a change&lt;/a&gt; in &lt;code&gt;eleventy.config.js&lt;/code&gt; to make the feed return JSON instead of the Atom format.&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%2Fwya5ht1pfemcmtzmqror.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%2Fwya5ht1pfemcmtzmqror.png" alt="The GitHub diff in the eleventy config" width="800" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the example site for your Compute app if you like, but later in this series we'll also try editing the site in conjunction with our edge functionality, so fork your own copy if you plan to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/glitchdotcom/my-site" rel="noopener noreferrer"&gt;Fork the repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Follow the steps in the &lt;code&gt;README&lt;/code&gt; to edit and deploy to GitHub Pages – tl;dr:

&lt;ul&gt;
&lt;li&gt;Edit the &lt;code&gt;eleventy.config.js&lt;/code&gt; &lt;code&gt;feedPlugin&lt;/code&gt; section to set &lt;code&gt;metadata&lt;/code&gt; &lt;code&gt;base&lt;/code&gt; to your own GitHub IO domain&lt;/li&gt;
&lt;li&gt;Change the references to &lt;code&gt;my-site&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt; to your repo name&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;.github/workflows/gh-pages.yml.sample&lt;/code&gt; to &lt;code&gt;.github/workflows/gh-pages.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;gh-pages&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;In your repo &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Pages&lt;/strong&gt; section, choose &lt;strong&gt;Deploy from a branch&lt;/strong&gt; and select &lt;code&gt;gh-pages&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Watch the &lt;strong&gt;Actions&lt;/strong&gt; for your deploy status&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Grab the URL for your new site, as you'll need it in your Compute setup.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your Fastly account
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.fastly.com/signup" rel="noopener noreferrer"&gt;Sign up for a free Fastly account&lt;/a&gt; if you haven't already.&lt;/p&gt;

&lt;p&gt;Grab an API token from your account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Account&lt;/strong&gt; &amp;gt; &lt;strong&gt;API tokens&lt;/strong&gt; &amp;gt; &lt;a href="https://manage.fastly.com/account/personal/tokens" rel="noopener noreferrer"&gt;&lt;strong&gt;Personal tokens&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Token&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; anything you like&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; Automation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; Engineer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; Global (deselect the Read-only access box)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access:&lt;/strong&gt; All services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration:&lt;/strong&gt; Never expire&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Copy the token value and save it on your computer&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set up your developer environment
&lt;/h2&gt;

&lt;p&gt;We'll use the Fastly CLI to spin up a Compute service, so get it installed on your computer:&lt;/p&gt;

&lt;p&gt;Create a new directory:&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;mkdir &lt;/span&gt;11ty-edge &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;11ty-edge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Fastly CLI – &lt;a href="https://www.fastly.com/documentation/reference/tools/cli/" rel="noopener noreferrer"&gt;see the docs if you aren't using NPM&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;  npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @fastly/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a Fastly profile, entering the API token you copied from your account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  fastly profile create 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start a Compute app
&lt;/h2&gt;

&lt;p&gt;We're going to use &lt;a href="https://expressly.edgecompute.app/docs/intro" rel="noopener noreferrer"&gt;Expressly&lt;/a&gt;, which lets us build our Compute app using a similar structure to Node.js server frameworks like Express. We'll listen for user requests, manipulating the request and response we send back to the user.&lt;/p&gt;

&lt;p&gt;Initialize your Compute app using the Expressly starter kit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&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-javascript-expressly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can set the name, description, and author if you like, or accept the defaults.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Include &lt;code&gt;--accept-defaults&lt;/code&gt; with your Fastly commands if you don't want to respond to every prompt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run &lt;code&gt;npm install&lt;/code&gt; when prompted.&lt;/p&gt;

&lt;p&gt;Check your app is installed correctly by running it locally and opening it in your browser:&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 start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F3rl1y40aqf4v8qkkvifm.jpg" 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%2F3rl1y40aqf4v8qkkvifm.jpg" alt="The app running locally" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The local site will just return &lt;code&gt;OK&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore the app
&lt;/h2&gt;

&lt;p&gt;Take a minute to explore the files in the Compute app.&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%2F85vcjbldxily2i3rarn1.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%2F85vcjbldxily2i3rarn1.png" alt="Compute logic in JS" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;src/index.js&lt;/code&gt; file contains our Compute logic including the Expressly routing structures&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;fastly.toml&lt;/code&gt; file contains our app setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll be making changes to both of these files, and the Fastly tooling will change the TOML when we deploy our app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 The &lt;code&gt;bin&lt;/code&gt; and &lt;code&gt;pkg&lt;/code&gt; directories include the compiled assets the CLI will use to deploy your app to Fastly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Set up your origin
&lt;/h2&gt;

&lt;p&gt;At the moment the app doesn't do much, it just returns a default response to the user. Let's get it to return the response from our 11ty website.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;fastly.toml&lt;/code&gt; and add the following at the end of the file, changing the domain to your own GitHub IO subdomain if you're using your own version of the site deployed to GitHub Pages:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[setup]

  [setup.backends]

    [setup.backends.blog]
      address = "glitchdotcom.github.io"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;src/index.js&lt;/code&gt;, add a variable near the top of the script:&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;let&lt;/span&gt; &lt;span class="nx"&gt;backendResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the &lt;code&gt;router.use&lt;/code&gt; function to fetch the response from the origin site, making it &lt;code&gt;async&lt;/code&gt;:&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-powered-by&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="s2"&gt;expressly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;backendResponse&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;fetch&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="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="s2"&gt;blog&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;We set the &lt;code&gt;blog&lt;/code&gt; backend in the TOML, so Fastly will make the request to our origin website.&lt;/p&gt;

&lt;p&gt;Delete the &lt;code&gt;router.get('/'...&lt;/code&gt; and &lt;code&gt;router.post("/submit"&lt;/code&gt; functions, replacing them with one to respond to all requests with the response from the origin site:&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(.*)&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backendResponse&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;Your script should now look something 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="c1"&gt;/// &amp;lt;reference types="@fastly/js-compute" /&amp;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;Router&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/expressly&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;router&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;Router&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;backendResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-powered-by&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="s2"&gt;expressly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;backendResponse&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;fetch&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="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="s2"&gt;blog&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;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(.*)&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backendResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy your app
&lt;/h2&gt;

&lt;p&gt;In the terminal, exit from your local server with &lt;code&gt;CTRL+C&lt;/code&gt; if necessary. Run the deploy 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 deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let the CLI create a new service, accepting the default options or changing any you like. The tooling will build and compile your app, upload the package to Fastly, and activate the service it's attached to. When it's ready, you'll see an &lt;code&gt;edgecompute.app&lt;/code&gt; address in the terminal output – open it in your browser, adding the path for your GitHub repo name (&lt;code&gt;my-site&lt;/code&gt; if you're using the example), like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://informally-one-shrimp.edgecompute.app/my-site/" rel="noopener noreferrer"&gt;informally-one-shrimp.edgecompute.app/my-site/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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%2Fhwbhq2tqg0qvh06mfc1d.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%2Fhwbhq2tqg0qvh06mfc1d.png" alt="Deployed app in IDE" width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right now all the app will do is return the response from the origin website, but we're going to change the behavior for certain requests.&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%2Fiikxbd5d2umk7lx1le7v.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%2Fiikxbd5d2umk7lx1le7v.png" alt="Site deployed in browser" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a synthetic feed page
&lt;/h2&gt;

&lt;p&gt;Click the &lt;strong&gt;Feed&lt;/strong&gt; link in the site header for your deployed app (at the &lt;code&gt;edgecompute.app&lt;/code&gt; address – it just returns a JSON list of the posts in the site, which is &lt;a href="https://glitchdotcom.github.io/my-site/feed/feed.json" rel="noopener noreferrer"&gt;what we get from the origin&lt;/a&gt;.&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%2Fl0kv5cadzk5odrimkasz.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%2Fl0kv5cadzk5odrimkasz.png" alt="JSON feed" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's turn this into a page. Back in your app, in the &lt;code&gt;src/index.js&lt;/code&gt; file, add a new variable near the top of the script, changing the value to the name of your forked 11ty repo if you're using your own version:&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;let&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/my-site/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//change to your repo name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a new endpoint &lt;strong&gt;before&lt;/strong&gt; the &lt;code&gt;router.all("(.*)"&lt;/code&gt; section:&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="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;feed/feed.json`&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;let&lt;/span&gt; &lt;span class="nx"&gt;originData&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;backendResponse&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;``&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// change to your repo name&lt;/span&gt;
  &lt;span class="k"&gt;for &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;pst&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;originData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;date&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date_published&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDateString&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;linkUrl&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;pst&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;posts&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;&amp;lt;a href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;linkUrl&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="s2"&gt;"&amp;gt;&amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt; – &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;`&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;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;!DOCTYPE html&amp;gt;
  &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
      &amp;lt;meta charset="UTF-8"&amp;gt;
      &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1" /&amp;gt;
      &amp;lt;title&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;originData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; – Feed 🗞️&amp;lt;/title&amp;gt;
      &amp;lt;!-- 🚧 Change CSS location to suit your site 🚧 --&amp;gt;
      &amp;lt;link rel="stylesheet" href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;css/index.css"/&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;header&amp;gt;&amp;lt;a class="home-link" href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;My Website&amp;lt;/a&amp;gt;&amp;lt;/header&amp;gt;
      &amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;originData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; – Feed 🗞️&amp;lt;/h2&amp;gt;
      &amp;lt;div&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backendResponse&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="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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 code parses the JSON feed response and builds it into HTML.&lt;/p&gt;

&lt;p&gt;Deploy your app again:&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 deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may take a minute or so for your changes to update, but when your new service version is up, open the feed page again at the &lt;code&gt;edgecompute.app&lt;/code&gt; address, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://informally-one-shrimp.edgecompute.app/my-site/feed/feed.json" rel="noopener noreferrer"&gt;informally-one-shrimp.edgecompute.app/my-site/feed/feed.json&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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%2Fjp4gs7kcntaksenib2yk.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%2Fjp4gs7kcntaksenib2yk.png" alt="Site feed" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we have a web page instead of the JSON data... 🎉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧰 You can access a complete example of the app code in the &lt;a href="https://github.com/glitchdotcom/11ty-feed" rel="noopener noreferrer"&gt;11ty-feed&lt;/a&gt; starter kit and &lt;a href="https://fiddle.fastly.dev/fiddle/dae9ad2b" rel="noopener noreferrer"&gt;Fiddle&lt;/a&gt; if you want to try running the code in the browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;⏳ Stay tuned for the next part of the series we'll add some edge data to the functionality – we can access information about the user, like their location, so we'll build that into our app processing to customize the site behavior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;In the meantime, check out how you can &lt;a href="https://www.fastly.com/documentation/solutions/tutorials/kv-hit-counter/" rel="noopener noreferrer"&gt;build edge data into your apps&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>serverless</category>
      <category>11ty</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Connecting your CDN to Fastly Object Storage</title>
      <dc:creator>Antoine Brossault</dc:creator>
      <pubDate>Tue, 11 Mar 2025 20:38:30 +0000</pubDate>
      <link>https://forem.com/fastly/connecting-your-cdn-to-fastly-object-storage-46n9</link>
      <guid>https://forem.com/fastly/connecting-your-cdn-to-fastly-object-storage-46n9</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;In this article, I'm going to explain how to get started with &lt;a href="https://www.fastly.com/products/storage" rel="noopener noreferrer"&gt;Fastly Object Storage&lt;/a&gt; and connect it to your &lt;a href="https://www.fastly.com/products/cdn" rel="noopener noreferrer"&gt;Fastly CDN&lt;/a&gt; service. In just a few minutes you’ll be up and running with assets stored on Fastly Object Storage and served from your own URL!&lt;/p&gt;

&lt;p&gt;First, what is Fastly Object Storage? It’s an S3 compatible object storage with no egress costs when you connect your Fastly Object Storage as an origin to your CDN service.      &lt;/p&gt;

&lt;p&gt;Fastly Object Storage is always a private bucket, which means you have to provide your CDN service with access and secret keys to connect to it. Therefore, you can’t simply use the object storage URL as the origin. You will need to add a bit of VCL to make it work, but it's not difficult.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To interact with Fastly Object Storage, you will need to create an access key in the Fastly control panel. This key consists of an access key ID and a secret key, which are used to authenticate requests to the S3-compatible API.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to the &lt;a href="http://manage.fastly.com" rel="noopener noreferrer"&gt;&lt;strong&gt;Fastly web interface&lt;/strong&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Resources&lt;/strong&gt; &amp;gt; &lt;a href="https://accounts.fastly.com/realms/fastly/protocol/openid-connect/auth?client_id=manage-fastly-com&amp;amp;redirect_uri=https%3A%2F%2Fmanage.fastly.com%2Fresources%2Fobject-storage&amp;amp;response_type=code&amp;amp;scope=openid&amp;amp;state=771edf4c688b43598753ee69c1e2e0fb&amp;amp;code_challenge=0yJg5k7Cw0r8cM2iDAmbiySKx87zpKd5ffUauQKpty8&amp;amp;code_challenge_method=S256" rel="noopener noreferrer"&gt;&lt;strong&gt;Object Storage&lt;/strong&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Key&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Enter a description and select the scope of access (read or read/write).
&lt;/li&gt;
&lt;li&gt;Be sure to note the access key and secret key, as the secret key will not be visible again.&lt;/li&gt;
&lt;/ol&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%2F767v5mdrkxod4qzebjui.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%2F767v5mdrkxod4qzebjui.png" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pro tip: For better security, you can restrict your API key to only access a specific bucket.&lt;/p&gt;

&lt;p&gt;Now that you have created your keys, make sure you keep them somewhere safe. We are going to use them to create our first bucket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your first bucket and upload files
&lt;/h2&gt;

&lt;p&gt;Currently Fastly does not have a UI for creating a bucket and uploading your first file. However, you can accomplish it by using the AWS S3 CLI or any of the numerous packages and libraries available.&lt;/p&gt;

&lt;p&gt;To make the process as easy as possible for you, &lt;a href="https://github.com/Antoinebr/Fastly-Object-Storage-Demo" rel="noopener noreferrer"&gt;I made a small GUI in Node.js&lt;/a&gt; that allows you to interact with the Fastly Object Storage seamlessly.&lt;/p&gt;

&lt;p&gt;You can install the app with npm or Docker &lt;/p&gt;

&lt;p&gt;Npm :&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-object-storage-easy-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 3009:3009 antoinebr/fastly-object-storage-easy-ui:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the app is installed successfully, you can access it by visiting &lt;a href="http://localhost:3009/" rel="noopener noreferrer"&gt;http://localhost:3009/&lt;/a&gt;.&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%2F7chnztb6l1ww0pfeqlze.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%2F7chnztb6l1ww0pfeqlze.png" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, set your credentials in the UI and select the region where you want your files to be stored, along with the access key and secret key. Once that is done you can start creating your first bucket.&lt;/p&gt;

&lt;p&gt;After creating your first bucket, click on “View files” to open the bucket, then proceed to your first upload.&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%2Fayr4gc9xcnhdzy44ybjx.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%2Fayr4gc9xcnhdzy44ybjx.png" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How to access your files in the buckets ?&lt;/p&gt;

&lt;p&gt;After uploading a file to the bucket, you will not be able to use the bucket URL to access the file directly, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ https://eu-central.object.fastlystorage.app/&amp;lt;bucket-name&amp;gt;/&amp;lt;file-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You either need to use a proxy app which connects to the S3 bucket via API : &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%2Fvtanxd49g9kju5k8oh4s.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%2Fvtanxd49g9kju5k8oh4s.png" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, you can use a proxy app (as shown in the diagram above) and the speed will be limited to ~ 150Mbps and a max 100 req/s per bucket. &lt;/p&gt;

&lt;h2&gt;
  
  
  Use your bucket with your CDN service
&lt;/h2&gt;

&lt;p&gt;The recommended way to use Object Storage is to connect a Fastly CDN service (via VCL) or Compute to your bucket. By doing so, no egress fees will apply, and there are no limitations with respect to speed or requests per second per bucket.&lt;br&gt;&lt;br&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%2F2nrrcmr6cnpu236il1t4.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%2F2nrrcmr6cnpu236il1t4.png" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Integrating with Fastly CDN&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Create a new service&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You may already have an existing service, but for demonstration purposes, I’m going to create a new service: &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%2Fn4e038w50jikxct3x5a4.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%2Fn4e038w50jikxct3x5a4.png" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The important part at this stage is to set the host. In my case, it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eu-central.object.fastlystorage.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you created your bucket in another region, set it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;your-region&amp;gt;.object.fastlystorage.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to inform our CDN service how to interact with our bucket. To do this, we need to add VCL snippets.&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%2Fq3xivs5794ot8bz6pech.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%2Fq3xivs5794ot8bz6pech.png" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;VCL Code for Object Storage Connection&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Below is a VCL code snippet for connecting a Fastly CDN service to an Object Storage bucket. This code handles authentication and request signing for S3-compatible API requests.&lt;/p&gt;

&lt;p&gt;Copy and paste the code in a &lt;code&gt;vcl_miss&lt;/code&gt; (within subroutine).&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%2Ff08r3vu45h54fx34e12t.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%2Ff08r3vu45h54fx34e12t.png" width="800" height="564"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

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

object storage connection

View Source 

Priority: 100

Type: miss

*/

declare local var.awsAccessKey STRING;

declare local var.awsSecretKey STRING;

declare local var.awsS3Bucket STRING;

declare local var.awsRegion STRING;

declare local var.awsS3Host STRING;

declare local var.canonicalHeaders STRING;

declare local var.signedHeaders STRING;

declare local var.canonicalRequest STRING;

declare local var.canonicalQuery STRING;

declare local var.stringToSign STRING;

declare local var.dateStamp STRING;

declare local var.signature STRING;

declare local var.scope STRING;

# Change these values to your own data

set var.awsAccessKey = "your_access_key"; # 👈 Update this

set var.awsSecretKey = "your_secret_key"; # 👈 Update this

set var.awsS3Bucket = "your_bucket_name"; # 👈 Update this

set var.awsRegion = "eu-central"; # 👈 Update this with your region of choice

set var.awsS3Host = var.awsRegion ".object.fastlystorage.app";

if (req.method == "GET" &amp;amp;&amp;amp; !req.backend.is_shield) {

 set bereq.http.x-amz-content-sha256 = digest.hash_sha256("");

 set bereq.http.x-amz-date = strftime({"%Y%m%dT%H%M%SZ"}, now);

 set bereq.http.host = var.awsS3Host;

 set bereq.url = querystring.remove(bereq.url);

 set bereq.url = regsuball(urlencode(urldecode(bereq.url.path)), {"%2F"}, "/");

 set var.dateStamp = strftime({"%Y%m%d"}, now);

 set var.canonicalHeaders = ""

   "host:" bereq.http.host LF

   "x-amz-content-sha256:" bereq.http.x-amz-content-sha256 LF

   "x-amz-date:" bereq.http.x-amz-date LF

 ;

 set var.canonicalQuery = "";

 set var.signedHeaders = "host;x-amz-content-sha256;x-amz-date";

 set var.canonicalRequest = ""

   "GET" LF

   bereq.url.path LF

   var.canonicalQuery LF

   var.canonicalHeaders LF

   var.signedHeaders LF

   digest.hash_sha256("")

 ;

 set var.scope = var.dateStamp "/" var.awsRegion "/s3/aws4_request";

 set var.stringToSign = ""

   "AWS4-HMAC-SHA256" LF

   bereq.http.x-amz-date LF

   var.scope LF

   regsub(digest.hash_sha256(var.canonicalRequest),"^0x", "")

 ;

 set var.signature = digest.awsv4_hmac(

   var.awsSecretKey,

   var.dateStamp,

   var.awsRegion,

   "s3",

   var.stringToSign

 );

 set bereq.http.Authorization = "AWS4-HMAC-SHA256 "

   "Credential=" var.awsAccessKey "/" var.scope ", "

   "SignedHeaders=" var.signedHeaders ", "

   "Signature=" + regsub(var.signature,"^0x", "")

 ;

 unset bereq.http.Accept;

 unset bereq.http.Accept-Language;

 unset bereq.http.User-Agent;

 unset bereq.http.Fastly-Client-IP;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code, update the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set var.awsAccessKey = "your_access_key"; # 👈 Update this

set var.awsSecretKey = "your_secret_key"; # 👈 Update this

set var.awsS3Bucket = "your_bucket_name"; # 👈 Update this

set var.awsRegion = "eu-central"; # 👈 Update this with your region of choice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your snippet and activate your service. &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%2Fknde74xojthtfhmucsk6.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%2Fknde74xojthtfhmucsk6.png" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Access your files :&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;After activating your service, you should be able to access your files from your CDN service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;domain-name&amp;gt;/&amp;lt;bucket-name&amp;gt;/&amp;lt;file-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you managed to follow the instructions up to this point, you should have a working connection between your CDN and Fastly Object Storage! Congrats 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Extra tips :&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To go beyond the basics of connecting your bucket, it's important to optimize delivery and caching performance. Here are a few tips for you:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Enable Segmented Caching for Large Files&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.fastly.com/en/guides/segmented-caching" rel="noopener noreferrer"&gt;Segmented Caching&lt;/a&gt; is essential for serving large files efficiently. It allows Fastly to split resources into smaller chunks, reducing load on the origin server and improving performance for Range requests.&lt;/p&gt;

&lt;p&gt;Example :&lt;/p&gt;

&lt;p&gt;Imagine a video streaming service hosting large videos (e.g., 4 GB movies) on Fastly. A user requests a portion of a video using a Range header to resume playback at 1 GB.&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%2Fo5tdqwerubhbr4vp5y6f.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%2Fo5tdqwerubhbr4vp5y6f.png" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable Segmented Caching, add the following code to the vcl_recv subroutine:&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%2Fkv56ydpdx0zay5mtkbgj.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%2Fkv56ydpdx0zay5mtkbgj.png" width="800" height="100"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Enable Segmented Caching for large files

if (req.url.ext == "mp4") {

 set req.enable_segmented_caching = true;

 # Consider changing the segment size to reduce volume of requests going back to origin

 # The default is 1 MB. This would generate a 100 requests to origin for 100 MB file

 # or 1000 requests for a 1 GB file. The maximum size is 20 MB or 20971520 bytes

 # set segmented_caching.block_size = 20971520;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Enable Streaming Miss&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.fastly.com/en/guides/streaming-miss" rel="noopener noreferrer"&gt;Streaming Miss&lt;/a&gt; feature ensures that responses are streamed back to the client immediately, reducing first-byte latency.&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%2Fstmggxgfdc9vln236coa.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%2Fstmggxgfdc9vln236coa.png" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable Streaming Miss, add the following code to the vcl_fetch subroutine:&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%2F03cpdjm4n5trl2rwow4f.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%2F03cpdjm4n5trl2rwow4f.png" width="800" height="100"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
Streaming Miss Feature
View Source 
Priority: 100
Type: fetch
*/

# Streaming Miss does not work with Gzip so you may want to wrap this in a condition
# based on file types that are likely to be Gzipped for example streaming manifests (m3u8 and mpd)
set beresp.do_stream = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Join the conversation in our forum
&lt;/h3&gt;

&lt;p&gt;We would like to hear how you are using Fastly Object Storage. Be sure to &lt;a href="https://community.fastly.com/c/object-storage/23" rel="noopener noreferrer"&gt;visit our forum&lt;/a&gt; to let us know how we can support you better and to stay up-to-date on new capabilities released for Fastly Object Storage.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Announcing the Pub/Sub Compute app</title>
      <dc:creator>Justin Karneges</dc:creator>
      <pubDate>Wed, 26 Feb 2025 19:44:25 +0000</pubDate>
      <link>https://forem.com/fastly/announcing-the-pubsub-compute-app-35ki</link>
      <guid>https://forem.com/fastly/announcing-the-pubsub-compute-app-35ki</guid>
      <description>&lt;p&gt;We are excited to announce the &lt;a href="https://github.com/fastly/pubsub" rel="noopener noreferrer"&gt;Pub/Sub app&lt;/a&gt; for Fastly Compute, making it easy to push real-time updates to browsers and other devices. Messages are delivered via the Server-Sent Events or MQTT protocols. Check out &lt;a href="https://pubsub-test.glitch.me/" rel="noopener noreferrer"&gt;the demo&lt;/a&gt; to see it in action, and read on to learn how to set up your own instance in minutes!&lt;/p&gt;

&lt;p&gt;The Pub/Sub app is one of the first user-installable apps provided by Fastly that is production ready out of the box. It is not a template or starter kit that requires modification. Just install and go. Of course, since the code is open source, you are free to fork it and add your own functionality. We also welcome contributions.&lt;/p&gt;

&lt;p&gt;You may know that Fastly also offers powerful real-time messaging capability via &lt;a href="https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/" rel="noopener noreferrer"&gt;Fanout&lt;/a&gt;, however Fanout is designed for API operators with rigid requirements around messaging protocols, authentication, and storage. If you aren’t building or operating a push API and you simply want to send messages to browsers connected to your own website, then the Pub/Sub app is for you. Fun fact: the Pub/Sub app is built on top of Fanout!&lt;/p&gt;

&lt;p&gt;The Pub/Sub app is highly scalable and there are no hard limits. It supports millions of concurrent connections and channels, and there is no limit to the number of subscriptions a single channel can have. Even though the app is user-installable, there is nothing to manage. Fastly runs the app entirely serverlessly, worldwide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;To get started, clone the repo and use the &lt;code&gt;fastly&lt;/code&gt; tool to deploy it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/fastly/pubsub.git
cd pubsub
fastly compute publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming the &lt;code&gt;fastly.toml&lt;/code&gt; file hasn't been modified, the above command will create a new Compute service and set up any related resources.&lt;/p&gt;

&lt;p&gt;After that, enable Fanout on the service:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Add a backend called "self" to your service, for directing Fanout-managed requests back to the service itself. Replace &lt;code&gt;{DOMAIN}&lt;/code&gt; with the domain selected during deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastly backend create \
  --name self \
  --address {DOMAIN} \
  --port 443 \
  --version latest --autoclone
fastly service-version activate --version latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Determine the ID of the "secrets" Secret Store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastly resource-link list --version latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for a block of output where the Resource Type is &lt;code&gt;secret-store&lt;/code&gt; and the Name is whatever name you gave to the "secrets" Secret Store. The Resource ID is the store's ID.&lt;/p&gt;

&lt;p&gt;Create a Fastly API token with the ability to publish Fanout messages. Do this by going to the Fastly management panel -&amp;gt; Accounts -&amp;gt; API tokens -&amp;gt; Personal tokens -&amp;gt; Create Token. Select Automation type, &lt;code&gt;purge_select&lt;/code&gt; scope, limited to the service, and never expiring, and click Create Token. Then save the token in the above store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastly secret-store-entry create \
  -s {STORE_ID} \
  --name publish-token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will prompt for the token value, which you can paste in.&lt;/p&gt;

&lt;p&gt;Congrats, you have a pub/sub service! Next, we'll discuss how to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Messages can be sent and received over HTTP or MQTT, and access must be authorized using tokens. In this section, we'll discuss how to send messages with HTTP.&lt;/p&gt;

&lt;p&gt;First, you'll need a token signing key. Create one by sending a POST to the app's &lt;code&gt;/admin/keys&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl \
  -X POST \
  -H "Fastly-Key: $FASTLY_API_TOKEN" \
  https://{DOMAIN}/admin/keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app will respond with a key ID and value. Note them in a safe place. The value is used for signing JWTs. The ID must be included in the &lt;code&gt;kid&lt;/code&gt; header field of the JWTs.&lt;/p&gt;

&lt;p&gt;Once you have a signing key, you can create authorization tokens for subscribers and publishers as needed. Below is an example using Python and the PyJWT library to create a token allowing access to a topic named "test". Replace &lt;code&gt;{KEY_ID}&lt;/code&gt; and &lt;code&gt;{KEY_VALUE}&lt;/code&gt; with your key ID and value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-fastly-read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-fastly-write&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{KEY_VALUE}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{KEY_ID}&lt;/span&gt;&lt;span class="sh"&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 that you have a token, you can subscribe a client. Here's an example using curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl \
  -H "Authorization: Bearer $TOKEN" \
  "https://{DOMAIN}/events?topic=test"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command establishes a never-ending response body that the client can receive messages over.&lt;/p&gt;

&lt;p&gt;And here's how to publish a message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"text":"hello world"}'
  "https://{DOMAIN}/events?topic=test"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the above command, the earlier curl should output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event: message
data: {"text":"hello world"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For further details, including how to use the app with MQTT, see the &lt;a href="https://github.com/fastly/pubsub/blob/main/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions/Comments?
&lt;/h2&gt;

&lt;p&gt;Feel free to file issues on the &lt;a href="https://github.com/fastly/pubsub" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for specific code related bugs or features, or chat with us on the &lt;a href="https://community.fastly.com/t/announcing-fastlys-official-pubsub-application/3876" rel="noopener noreferrer"&gt;Fastly Community Forum&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pubsub</category>
      <category>serverless</category>
      <category>rust</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Private, cost-effective cloud storage that you will want to use!</title>
      <dc:creator>Jaskirat Randhawa</dc:creator>
      <pubDate>Wed, 05 Feb 2025 21:18:44 +0000</pubDate>
      <link>https://forem.com/fastly/private-cost-effective-cloud-storage-that-you-will-want-to-use-1495</link>
      <guid>https://forem.com/fastly/private-cost-effective-cloud-storage-that-you-will-want-to-use-1495</guid>
      <description>&lt;p&gt;If you are like me who wants private, affordable and easy to use storage, then &lt;a href="https://www.fastly.com/products/storage" rel="noopener noreferrer"&gt;Fastly Object Storage&lt;/a&gt; might be for you. It is an S3 compatible object storage with zero egress fees! Let’s take a look at how you can set it up and use S3 compatible tools to interact with your data. In this demo, I’ll walk you through connecting one of the popular tools &lt;a href="https://sftpgo.com/" rel="noopener noreferrer"&gt;SFTPGo&lt;/a&gt;, with Fastly Object Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This demo assumes that you have&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Fastly account with Fastly Object storage&lt;/li&gt;
&lt;li&gt;A Fastly API Token set locally as &lt;code&gt;$FASTLY_API_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AWS CLI and Docker installed locally on your machine&lt;/li&gt;
&lt;/ol&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%2Fhvi6nrnbigwbcrb88m2b.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%2Fhvi6nrnbigwbcrb88m2b.png" alt="Fastly Object Storage product page" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an Object Storage Key and a Bucket
&lt;/h2&gt;

&lt;p&gt;Once you have the object storage available in your account, create an access key in the Fastly web interface. It consists of an access key ID and a secret key that is used to authenticate requests made to the Fastly Object Storage.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to the Fastly web interface.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Resources&lt;/strong&gt; &amp;gt; Object Storage.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter a description and select the scope of access &lt;em&gt;(Note: I am using read-write-admin for this demo.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Note the access key and secret key, as the secret key will not be visible again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, if you have a Fastly API token, you can use curl to create an access key for object storage.&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;export &lt;/span&gt;&lt;span class="nv"&gt;FASTLY_API_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xxxxx

&lt;span class="c"&gt;# Create an access key using Fastly API token.&lt;/span&gt;
&lt;span class="c"&gt;# It will create AWS_ACCESS_KEY and AWS_SECRET_KEY set of keys&lt;/span&gt;
curl &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Fastly-Key: &lt;/span&gt;&lt;span class="nv"&gt;$FASTLY_API_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.fastly.com/resources/object-storage/access-keys &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "description": "Demo access key",
  "permission": "read-write-admin",
  "buckets": ["sftpgo-bucket"]
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure AWS CLI and create a new Fastly profile &lt;code&gt;nano ~/.aws/config&lt;/code&gt;. You can pick one of three available regions: us-west, us-east, or eu-central.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[default]
region = us-west

[profile fastly]
region = us-west
endpoint_url = https://us-west.object.fastlystorage.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are some commands to configure the AWS CLI to work with Fastly Object Storage. Let’s create a bucket &lt;code&gt;sftpgo-bucket&lt;/code&gt; that we will connect our SFTPGo client to in the next steps. Feel free to play around with using AWS CLI to interact with Fastly Object Storage. We will be able to see all the contents stored in the bucket using the SFTPGo client.&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;# Configure the AWS CLI&lt;/span&gt;
aws configure &lt;span class="nt"&gt;--profile&lt;/span&gt; fastly

&lt;span class="c"&gt;# Create a bucket&lt;/span&gt;
aws s3 mb s3://sftpgo-bucket &lt;span class="nt"&gt;--profile&lt;/span&gt; fastly

&lt;span class="c"&gt;# List buckets&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--profile&lt;/span&gt; fastly

&lt;span class="c"&gt;# Upload a file&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; ./sample.txt s3://sftpgo-bucket/ &lt;span class="nt"&gt;--profile&lt;/span&gt; fastly

&lt;span class="c"&gt;# List contents of a bucket&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;sftpgo-bucket &lt;span class="nt"&gt;--profile&lt;/span&gt; fastly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using SFTPGo is as easy as 1-2-3
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is SFTPGo?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SFTPGo is one of my favorite file management solutions that supports S3 as a backend. One of the best things about it is that you can store and share files with your personal contacts or work colleagues with a convenient link sharing function. Advanced users can also find comfort in its abilities to create and manage users, folders, mounts and other resources. You can also go a step further to set up two-factor authentication.&lt;/p&gt;

&lt;p&gt;We’ll keep it simple. All you have to do is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install SFTPGo&lt;/li&gt;
&lt;li&gt;Add Fastly Object Storage as a backend&lt;/li&gt;
&lt;li&gt;Done! You have a working setup.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are multiple ways to install SFTPGo. I’m going to use docker-compose with minimal configuration. It’ll take less than 2 minutes to get up and running.&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sftpgo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;drakkan/sftpgo:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;some-sftpgo&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2022:2022"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sftpgodata:/srv/sftpgo&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sftpgohome:/var/lib/sftpgo&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sftpgodata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sftpgohome&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;docker-compose.yaml&lt;/code&gt; file and run &lt;code&gt;docker-compose up -d&lt;/code&gt; to spin your service.&lt;/p&gt;

&lt;p&gt;This will create a barebones SFTPGo server for you. You have plenty of &lt;a href="https://docs.sftpgo.com/2.6/" rel="noopener noreferrer"&gt;customization options&lt;/a&gt; depending on your needs. Next, launch &lt;a href="http://127.0.0.1:8080/web/admin/login" rel="noopener noreferrer"&gt;127.0.0.1:8080/web/admin/login&lt;/a&gt; and run through the first time setup for the administrator. Provide the username and password that you’ll use to manage this instance. Note: Don’t worry if you lose these credentials or docker volume. Your data will still remain safe in Fastly Object Storage, because we’re using it as a backend.&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%2F53iga41qbbs9ggivjrw7.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%2F53iga41qbbs9ggivjrw7.png" alt="SFTPGo admin login" width="800" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The wonderful thing about this sort of a setup is that you can host your content without needing a degree in server management. It’s essentially your very own digital attic. You get to control everything. Now, you’re thinking, "Wait, I don’t want to mess around with code and stuff. I just want to store some content." No problem. Setting up Fastly Object Storage with SFTPGo is quite simple. You’re not building a spaceship here—just a safe space for your assets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Here’s the breakdown:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Create a new user account&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you log into the WebAdmin console, go to &lt;strong&gt;Users&lt;/strong&gt; and click on &lt;strong&gt;Add&lt;/strong&gt; to create a new user account. This is the simplest way to get started. You can, of course, do more than that like creating groups and virtual folders. &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%2Futyigqgkrlluo94h072w.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%2Futyigqgkrlluo94h072w.png" alt="SFTPGo user management" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configure the user account with following settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage: S3 (Compatible)&lt;/li&gt;
&lt;li&gt;Bucket: Your bucket name&lt;/li&gt;
&lt;li&gt;Region: us-west&lt;/li&gt;
&lt;li&gt;Access Key: Generated earlier using Fastly API&lt;/li&gt;
&lt;li&gt;Access Secret: Generated earlier using Fastly API&lt;/li&gt;
&lt;li&gt;Endpoint: &lt;a href="https://us-west.object.fastlystorage.app" rel="noopener noreferrer"&gt;https://us-west.object.fastlystorage.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Toggle on the option for &lt;code&gt;Use path-style addressing&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&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%2Fppzolraa7s8q07bxpkpx.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%2Fppzolraa7s8q07bxpkpx.png" alt="SFTPGo user configuration" width="800" height="1329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done. Now test your setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="http://127.0.0.1:8080/web/client/login" rel="noopener noreferrer"&gt;127.0.0.1:8080/web/client/login&lt;/a&gt; and log in with the credentials of the user that you just created. You should be able to see the contents of your storage bucket. Violá! You can now use the UI to upload, download and share your files. &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%2F68gtnivhkkbyzxukvw9y.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%2F68gtnivhkkbyzxukvw9y.png" alt="SFTPGo web client" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To share files, click on options for a file or a folder. Then click on &lt;strong&gt;Share&lt;/strong&gt;. It will prompt you with a few options to create a Share. &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%2F2lw8ulw8f3jrric0wqlv.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%2F2lw8ulw8f3jrric0wqlv.png" alt="SFTPGo add share" width="800" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve created a Share, you can go to Share in the left sidebar and get the options for viewing and managing your shares. Select the one you created and it’ll provide you a few links that you can pass along to others. &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%2Fd4as1y8mhynjjdy8d6t1.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%2Fd4as1y8mhynjjdy8d6t1.png" alt="SFTPGo share links" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy to use storage that’s more private than your secret snack stash
&lt;/h2&gt;

&lt;p&gt;So, there you have it: a private cloud storage that’s simple to set up, easy to share with your friends and colleagues (okay, family too). Once you set up the SFTPGo instance, you don’t need to manage any code. The best part? Using Fastly Object storage, you only pay for what you actually use. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tell us how are you’re using Fastly Object Storage
&lt;/h2&gt;

&lt;p&gt;I’m a product manager at Fastly and I would like to hear more about how you are using Fastly Object Storage for your personal or work life. What tools are you using? What tools would you like us to support? Join the conversation in our forum, and &lt;a href="https://www.fastly.com/signup?tier=free" rel="noopener noreferrer"&gt;get started for free&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Go Beyond the Basics: Connecting Compute to Object Storage with the Official S3 Clients</title>
      <dc:creator>Kay Sawada</dc:creator>
      <pubDate>Fri, 24 Jan 2025 21:42:24 +0000</pubDate>
      <link>https://forem.com/fastly/go-beyond-the-basics-connecting-compute-to-object-storage-with-the-official-s3-clients-4l9p</link>
      <guid>https://forem.com/fastly/go-beyond-the-basics-connecting-compute-to-object-storage-with-the-official-s3-clients-4l9p</guid>
      <description>&lt;p&gt;Fastly Compute has consistently supported developers through its SDK with strong APIs for persistence needs, such as Config, Secret, and KV Store. Recently, we introduced Fastly Object Storage, an S3-compatible storage solution, which further streamlines the process of building systems on Fastly's Edge Network. With these advancements, the question arises: how best to connect Compute to Fastly Object Storage, particularly in terms of the most efficient connection methods? Fastly Object Storage is designed for S3-compatible API access, making it possible to connect from Compute via REST api call. But what's the most efficient way to do this?&lt;/p&gt;

&lt;p&gt;In this article, I will guide you through using the official AWS SDKs to provide straightforward and intuitive access to Fastly Object Storage's API-compatible storage from Compute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the official AWS SDK with Fastly Compute
&lt;/h2&gt;

&lt;p&gt;Let's dive into some concrete code examples. In this first example, we're using the Compute Go SDK, which launched &lt;a href="https://www.fastly.com/blog/announcing-standard-go-support-for-fastly-compute" rel="noopener noreferrer"&gt;its official support in the summer of last year&lt;/a&gt;, along with AWS's official SDK(&lt;a href="https://github.com/aws/aws-sdk-go-v2" rel="noopener noreferrer"&gt;aws/aws-sdk-go-v2&lt;/a&gt;) to connect with Fastly Object Storage. What makes this approach particularly effective is its use of AWS's widely-adopted standard SDK, which enables intuitive handling of S3 objects. Developers familiar with this library can quickly access it with only a few lines of code, and even those new to the SDK will find it more efficient compared to directly handling the REST API. This efficiency is due to the well-developed documentation and the type hint and suggestions offered by the SDK. Additionally, &lt;a href="https://github.com/aws/aws-sdk-go-v2" rel="noopener noreferrer"&gt;aws/aws-sdk-go-v2&lt;/a&gt; is particularly convenient because it supports interfaces for other AWS services beyond S3, such as RDS and Bedrock, which means this code can be adapted for use with other AWS services, too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="s"&gt;"context"&lt;/span&gt;
   &lt;span class="s"&gt;"fmt"&lt;/span&gt;
   &lt;span class="s"&gt;"net/http"&lt;/span&gt;
   &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/aws"&lt;/span&gt;
   &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/credentials"&lt;/span&gt;
   &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/service/s3"&lt;/span&gt;
   &lt;span class="s"&gt;"github.com/fastly/compute-sdk-go/fsthttp"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;BUCKET_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_BUCKET_NAME"&lt;/span&gt;
   &lt;span class="n"&gt;ACCESS_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_ACCESS_KEY"&lt;/span&gt;
   &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_SECRET_KEY"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;fsthttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;fsthttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fsthttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c"&gt;// connect to s3&lt;/span&gt;
     &lt;span class="n"&gt;customClient&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fsthttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"origin_0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="s"&gt;"us-east"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;BaseEndpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://us-east.object.fastlystorage.app"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCredentialsCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStaticCredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
       &lt;span class="n"&gt;UsePathStyle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;HTTPClient&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;customClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;})&lt;/span&gt;
     &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewListObjectsV2Paginator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListObjectsV2Input&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;

     &lt;span class="c"&gt;// generate response html&lt;/span&gt;
     &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html; charset=utf-8"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;ul&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasMorePages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NextPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contents&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;li&amp;gt;Object:%s&amp;lt;/li&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&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="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;/ul&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;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;h2&gt;
  
  
  The Secret Sauce
&lt;/h2&gt;

&lt;p&gt;There's one crucial implementation detail worth highlighting in our example: we're injecting a custom client as the http.Client used by the AWS SDK, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;       &lt;span class="n"&gt;customClient&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fsthttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"origin_0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="o"&gt;...&lt;/span&gt;
           &lt;span class="n"&gt;HTTPClient&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;customClient&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;When using external libraries within Fastly Compute, securing access to external system resources, such as file I/O or network operations, sometimes presents a challenge. For this scenario, the critical aspect is determining how to enable network communication. Thanks to Go's excellent abstraction in the &lt;a href="https://pkg.go.dev/net/http" rel="noopener noreferrer"&gt;net/http standard library&lt;/a&gt;, particularly the RoundTripper interface, it's relatively straightforward to ensure compatibility with external network resources. In fact, many libraries in Go are designed to allow the injection of a custom http.Client, which is advantageous in WebAssembly (WASI) environments, offering significant potential to leverage existing code assets. This adaptability is a notable strength of Go's Wasm target.&lt;/p&gt;

&lt;p&gt;Note: To run the code demonstrated here, you'll need to configure a backend named “origin_0” with an endpoint URL in the format &lt;code&gt;https://us-east.object.fastlystorage.app/&lt;/code&gt; (No host override configuration is required).&lt;/p&gt;

&lt;h2&gt;
  
  
  What about JavaScript and Rust?
&lt;/h2&gt;

&lt;p&gt;Of course we can connect with them, too! For JavaScript, in fact it’s much easier than the Go code above. All you need is to install &lt;a href="https://github.com/aws/aws-sdk-js-v3" rel="noopener noreferrer"&gt;the official AWS JS SDK&lt;/a&gt; by &lt;code&gt;npm install @aws-sdk/client-s3&lt;/code&gt; and add the following import statement at the top of the script;&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;paginateListBuckets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;S3ServiceException&lt;/span&gt;&lt;span class="p"&gt;,&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;@aws-sdk/client-s3&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;/div&gt;



&lt;p&gt;Followed by some s3 operation like below;&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;client&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://us-east.object.fastlystorage.app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_ACCESS_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_SECRET_KEY&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buckets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &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;page&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;paginateListBuckets&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;client&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;buckets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buckets&lt;/span&gt;&lt;span class="p"&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;buckets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;bucket&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;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&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="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&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;/div&gt;



&lt;p&gt;Lastly, in case of Rust, just like we did with Go SDK, we need a custom http client to inject to &lt;a href="https://github.com/awslabs/aws-sdk-rust" rel="noopener noreferrer"&gt;the AWS SDK for Rust&lt;/a&gt; to make its network layer functional with wasm32-wasi compilation target. A critical consideration for this work (the most challenging aspect) would be that the AWS SDK for Rust has &lt;a href="https://docs.aws.amazon.com/sdk-for-rust/latest/dg/fundamentals.html" rel="noopener noreferrer"&gt;a dependency on Tokio&lt;/a&gt;, which requires developers not only to implement asynchronous coding patterns but also to handle dependencies carefully.&lt;/p&gt;

&lt;p&gt;For easier adoption of this SDK, I’d recommend you to visit this awesome repository(&lt;a href="http://github.com/tidal-music/aws-fastly-http-client" rel="noopener noreferrer"&gt;github.com/tidal-music/aws-fastly-http-client&lt;/a&gt;) created by Tidal Music, which enables you to have the AWS Rust SDK connect with backend servers through custom http client instantiated from FastlyHttpClient with ease. Simply git clone the project, and add your code and dependencies needed for your project - the final code should look something like below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[tokio::main(flavor&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"current_thread"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;custom_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;FastlyHttpClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"origin_0"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;aws_sdk_s3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="nf"&gt;.region&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"us-east"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
     &lt;span class="nf"&gt;.endpoint_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://us-east.object.fastlystorage.app/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nf"&gt;.stalled_stream_protection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;StalledStreamProtectionConfig&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
     &lt;span class="nf"&gt;.identity_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;IdentityCache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;no_cache&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
     &lt;span class="nf"&gt;.credentials_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nn"&gt;aws_sdk_s3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
     &lt;span class="nf"&gt;.http_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;custom_client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nf"&gt;.behavior_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;BehaviorVersion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;v2024_03_28&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
     &lt;span class="nf"&gt;.force_path_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;aws_sdk_s3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_conf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
     &lt;span class="nf"&gt;.list_buckets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="k"&gt;.await&lt;/span&gt;
     &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unable to list buckets"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.buckets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"no bucket name"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bucket_name:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nn"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.send_to_client&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;Side note; There's a caveat with this Rust solution - you need to use fastly crate version 0.9.x alongside Rust compiler version 1.79.0. If you upgrade the Fastly crate version, you'll encounter compilation errors (e.g. type mismatch etc), as of writing this.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;The approach we've demonstrated using these AWS SDKs is just one way to connect Fastly Compute to Fastly Object Storage. The flexibility of S3 client options, combined with the injection pattern of custom http clients, opens up a world of possibilities for leveraging existing code assets and achieving intuitive development at the edge.&lt;/p&gt;

&lt;p&gt;I encourage you to take advantage of these SDKs and libraries to enable smooth integration with Fastly Object Storage and other services, and set the stage for your next wave of innovation. Also, don’t forget to visit &lt;a href="https://community.fastly.com/c/object-storage/23" rel="noopener noreferrer"&gt;the forum&lt;/a&gt; and stay informed about the latest advancements of Fastly Object Storage. We welcome your contributions and look forward to seeing the innovative solutions you develop!&lt;/p&gt;

</description>
      <category>fastly</category>
      <category>serverless</category>
      <category>webassembly</category>
      <category>s3</category>
    </item>
  </channel>
</rss>
