
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Thu, 09 Apr 2026 20:34:48 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Keep track of Workers’ code and configuration changes with Deployments]]></title>
            <link>https://blog.cloudflare.com/deployments-for-workers/</link>
            <pubDate>Thu, 17 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ Today we’re happy to announce Deployments for Workers. Deployments allow developers to audit changes made to their applications. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today we’re happy to introduce Deployments for Workers. Deployments allow developers to keep track of changes to their Worker; not just the code, but the configuration and bindings as well. With deployments, developers now have access to a powerful audit log of changes to their production applications.</p><p>And tracking changes is just the beginning! Deployments provide a strong foundation to add: automated deployments, rollbacks, and integration with version control.</p><p>Today we’ll dive into the details of deployments, how you can use them, and what we’re thinking about next.</p>
    <div>
      <h3>Deployments</h3>
      <a href="#deployments">
        
      </a>
    </div>
    <p>Deployments are a powerful new way to track changes to your Workers. With them, you can track who’s making changes to your Workers, where those changes are coming from, and when those changes are being made.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6qNd7JL2HjmxbVVNqK83hQ/05929c250e068877efc5f92da0d9fed9/image2-43.png" />
            
            </figure><p>Cloudflare reports on deployments made from wrangler, API, dashboard, or Terraform anytime you make changes to your Worker’s code, edit resource bindings and environment variables, or modify configuration like name or usage model.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2eKs2HQ4xMx5UgrGa7HBfn/e4d63e1d631f7afa53e1253720d7dd87/image5-13.png" />
            
            </figure><p>We expose the source of your deployments, so you can track where changes are coming from. For example, if you have a CI job that’s responsible for changes, and you see a user made a change through the Cloudflare dashboard, it’s easy to flag that and dig into whether the deployment was a mistake.</p>
    <div>
      <h3>Interacting with deployments</h3>
      <a href="#interacting-with-deployments">
        
      </a>
    </div>
    <p>Cloudflare tracks the authors, sources, and timestamps of deployments. If you have a set of users responsible for deployment, or an API Token that’s associated with your CI tool, it’s easy to see which made recent deployments. Each deployment also includes a timestamp, so you can track when those changes were made.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7hSjh1SH7GGpG7B3ixCE94/d4c31d4cfee9943ba898f0eb623f5155/unnamed-3.png" />
            
            </figure><p>You can access all this deployment information in your Cloudflare dashboard, under your <i>Worker’s Deployments</i> tab. We also report on the active version right at the front of your Worker’s detail page. Wrangler will also report on deployment information. <code>wrangler publish</code> now reports the latest deployed version, and a new `<a href="https://developers.cloudflare.com/workers/platform/deployments">wrangler deployments</a>` command can be used to view a deployment history.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4SImxu5FugyTmsz6S199S8/7222d4b30bfb8ca547030697db4d15a1/image1-58.png" />
            
            </figure><p>To learn more about the details of deployments, head over to our <a href="https://developers.cloudflare.com/workers/platform/deployments">Developer Documentation</a>.</p>
    <div>
      <h3>What’s next?</h3>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>We’re excited to share deployments with our customers, available today in an open beta. As we mentioned up front, we’re just getting started with deployments. We’re also excited for more on-platform tooling like rollbacks, deploy status, deployment rules, and a view-only mode to historical deployments. Beyond that, we want to ensure deployments can be automated from commits to your repository, which means working on version control integrations to services like GitHub, Bitbucket, and Gitlab. We’d love to hear more about how you're currently using Workers and how we can improve developer experience. If you’re interested, <a href="http://www.cloudflare.com/lp/developer-week-deployments">let’s chat</a>.</p><p>If you’d like to join the conversation, head over to <a href="https://discord.gg/cloudflaredev">Cloudflare’s Developer Discord</a> and give us a shout! We love hearing from our customers, and we’re excited to see what you build with Cloudflare.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">4NT66c2xlxdnFSu7w4b1Gd</guid>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Going originless with Cloudflare Workers – Building a Todo app – Part 1: The API]]></title>
            <link>https://blog.cloudflare.com/workers-todo-part-1/</link>
            <pubDate>Wed, 21 Sep 2022 13:45:00 GMT</pubDate>
            <description><![CDATA[ Custom Domains are now in Generally Available! Today we go through Part 1 in a series on building completely serverless applications on Cloudflare’s Developer Platform ]]></description>
            <content:encoded><![CDATA[ <p></p><p>A few months ago we launched Custom Domains into an <a href="/custom-domains-for-workers/">open beta</a>. Custom Domains allow you to hook up your Workers to the Internet, without having to deal with DNS records or certificates – just enter a valid hostname and Cloudflare will do the rest! The beta’s over, and Custom Domains are now GA.</p><p>Custom Domains aren’t just about a seamless developer experience; they also allow you to build a globally distributed instantly scalable application on Cloudflare’s Developer Platform. That’s because Workers leveraging Custom Domains have no concept of an ‘Origin Server’. There’s no ‘home’ to phone to - and that also means your application can use the power of Cloudflare’s global network to run your application, well, everywhere. It’s truly serverless.</p>
    <div>
      <h3>Let’s build “Todo”, but without the servers</h3>
      <a href="#lets-build-todo-but-without-the-servers">
        
      </a>
    </div>
    <p>Today we’ll start a series of posts outlining a simple todo list application. We’ll start with an <a href="https://www.cloudflare.com/learning/security/api/what-is-an-api/">API</a> and hook it up to the Internet using Custom Domains.</p><p>With Custom Domains, you’re treating the whole network as the application server. Any time a request comes into a Cloudflare data center, Workers are triggered in that data center and connect to resources across the network as needed. Our developers don’t need to think about regions, or replication, or spinning up the right number of instances to handle unforeseen load. Instead, just deploy your Workers and Cloudflare will handle the rest.</p><p>For our todo application, we begin by building an <a href="https://www.cloudflare.com/learning/security/api/what-is-an-api-gateway/">API Gateway</a> to perform routing, any authorization checks, and drop invalid requests. We then fan out to each individual use case in a separate Worker, so our teams can independently make updates or add features to each endpoint without a full redeploy of the whole application. Finally, each Worker has a D1 binding to be able to create, read, update, and delete records from the database. All of this happens on Cloudflare’s global network, so your API is truly available everywhere. The architecture will look something like this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7qm175kv1nM4LmxTUhY48p/237e8c8839a15d139a8c6a689888eaa3/image2-24.png" />
            
            </figure>
    <div>
      <h3>Bootstrap the D1 Database</h3>
      <a href="#bootstrap-the-d1-database">
        
      </a>
    </div>
    <p>First off, we’re going to need a D1 database set up, with a schema for our todo application to run on. If you’re not familiar with D1, it’s <a href="https://www.cloudflare.com/developer-platform/products/d1/">Cloudflare’s serverless database offering</a> - explained in more detail <a href="/introducing-d1/">here</a>. To get started, we use the <code>wrangler d1</code> command to create a new database:</p>
            <pre><code>npx wrangler d1 create &lt;todo | custom-database-name&gt;</code></pre>
            <p>After executing this command, you will be asked to add a snippet of code to your <code><b>wrangler.toml</b></code> file that looks something like this:</p>
            <pre><code>[[ d1_databases ]]
binding = "db" # i.e. available in your Worker on env.db
database_name = "&lt;todo | custom-database-name&gt;"
database_id = "&lt;UUID&gt;"</code></pre>
            <p>Let’s save that for now, and we’ll put these into each of our private microservices in a few moments. Next, we’re going to create our database schema. It’s a simple todo application, so it’ll look something like this, with some seeded data:</p><p><b><i>db/schema.sql</i></b></p>
            <pre><code>DROP TABLE IF EXISTS todos;
CREATE TABLE todos (id INTEGER PRIMARY KEY, todo TEXT, todoStatus BOOLEAN NOT NULL CHECK (todoStatus IN (0, 1)));
INSERT INTO todos (todo, todoStatus) VALUES ("Fold my laundry", 0),("Get flowers for mum’s birthday", 0),("Find Nemo", 0),("Water the monstera", 1);</code></pre>
            <p>You can bootstrap your new D1 database by running:</p>
            <pre><code>npx wrangler d1 execute &lt;todo | custom-database-name&gt; --file=./schema.sql</code></pre>
            <p>Then validate your new data by running a query through Wrangler using the following command:</p>
            <pre><code>npx wrangler d1 execute &lt;todo | custom-database-name&gt; --command='SELECT * FROM todos';</code></pre>
            <p>Great! We’ve now got a database running entirely on Cloudflare’s global network.</p>
    <div>
      <h3>Build the endpoint Workers</h3>
      <a href="#build-the-endpoint-workers">
        
      </a>
    </div>
    <p>To talk to your database, we’ll spin up a series of private microservices for each endpoint in our application. We want to be able to create, read, update, delete, and list our todos. The full source code for each is available <a href="https://github.com/kabirsikand/worker-todos-api">here</a>. Below is code from a Worker that lists all our todos from D1.</p><p><b><i>list/src/list.js</i></b></p>
            <pre><code>export default {
   async fetch(request, env) {
     const { results } = await env.db.prepare(
       "SELECT * FROM todos"
     ).all();
     return Response.json(results);
   },
 };</code></pre>
            <p>The Worker ‘todo-list’ needs to be able to access D1 from the environment variable <code>db</code>. To do this, we’ll define the D1 binding in our <code>wrangler.toml</code> file. We also specify that workers_dev is false, preventing a preview from being generated via workers.dev (we want this to be a <i>private</i> microservice).</p><p><b><i>list/wrangler.toml</i></b></p>
            <pre><code>name = "todo-list"
main = "src/list.js"
compatibility_date = "2022-09-07"
workers_dev = false
usage_model = "unbound"

[[ d1_databases ]]
binding = "db" # i.e. available in your Worker on env.db
database_name = "&lt;todo | custom-database-name&gt;"
database_id = "UUID"</code></pre>
            <p>Finally, use wrangler publish to deploy this microservice.</p>
            <pre><code>todo/list on ∞main [!] 
› wrangler publish
 ⛅️ wrangler 0.0.0-893830aa
-----------------------------------------------------------------------
Retrieving cached values for account from ../../../node_modules/.cache/wrangler
Your worker has access to the following bindings:
- D1 Databases:
  - db: todo (UUID)
Total Upload: 4.71 KiB / gzip: 1.60 KiB
Uploaded todo-list (0.96 sec)
No publish targets for todo-list (0.00 sec)</code></pre>
            <p>Notice that wrangler mentions there are no ‘publish targets’ for todo-list. That’s because we haven’t hooked todo-list up to any HTTP endpoints. That’s fine! We’re going to use Service Bindings to route requests through a gateway worker, as described in the architecture diagram above.</p><p>Next, reuse these steps to create similar microservices for each of our create, read, update, and delete endpoints. The source code is available to <a href="https://github.com/kabirsikand/worker-todos-api">follow along</a>.</p>
    <div>
      <h3>Tying it all together with an API Gateway</h3>
      <a href="#tying-it-all-together-with-an-api-gateway">
        
      </a>
    </div>
    <p>Each of our Workers are able to talk to the D1 database, but how can our application talk to our API? We’ll build out a simple API gateway to route incoming requests to the appropriate microservice. For the purposes of our application, we’re using a combination of URL pathname and request method to detect which endpoint is appropriate.</p><p><b><i>gateway/src/gateway.js</i></b></p>
            <pre><code>export default {
 async fetch(request, env) {
   try{
     const url = new URL(request.url)
     const idPattern = new URLPattern({ pathname: '/:id' })
     if (idPattern.test(request.url)) {
       switch (request.method){
         case 'GET':
           return await env.get.fetch(request.clone())
         case 'PATCH':
           return await env.update.fetch(request.clone())
         case 'DELETE':
           return await env.delete.fetch(request.clone())
         default:
           return new Response("Unsupported method for endpoint /:id", {status: 405})
       }
     } else if (url.pathname == '/') {
       switch (request.method){
         case 'GET':
           return await env.list.fetch(request.clone())
         case 'POST':
           return await env.create.fetch(request.clone())
         default:
           return new Response("Unsupported method for endpoint /", {status: 405})
       }
     }
     return new Response("Not found. Supported endpoints are /:id and /", {status: 404})
   } catch(e) {
     return new Response(e, {status: 500})
   }
 },
};</code></pre>
            <p>With our API gateway all set, we just need to expose our application to the Internet using a Custom Domain, and hook up our Service Bindings, so the gateway Worker can access each appropriate microservice. We’ll set this up in a <code>wrangler.toml</code>.</p><p><b><i>gateway/wrangler.toml</i></b></p>
            <pre><code>name = "todo-gateway"
main = "src/gateway.js"
compatibility_date = "2022-09-07"
workers_dev = false
usage_model = "unbound"
 
routes =  [
   {pattern="todos.radiobox.tv", custom_domain=true, zone_name="radiobox.tv"}
]
 
services = [
   {binding = "get",service = "todo-get"},
   {binding = "delete",service = "todo-delete"},
   {binding = "create",service = "todo-create"},
   {binding = "update",service = "todo-update"},
   {binding = "list",service = "todo-list"}
]</code></pre>
            <p>Next, use <code>wrangler publish</code> to deploy your application to the Cloudflare network. Seconds later, you’ll have a simple, functioning todo API built entirely on Cloudflare’s Developer Platform!</p>
            <pre><code>› wrangler publish
 ⛅️ wrangler 0.0.0-893830aa
-----------------------------------------------------------------------
Retrieving cached values for account from ../../../node_modules/.cache/wrangler
Your worker has access to the following bindings:
- Services:
  - get: todo-get
  - delete: todo-delete
  - create: todo-create
  - update: todo-update
  - list: todo-list
Total Upload: 1.21 KiB / gzip: 0.42 KiB
Uploaded todo-gateway (0.62 sec)
Published todo-gateway (0.51 sec)
  todos.radiobox.tv (custom domain - zone name: radiobox.tv)</code></pre>
            
    <div>
      <h3>Natively Global</h3>
      <a href="#natively-global">
        
      </a>
    </div>
    <p>Since it’s built natively on Cloudflare, you can also include Cloudflare’s security suite in front of the application. If we want to <a href="https://www.cloudflare.com/learning/security/threats/how-to-prevent-sql-injection/">prevent SQL Injection attacks</a> for this endpoint, we can enable the appropriate Managed WAF rules on our todos API endpoint. Alternatively, if we wanted to prevent global access to our API (only allowing privileged clients to access the application), we can simply put Cloudflare Access in front, with custom Access Rules.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3i5ZY1TUMPyiUuvZ6Uggy9/4341c43b2022b5820c0eb002802e18be/image3-15.png" />
            
            </figure><p>With Custom Domains on Workers, you’re able to easily create applications that are native to Cloudflare’s global network, instantly. Best of all, your developers don’t need to worry about maintaining DNS records or certificate renewal - Cloudflare handles it all on their behalf. We’d like to give a huge shout out to the 5,000+ developers who used Custom Domains during the open beta period, and those that gave feedback along the way to make this possible. Can’t wait to see what you build next! As always, if you have any questions or would like to get involved, please join us on <a href="http://discord.gg/cloudflaredev">Discord</a>.</p><p>Tune in next time to see how we can build a frontend for our application. In the meantime, you can play around with the todos API we built today at <a href="https://todos.radiobox.tv"><code>todos.radiobox.tv</code></a>.</p>
    <div>
      <h3>Watch on Cloudflare TV</h3>
      <a href="#watch-on-cloudflare-tv">
        
      </a>
    </div>
    <div></div><p></p> ]]></content:encoded>
            <category><![CDATA[GA Week]]></category>
            <category><![CDATA[General Availability]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[D1]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">3pIn020UceWWDhjyEQnm08</guid>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Custom Domains for Workers]]></title>
            <link>https://blog.cloudflare.com/custom-domains-for-workers/</link>
            <pubDate>Thu, 12 May 2022 12:58:52 GMT</pubDate>
            <description><![CDATA[ Custom Domains are now available for your Workers. Hook up a Worker to any domain you own, and Cloudflare will handle making DNS records and issuing certificates on your behalf ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we’re happy to announce Custom Domains for Workers. Custom Domains allow you to hook up a domain to your Worker, without having to fuss about certificates, origin servers or <a href="https://www.cloudflare.com/learning/dns/what-is-dns/">DNS</a> – it just works. Let’s take a look at how we built Custom Domains and how you can use them.</p>
    <div>
      <h3>The magic of Cloudflare DNS</h3>
      <a href="#the-magic-of-cloudflare-dns">
        
      </a>
    </div>
    <p>Under the hood, we’re leveraging Cloudflare DNS to register your Worker as the origin for your domain. All you need to do is head to your Worker, go to the Triggers tab, and click Add Custom Domain. Cloudflare will handle creating the DNS record and issuing a certificate on your behalf. In seconds, your domain will point to your Worker, and all you need to worry about is writing your code. We’ll also help guide you through the process of creating these new records and replace any existing ones. We built this with a straightforward ethos in mind: we should be clear and transparent about actions we’re taking, and make it easy to understand.</p><p>We’ve made a few welcome changes when you’re using a Custom Domain on your Worker. First off, when you send a request to <i>any</i> path on that Custom Domain, your Worker will be triggered. No need to create a route with <code>/*</code> at the end.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/s8D0axwl0ZMm0ONFQ3Fvf/c6efda35ed2bfc7c8d01da71310718df/image3-19.png" />
            
            </figure><p>Second, your Custom Domain Worker is considered the ‘origin server’. That means, no need to `fetch(event.request)` once you’re in that Worker; instead, talk to any internal or external services you need to by creating request objects in your code, or talk to other Workers services using any of our available bindings. We’ve increased the limit of external requests you can make, when using our Unbound usage model, to 1,000. You can talk to any services you’d like to – things like payment, communication, analytics, or tracking services come to mind, not to mention your databases. If that’s not enough for your use case, feel free to reach out via Discord or support, and we’ll happily chat.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4OTQJxGplvLzRcu36c4BVE/f69269859b34cc3d3321948e63642e59/image1-31.png" />
            
            </figure><p>Finally, what if you need to talk to your Worker from another one? Since these Workers act as an origin server, you can just send a normal request to the associated endpoint, and we’ll invoke that Worker – even if it’s on the same Cloudflare zone.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ZStNKjBs4qGgdT6ANfPKN/e134c2c7b280dbeefa9a439f815d964a/image4-14.png" />
            
            </figure>
    <div>
      <h3>Let’s build an example application</h3>
      <a href="#lets-build-an-example-application">
        
      </a>
    </div>
    <p>We’ll start by writing our application. I have an example that I’ve called <code>api-gateway</code> below. It checks the incoming request path, and delegates work to downstream Workers using <a href="/service-bindings-ga">Service Bindings</a>. For any privileged endpoints, it performs an authorization check before executing that code:</p>
            <pre><code>export default {
 async fetch(request, environment) {
   const url = new URL(request.url);
   switch (url.pathname) {
     case '/login':
       return await environment.login.fetch(request);

     case '/logout':
       return await environment.logout.fetch(request);

     case '/admin': {
       // Check that the "Authorization" header is sent when authenticated.
       const authCheck = await environment.auth.fetch(request.clone());
       if (authCheck.status != 200) { return authCheck }
       // If the auth check passes, send the request to the /admin endpoint
       return await environment.admin.fetch(request);
     }

     case '/robots.txt':
       return new Response(null, { status: 204 });
   }
   return new Response('Not Found.', { status: 404 });
 }
}```</code></pre>
            <p>Now that I have a working application, I want to serve it on my Custom Domain. To hook this up, head over to <b><i>Workers</i></b>, <b><i>Triggers</i></b>, click ‘<b><i>Add Custom Domain</i></b>’, and type in your desired hostname. You’ll be guided through a simple workflow to generate your new Worker record, and your Worker will be the target.</p><div></div>
<p></p><p>Best of all, with Custom Domains you can reap the performance benefits of DNS-routable Workers; Cloudflare never has to look through a routing table to invoke your Worker. And, by leveraging Service Bindings, you can customize your routing to your heart’s content – using URL parameters, headers, request content, or query strings to appropriately invoke the right Worker at the right time.</p><p>We’re excited to see what you build with Custom Domains. Custom Domains are available in an Open Beta starting today. Support is built right into the Cloudflare Dashboard and API’s, and CLI support via Wrangler is coming soon.</p> ]]></content:encoded>
            <category><![CDATA[Platform Week]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">30CsBBawnxrctsDxDj5C5Z</guid>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Service Bindings are generally available, with efficient pricing]]></title>
            <link>https://blog.cloudflare.com/service-bindings-ga/</link>
            <pubDate>Tue, 10 May 2022 13:01:16 GMT</pubDate>
            <description><![CDATA[ Service bindings are an API that facilitate Worker-to-Worker communication. You can invoke other Workers directly from your code; making it possible to communicate with shared services. We’ll also make it cost-efficient to run multiple Workers ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we’re happy to unveil a new way to communicate between your Workers. In the spirit of baking more and more flexibility into our Developer Platform, our team has been hard at work building a new API to facilitate Worker to Worker communication: Service Bindings. Service Bindings allow your Workers to send requests to other Workers Services, from your code, without those requests going over the Internet. It opens up a world of composability that was previously closed off by a difficult interface, and makes it a lot easier for you to build complex applications on our developer platform.</p><p>Service Bindings allow teams to segment application logic across multiple Workers. By segmenting your logic, your teams can now build with more confidence by only deploying narrowly scoped changes to your applications, instead of recommitting the whole application every time. Service Bindings give developers both composability and confidence. We’ve seen some excellent uses so far, and today we’ll go through one of the more common examples. Alongside this functionality, we'll show you how Cloudflare’s cost efficiency will save you money.</p>
    <div>
      <h3>Example: An API Gateway</h3>
      <a href="#example-an-api-gateway">
        
      </a>
    </div>
    <p>Service Bindings allow you to easily expand the number of services running on a single request. Developers can now create a pipeline of Workers that call one another and create a complex series of compute blocks. The ability to separate and compose application logic together has opened Cloudflare Workers up to even more uses.</p><p>With Service Bindings, one of our customers has moved multiple services off of their legacy infrastructure by creating a gateway Worker that serves as the entry point of a request. This gateway Worker handles decision-making about request routing and quickly shifts requests to appropriate services – be it on their legacy application servers or their newly created Workers. This project enabled several new teams to onboard as a result, each managing their Worker independently. Large teams need a development ecosystem that allows for granular deployments, minimizing the scope of impact when a bad push to production occurs.</p><p>Let’s walk through a simple example of an <a href="https://www.cloudflare.com/learning/security/api/what-is-an-api-gateway/">API gateway</a> Worker that handles routing and user authentication. We’ll build an application that takes in a user request and checks for authorization. If the user isn’t authorized, we block the request. If the user has valid credentials, we’ll fetch the user data. The application will also implement login and logout to change the user authentication state.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1kkaZ70HmxIJ6wXKINDb1b/82f037cb3b79c88d2ba0ac09922a1b3c/image3-8.png" />
            
            </figure><p>Here, the <code>api-gateway</code> Worker calls <code>login</code> and <code>logout</code> Workers for authentication to privileged endpoints like <code>/getuser</code>. The <code>api-gateway</code> Worker also checks each request for authorization via the <code>auth</code> Worker and allows valid requests to call the <code>get-user</code> Worker. The <code>get-user</code> Worker then makes an outbound network request to gather the required user information, and passes that data back to the client via our <code>api-gateway</code> Worker. The <code>api-gateway</code> Worker is therefore bound to four other Worker Services: <code>auth</code>, <code>get-user</code>, <code>login</code>, and <code>logout</code>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4eixPNmvpG8IywgiuhSIbV/6be31ae6fa9cdbc336a385b977d0366a/image6-6.png" />
            
            </figure><p>Let’s take a look at the code for the <code>api-gateway</code> Worker. We’ll see the routes <code>/login</code>, <code>/logout</code>, and <code>/getuser</code> are implemented on this API. For the <code>/getuser route</code>, the <code>api-gateway</code> Worker requires authorization via the <code>auth</code> Worker. Requests to any other endpoints will return a 404 HTTP status code.</p>
            <pre><code>export default {
 async fetch(request, environment) {
   const url = new URL(request.url);
   switch (url.pathname) {
     case '/login':
       return await environment.login.fetch(request);

     case '/logout':
       return await environment.logout.fetch(request);

     case '/getuser': {
       // Check that the "Authorization" header is sent when authenticated.
       const authCheck = await environment.auth.fetch(request.clone());
       if (authCheck.status != 200) { return authCheck }
       // If the auth check passes, send the request to the /admin endpoint
       return await environment.getuser.fetch(request);
     }
   }
   return new Response('Not Found.', { status: 404 });
 }
}</code></pre>
            <p>The code really is that simple. The separation of concerns allows your teams to work independently of each other, relying on each service to do what it is supposed to do in production. It allows you to separate your code by use case, developing, testing, and debugging more effectively.</p><p>But your next question might be, what am I charged for? Before we get into price, let’s first talk about where the compute execution is happening using our example above. A request to <code>/getuser</code> may look something like this, when looking across the request’s lifecycle:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5YCcVOSEERBcJLV2iya61r/d5fc05b866364d00d82055683c21c433/image7-2.png" />
            
            </figure><p>The <code>get-user</code> Worker makes a network call to gather user information while the <code>auth</code> Worker executes entirely within the Workers runtime. Now that we understand what a single execution looks like, let’s talk about cost efficiency.</p>
    <div>
      <h3>Cost efficiency that saves you money</h3>
      <a href="#cost-efficiency-that-saves-you-money">
        
      </a>
    </div>
    <p>Service Bindings are available for you to use starting today. They cost the same as any normal Worker; each invocation is charged as if it’s a request from the Internet – with one major and important difference. We’re removing the concept of “idle resources” across Workers. You will be charged a single billable duration across <i>all</i> Workers triggered by a single incoming request. This is possible because Cloudflare can share compute resources used by each request across your Workers and pass the resulting cost savings on to our customers.</p><p>Revisiting our example above, the <code>api-gateway</code> Worker may be waiting on other dependencies to perform some work, while it sits idle. When we say idle, we mean the time the <code>api-gateway</code> Worker is awaiting a response from the <code>auth</code> and <code>get-user</code> Workers – represented by the gray bars in the request lifetime graphic.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3inkZDySh1DZoX4yfauPcS/b6596a1db23517c0eb0999f975f59259/image7-3.png" />
            
            </figure><p>When using Service Bindings, you no longer have to pay for those “idle resources”. With the Workers model, customers can execute work on a single shared compute thread across multiple individual Services, for each and every request. Cloudflare will charge for the amount of time that thread is allocated to your Workers and the time your Workers are awaiting external dependencies. Cloudflare won’t double charge for any overlap.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/40SbbAaIExmf7sZ9p5w5zg/870c0084dcabc7aef7cba165c656e752/image2-11.png" />
            
            </figure><p>This is in stark contrast to classic serverless compute models (like Amazon Web Services’ Lambda), where resources are allocated on a per-instance basis, and as such cost is passed to the customer even when those resources are not actively being used. That extra charge is represented by the magenta portions of the request lifetime graphic below.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2QLn6BPLvLTfqUHdnba4gS/a4b79e01657d18c62babefbfe25a7731/image4-7.png" />
            
            </figure><p>Cloudflare is able to squash duration down to a single charge, since Cloudflare can share the compute resources between your services. We pass those cost savings on to our customers, so you can pay only for the work you need done, when you need it done, every time.</p>
    <div>
      <h3>Getting Started</h3>
      <a href="#getting-started">
        
      </a>
    </div>
    <p>Excited to try our Service Bindings? Head over to the <b>Settings =&gt; Variables</b> tab of your Worker, and click ‘<b>Edit Variables</b>’ under Service Bindings. You can then reference those bindings within your code and call <code>fetch()</code> on any one of them.</p><div></div>
<p></p><p>We can’t wait to see what you build. Check us out on <a href="http://discord.gg/cloudflaredev">Discord</a> to join the conversation.</p> ]]></content:encoded>
            <category><![CDATA[Platform Week]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">eXsiivwboTEPQ3hinE40f</guid>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Happy Earth Day: Announcing Green Compute open beta]]></title>
            <link>https://blog.cloudflare.com/earth-day-2022-green-compute-open-beta/</link>
            <pubDate>Fri, 22 Apr 2022 12:57:13 GMT</pubDate>
            <description><![CDATA[ Today, we are happy to announce that we are bringing Green Compute to all our developers ]]></description>
            <content:encoded><![CDATA[ <p></p><p>At Cloudflare, we are on a mission to help build a better Internet. We continue to grow our network, and it is important for us to do so responsibly.</p><p>Since Earth Day 2021, some pieces of this effort have included:</p><ul><li><p>Making a commitment to powering our network with <a href="/cloudflare-committed-to-building-a-greener-internet/">100% renewable energy</a></p></li><li><p>Hosting our first <a href="https://www.cloudflare.com/impact-week/">Impact Week</a></p></li><li><p>Releasing our first <a href="https://www.cloudflare.com/impact/">Impact Report</a> and <a href="https://assets.ctfassets.net/slt3lc6tev37/2YzIeTtzSbyKkM4GsryP5S/62ce0dff98e92a142281a0b462ce4408/Cloudflare_Emissions_Inventory_-_2020.pdf">emissions inventory report</a></p></li><li><p>Reducing the environmental impact of web searches via <a href="/from-0-to-20-billion-how-we-built-crawler-hints/">Crawler Hints</a></p></li><li><p>Providing customers with visibility about their Scope 3 emissions with <a href="/understand-and-reduce-your-carbon-impact-with-cloudflare/">Carbon Impact Reports</a></p></li></ul><p>And we are just getting started. We are working to make the Cloudflare network — and our customers’ websites, applications, and networks — as efficient as possible in terms of design, hardware, systems, and protocols. After all, we do not want to lose sight of our responsibilities to our home: our planet Earth.</p>
    <div>
      <h3>Green Compute for Workers Cron Triggers</h3>
      <a href="#green-compute-for-workers-cron-triggers">
        
      </a>
    </div>
    <p>During Impact Week last year, we began testing <a href="/announcing-green-compute/">Green Compute</a> in a closed beta. Green Compute makes Workers Cron Triggers run only in facilities that are powered by renewable energy. We are hoping to incentivize more facilities to implement responsible climate and energy policies.</p><p>With Green Compute enabled, Workers Cron Triggers will run only on Cloudflare points of presence that are located in data centers that are powered by 100% renewable energy.</p><p>Based on carbon accounting and renewable energy standards like RE100, all Cloudflare operations are considered 100% powered by renewable energy because we purchase the same amount of renewable energy as the total energy we use globally.</p><p>However, the data centers we operate in are co-located with facilities owned by other companies. Although our renewable energy purchases cover the energy used by our equipment, all other energy consumed at that facility may or may not be renewable.</p><p>Renewable energy can be purchased in a number of ways, including:</p><ul><li><p>Through on-site generation (wind turbines, solar panels)</p></li><li><p>Directly from renewable energy producers through contractual agreements called Power Purchase Agreements (PPA)</p></li><li><p>In the form of Renewable Energy Credits (RECs, IRECs, GoOs) from an energy credit market that helps offset energy use on-site</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/60vdYFTbwiXgamlWcyuRAB/e17e803c2f27e9fd24ee46083c501398/image1-13.png" />
            
            </figure>
    <div>
      <h3>How to get started with Green Compute</h3>
      <a href="#how-to-get-started-with-green-compute">
        
      </a>
    </div>
    <p>Today, we are happy to announce that we are bringing this function to all our developers — Green Compute is now in open beta. To get started, head to your Workers App, click <b>Change Setting</b> under <b>Compute Setting</b>, and select <b>Green Compute</b>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4mPfwK1ZBeiss8SGuDKSKZ/67ce3d1bbf92cd3bce5c7243a9576b74/image2-12.png" />
            
            </figure><p>Now, any time you create a scheduled workload, you will see a leaf icon indicating that your schedules will be run in data centers powered by renewable energy sources. With just one click, you can help build a better Internet.</p>
    <div>
      <h3>Next steps</h3>
      <a href="#next-steps">
        
      </a>
    </div>
    <ul><li><p>Want to get more involved with the Cloudflare Developer community, help shape what we build next, or give feedback on Green Compute? Enter <a href="https://discord.gg/cloudflaredev">our Discord</a> and join the conversation.</p></li><li><p>Learn more about how Cloudflare is helping build a better Internet via <a href="https://www.cloudflare.com/impact/">Cloudflare Impact</a>.</p></li></ul> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Earth Day]]></category>
            <category><![CDATA[Green]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Policy & Legal]]></category>
            <guid isPermaLink="false">1neiUAnn66h4WVXQhlypPI</guid>
            <dc:creator>Kabir Sikand</dc:creator>
            <dc:creator>Andie Goodwin</dc:creator>
        </item>
        <item>
            <title><![CDATA[Workers, Now Even More Unbound: 15 Minutes, 100 Scripts, and No Egress Fees]]></title>
            <link>https://blog.cloudflare.com/workers-now-even-more-unbound/</link>
            <pubDate>Thu, 18 Nov 2021 14:00:18 GMT</pubDate>
            <description><![CDATA[ Workers is now even more Unbound, with no egress, more execution time, and more scripts. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Our mission is to enable developers to build their applications, end to end, on our platform, and ruthlessly eliminate limitations that may get in the way. Today, we're excited to announce you can build large, data-intensive applications on our network, all without breaking the bank; starting today, we're dropping <a href="https://www.cloudflare.com/learning/cloud/what-are-data-egress-fees/">egress fees</a> to zero.</p>
    <div>
      <h3>More Affordable: No Egress Fees</h3>
      <a href="#more-affordable-no-egress-fees">
        
      </a>
    </div>
    <p>Building more on any platform historically comes with a caveat — high data transfer cost. These costs often come in the form of egress fees. Especially in the case of data intensive workloads, egress data transfer costs can come at a high premium, depending on the provider.</p><p>What exactly are data egress fees? They are the costs of retrieving data from a cloud provider. Cloud infrastructure providers generally pay for bandwidth based on capacity, but often bill customers based on the amount of data transferred. Curious to learn more about what this means for end users? We recently wrote an analysis of <a href="/aws-egregious-egress/">AWS’ Egregious Egress</a> — a good read if you would like to learn more about the ‘Hotel California’ model AWS has spun up. Effectively, data egress fees lock you into their platform, making you choose your provider based not on which provider has the best infrastructure for your use case, but instead choosing the provider where your data resides.</p><p>At Cloudflare, we’re working to flip the script for our customers. Our recently announced <a href="/introducing-r2-object-storage/">R2 Storage</a> waives the data egress fees other providers implement for similar products. Cloudflare is a founding member of the <a href="https://www.cloudflare.com/bandwidth-alliance/">Bandwidth Alliance</a>, aiming to help our mutual customers overcome these data transfer fees.</p><p>We’re keeping true to our mission and, effective immediately, dropping all Egress Data Transfer fees associated with <a href="https://developers.cloudflare.com/workers/platform/pricing#pricing-1">Workers Unbound</a> and <a href="https://developers.cloudflare.com/workers/learning/using-durable-objects">Durable Objects</a>. If you’re using Workers Unbound today, your next bill will no longer include Egress Data Transfer fees. If you’re not using Unbound yet, now is a great time to experiment. With Workers Unbound, get access to longer CPU time limits and pay only for what you use, and don’t worry about the data transfer cost. When paired with Bandwidth Alliance partners, this is a cost-effective solution for any data intensive workloads.</p>
    <div>
      <h3>More Unbound: 15 Minutes</h3>
      <a href="#more-unbound-15-minutes">
        
      </a>
    </div>
    <p>This week has been about defining what the future of computing is going to look like. Workers are great for your latency sensitive workloads, with zero-milliseconds cold start times, fast global deployment, and the power of Cloudflare’s network. But Workers are not limited to lightweight tasks — we want you to run your heavy workloads on our platform as well. That’s why we’re announcing you can now use up to 15 minutes of CPU time on your Workers! You can run your most compute-intensive tasks on Workers using Cron Triggers. To get started, head to the <b>Settings</b> tab in your Worker and select the ‘Unbound’ usage model.</p><div></div><p>Once you’ve confirmed your Usage Model is Unbound, switch to the <b>Triggers</b> tab and click <b>Add Cron Trigger</b>. You’ll see a ‘Maximum Duration’ is listed, indicating whether your schedule is eligible for 15 Minute workloads.</p><div></div>
    <div>
      <h3>Wait, there’s more (literally!)</h3>
      <a href="#wait-theres-more-literally">
        
      </a>
    </div>
    <p>That’s not all. As a platform, it is validating to see our customers want to grow even more with us, and we’ve been working to address these restrictions. That’s why, starting today, all customers will be allowed to deploy up to 100 Worker scripts. With the introduction of <a href="/introducing-worker-services">Services</a>, that represents up to 100 environments per account. This higher limit will allow our customers to migrate more use cases to the Workers platform.</p><p>We’re also delighted to announce that, alongside this increase, the Workers platform will plan to support scripts larger in size. This increase will allow developers to build Workers with more libraries and new possibilities, like running Golang with WASM. Check out an <a href="https://esbuild.developers.workers.dev/">example of esbuild running on a Worker</a>, in a script that’s just over 2MB compressed. If you’re interested in larger script sizes, sign up <a href="https://www.cloudflare.com/larger-scripts-on-workers-early-access/">here</a>.</p><p>The future of cloud computing is here, and it’s on Cloudflare. Workers has always been the <a href="https://developers.cloudflare.com/workers/learning/security-model">secure</a>, <a href="/cloudflare-workers-the-fast-serverless-platform/">fast</a> serverless offering, and has recently been named a <a href="/forrester-wave-edge-development-2021/">leader</a> in the space. Now, it is even more affordable and flexible too.</p><p>We can’t wait to see what ambitious projects our customers build. Developers are now better positioned than ever to deploy large and complex applications on Cloudflare. Excited to build using Workers, or get engaged with the community? Join our <a href="https://discord.gg/rH4SsffFcc">Discord server</a> to keep up with the latest on Cloudflare Workers.</p> ]]></content:encoded>
            <category><![CDATA[Full Stack Week]]></category>
            <category><![CDATA[Egress]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Workers Unbound]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">6FeUc0F2arVJbpfBky0Jeu</guid>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Services: Build Composable, Distributed Applications on Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/introducing-worker-services/</link>
            <pubDate>Tue, 16 Nov 2021 13:58:40 GMT</pubDate>
            <description><![CDATA[ We’re excited to announce Services, the new way to build composable, distributed applications on Cloudflare Workers. Learn how Services can replace the traditional “microservice architecture” with an alternative, zero-cost abstraction model. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>First, there was the Worker script. It was simple, yet elegant. With just a few lines of code, you could rewrite an HTTP request, append a header, or make a quick fix to your website.</p><p>Though, what if you wanted to build an entire application on Workers? You’d need a lot more tools in your developer toolbox. That’s why we’ve introduced extensions to Workers platform like <a href="/workers-kv-is-ga/">KV</a>, our distributed key-value store; <a href="/durable-objects-open-beta/">Durable Objects</a>, — a strongly consistent, object-oriented database; and soon <a href="/introducing-r2-object-storage/">R2</a>, the <a href="https://www.cloudflare.com/developer-platform/products/r2/">no-egress object storage</a>. While these tools allow you to build a more robust application, there’s still a gap when it comes to building a system architecture, composed of <i>many</i> applications or services.</p><p>Imagine you’ve built an authentication service that authorizes requests to your API. You’d want to re-use that logic among all your other services. Moreover, when you make changes to that authentication service, you’d want to test it in a controlled environment that doesn’t affect those other services in production. Well, you don’t need to imagine anymore.</p>
    <div>
      <h3>Introducing Services</h3>
      <a href="#introducing-services">
        
      </a>
    </div>
    <p>Services are the new building block for deploying applications on Cloudflare Workers. Unlike the script, a service is composable, which allows services to talk to each other. Services also support multiple environments, which allow you to test changes in a preview environment, then promote to production when you’re confident it worked.</p><p>To enable a seamless transition to services, we’ve automatically migrated every script to become a service with one “production” environment — no action needed.</p>
    <div>
      <h3>Services have environments</h3>
      <a href="#services-have-environments">
        
      </a>
    </div>
    <p>Each service comes with a production environment and the ability to create or clone dozens of preview environments. Every aspect of an environment is overridable: the code, environment variables, and even resources like a KV namespace. You can create and switch between environments with just a few clicks in the dashboard.</p><div></div><p>Each environment is resolvable at a unique hostname, which is automatically generated when you create or rename the environment. There’s no waiting around after you deploy. Everything you need, like DNS records, <a href="https://www.cloudflare.com/application-services/products/ssl/">SSL certificates</a>, and more, is ready-to-go seconds later. If you’d like a more advanced setup, you can also add custom routes from your domain to an environment.</p><p>Once you’ve tested your changes in a preview environment, you’re ready to promote to production. We’ve made it really easy to promote code from one environment to another, without the need to rebuild or upload your code again. Environments also manage code separately from settings, so you don’t need to manually edit environment variables when you promote from staging to production.</p><div></div>
    <div>
      <h3>Services are versioned</h3>
      <a href="#services-are-versioned">
        
      </a>
    </div>
    <p>Every change to a service is versioned and audited. Mistakes do happen, but when they do, it’s important to be able to quickly roll back, then have the tools to answer the age-old question: “who changed what, when?”</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/AJHfT2PjwB7j3BIP0ouSU/b33181ce481ad9acd6f7c1a34302117b/unnamed--1--6.png" />
            
            </figure><p>Each environment in a service has its own version history. Every time there is a code change or an environment variable is updated, the version number of that environment is incremented. You can also append additional metadata to each version, like a git commit or a deployment tag.</p>
    <div>
      <h3>Services can talk to each other</h3>
      <a href="#services-can-talk-to-each-other">
        
      </a>
    </div>
    <p>Services are composable, allowing one service to talk to another service. To support this, we’re introducing a new API to facilitate service-to-service communication: service bindings.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7eealXyrW68mNRRRR6reuQ/238fa89f310c0a70ca66c9bd0194b79d/image2-10.png" />
            
            </figure><p>A service binding allows you to send HTTP requests to another service, without those requests going over the Internet. That means you can invoke other Workers directly from your code! Service bindings open up a new world of composability. In the example below, requests are validated by an authentication service.</p>
            <pre><code>export default {
  async fetch(request, environment) {
    const response = await environment.AUTH.fetch(request);
    if (response.status !== 200) {
      return response;
    }
    return new Response("Authenticated!");
  }
}</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7nJXwQ6IMSgEPtnhP95yRn/ec33050f570daaf8dcf18d491ceecee7/image1-25.png" />
            
            </figure><p>Service bindings use the standard <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch">fetch</a> API, so you can continue to use your existing utilities and libraries. You can also change the environment of a service binding, so you can test a new version of a service. In the next example, 1% of requests are routed to a “canary” deployment of a service. If a request to the canary fails, it’s sent to the production deployment for another chance.</p>
            <pre><code>export default {
  canRetry(request) {
    return request.method === "GET" || request.method === "HEAD";
  },
  async fetch(request, environment) {
    if (Math.random() &lt; 0.01) {
      const response = await environment.CANARY.fetch(request.clone());
      if (response.status &lt; 500 || !canRetry(request)) {
        return response;
      }
    }
    return environment.PRODUCTION.fetch(request);
  }
}</code></pre>
            <p>While the interface among services is HTTP, the networking is not. In fact, there is no networking! Unlike the typical “microservice architecture,” where services communicate over a network and can suffer from latency or interruption, service bindings are a zero-cost abstraction. When you deploy a service, we build a dependency graph of its service bindings, then package all of those services into a single deployment. When one service invokes another, there is no network delay; the request is executed immediately.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/68l5AHIonTqDD9fpXLJQKh/fdf129dd6695975cd3073e00ed73ca07/image3-18.png" />
            
            </figure><p>This zero-cost model enables teams to share and reuse code within their organizations, without sacrificing latency or performance. Forget the days of convoluted YAML templates or exponential back off to orchestrate services — just write code, and we’ll stitch it all together.</p>
    <div>
      <h3>Try out the future, today!</h3>
      <a href="#try-out-the-future-today">
        
      </a>
    </div>
    <p>We’re excited to announce that you can start using Services today! If you’ve already used Workers, you’ll notice that each of your scripts have been upgraded to a service with one “production” environment. The dashboard and all the existing Cloudflare APIs will continue to “just work” with services.</p><p>You can also create and deploy code to multiple “preview” environments, as part of the open-beta launch. We’re still working on service bindings and versioning, and we’ll provide an update as soon as you can start using them.</p><p>For more information about Services, check out any of the resources below:</p><ul><li><p>Go to the <a href="https://workers.new">dashboard</a> and create your first service!</p></li><li><p>Sign up for the <a href="https://www.cloudflare.com/service-bindings-closed-beta-sign-up">early-access</a> to service bindings.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2hteH8Qu4zVjXKVWv9Q5PT/79b3ce57bfd3e619b8085733d6cfb4a9/Lego.png" />
            
            </figure>
    <div>
      <h3>Watch on Cloudflare TV</h3>
      <a href="#watch-on-cloudflare-tv">
        
      </a>
    </div>
    <div></div><p></p> ]]></content:encoded>
            <category><![CDATA[Full Stack Week]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">7wyOeCHuw2e6KtdAUZh4wM</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Relational Database Connectors]]></title>
            <link>https://blog.cloudflare.com/relational-database-connectors/</link>
            <pubDate>Mon, 15 Nov 2021 13:59:29 GMT</pubDate>
            <description><![CDATA[ Customers can connect to a Postgres or MySQL database directly from their Workers using a Cloudflare Tunnel today. In the future, you can use Database Connectors to achieve this natively using a standardized Socket API. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>At Cloudflare, we’re building the best compute platform in the world. We want to make it easy, seamless, and obvious to build your applications with us. But simply making the best compute platform is not enough — at the heart of your applications are the data they interact with.</p><p>Cloudflare has multiple data storage solutions available today: <a href="/introducing-workers-kv/">Workers KV</a>, <a href="/introducing-r2-object-storage/">R2</a>, and <a href="/introducing-workers-durable-objects/">Durable Objects</a>. All three follow Cloudflare’s design goals for Workers: global by default, infinitely scalable, and delightful for developers to use. We’ve partnered with third-party storage solutions like Fauna, MongoDB and Prisma, who have built data platforms that align beautifully with our design goals and written tutorials for databases that already support HTTP connections.</p><p>The one area that’s been sorely missed: relational databases. Cloudflare itself runs on relational databases, and we’re not alone. In April, we asked <a href="https://workers.cloudflare.com/node">which Node libraries</a> you wanted us to support, and <b>four of the top five requests</b> were related to databases. For this Full Stack Week, we asked ourselves: how could we support relational databases in a way that aligned with our design goals?</p><p>Today, we’re taking a first step towards that world by announcing support for relational databases, including Postgres and MySQL from Workers.</p><p>Connecting to a database is no simple task — if it were as easy as passing a connection string to a database driver, we would have already done it. We’ve had to overcome several hurdles to reach this point, and have several more still to conquer.  </p><p>Our goal with this announcement is to work with you, our developers, to solve the unique pain points that come from accessing databases inside Workers. If you’d like to work with us, fill out <a href="https://www.cloudflare.com/database-connectors-early-access">this form</a> or join us <a href="https://discord.gg/rH4SsffFcc">on Discord</a> — this is just the beginning. If you’d just like to grab the code and play around, use this <a href="https://developers.cloudflare.com/workers/tutorials/query-postgres-from-workers-using-database-connectors">example</a> to get started connecting to your own database, or check out our demo.</p>
    <div>
      <h3>Why are Database Connectors so hard to build?</h3>
      <a href="#why-are-database-connectors-so-hard-to-build">
        
      </a>
    </div>
    <p>Serverless database connections are challenging to support for several reasons.</p><p>Databases are needy — they often require TCP connections, since they assume long-lived connections between an application server and the database. The Workers runtime doesn’t currently support TCP connections, so we’ve only been able to support HTTP-based databases or proxies.</p><p>Like a relationship, establishing a connection isn’t quite enough. Developers use client libraries for databases to make submitting queries and managing the responses easy. Since the Workers runtime is not entirely Node.js compatible, we need to either roll our own database library or find one that does not use unsupported built-in libraries.</p><p>Finally, databases are sensitive. It often takes external libraries to manage shared connections between an application server and a database, since these connections tend to be expensive to establish.</p>
    <div>
      <h3>Moving past these challenges</h3>
      <a href="#moving-past-these-challenges">
        
      </a>
    </div>
    <p>Our approach today gives us the foundation to address each of these challenges in creative ways going forward.</p><p>First, we’re leveraging <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup">cloudflared</a> to create a secure tunnel between Cloudflare and a private network within your existing infrastructure. Cloudflared already supports proxying HTTP to TCP over WebSockets — Our challenge is providing interfaces that look like the socket interfaces existing libraries expect, while rewiring the implementations to redirect reads and writes to our websocket. This method is fast, safe, and secure; but limiting in that we lack control of where to direct the final connections. This is a problem we will solve soon, but until then our approach is essential to gathering latency and performance data to see where else we need to improve.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/vVs0KefQQxbNEt3VsNHHj/41225487a689b82c04c9ef7beb7d8ae2/unnamed-10.png" />
            
            </figure><p>Next, we’ve created a shim-layer that adapts the socket API from a popular runtime to connect directly to databases using a WebSocket. This allows us to bundle code as-is, without forking or otherwise making significant changes to the database library. As part of this announcement, we’ve published a <a href="https://developers.cloudflare.com/workers/tutorials/query-postgres-from-workers-using-database-connectors">tutorial</a> on how to connect to and query a Postgres database from your Workers, using existing Cloudflare technology and a driver from the growing community at Deno. We’re excited to work with the upstream maintainers, on expanding support.</p><p>Finally, we’re most excited for how this approach will let us begin to manage connection pooling and connection establishment overhead. While our current tech demo requires setting up the Cloudflare Tunnel on your own infrastructure, we’re looking for customers who’d like to pilot a model where Cloudflare hosts the tunnel for you.</p>
    <div>
      <h3>Where we’re going</h3>
      <a href="#where-were-going">
        
      </a>
    </div>
    <p>We’re just getting started. Our goal with today’s announcement is to find customers who are looking to build new applications or migrate existing applications to Workers while working with data that’s stored in a relational database.</p><p>Just as Cloudflare started by providing security, performance, and reliability for customer’s websites, we’re excited about a future where Cloudflare manages database connections, handles replication of data across cloud providers and provides low-latency access to data globally.</p><p>First, we’re looking to add <a href="/introducing-socket-workers/">support for TCP into the runtime natively</a>. With native support for TCP we’ll not only have better support for databases, but expand the Workers runtime to work with data infrastructure more broadly.</p><p>Our position in the network layer of the stack makes providing performance, security benefits and extremely reduced egress costs to global databases all possible realities. To do so, we’ll repurpose the HTTP to TCP proxy service that we’ve currently built and run it for developers as a connection pooling service, managing connections to their databases on their behalf.</p><p>Finally, our network makes caching data and making it accessible globally at low latency possible. Once we have connections back to your data, making it globally accessible in Cloudflare’s network will unlock fundamentally new architectures for distributed data.</p>
    <div>
      <h3>Take our connectors for a spin</h3>
      <a href="#take-our-connectors-for-a-spin">
        
      </a>
    </div>
    <p>Want to check things out? There are three main steps to getting up-and-running:</p><ol><li><p>Deploying cloudflared within your infrastructure.</p></li><li><p>Deploying a database that connects to cloudflared.</p></li><li><p>Deploying a Worker with the database driver that submits queries.</p></li></ol><p>The Postgres tutorial is available <a href="https://developers.cloudflare.com/workers/tutorials/query-postgres-from-workers-using-database-connectors">here</a>.</p><p>When you’re all done, it’ll look a little something like this:</p>
            <pre><code>import { Client } from './driver/postgres/postgres'

export default {
  async fetch(request: Request, env, ctx: ExecutionContext) {
    try {
      const client = new Client({
        user: 'postgres',
        database: 'postgres',
        hostname: 'https://db.example.com',
        password: '',
        port: 5432,
      })
      await client.connect()
      const result = await client.queryArray('SELECT * FROM users WHERE uuid=1;')
      ctx.waitUntil(client.end())
      return new Response(JSON.stringify(result.rows[0]))
    } catch (e) {
      return new Response((e as Error).message)
    }
  },
}</code></pre>
            <p>Hit any snags? Fill out <a href="https://www.cloudflare.com/database-connectors-early-access">this form</a>, join <a href="https://discord.gg/rH4SsffFcc">our Discord</a> or shoot us an <a>email</a> and let’s chat!</p> ]]></content:encoded>
            <category><![CDATA[Full Stack Week]]></category>
            <category><![CDATA[Postgres]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">CinJ7mVjQHrKXIimcCbNR</guid>
            <dc:creator>Kabir Sikand</dc:creator>
            <dc:creator>Greg McKeon</dc:creator>
            <dc:creator>Ben Yule</dc:creator>
        </item>
        <item>
            <title><![CDATA[Profiling Your Workers with Wrangler]]></title>
            <link>https://blog.cloudflare.com/profiling-your-workers-with-wrangler/</link>
            <pubDate>Sat, 18 Sep 2021 12:59:17 GMT</pubDate>
            <description><![CDATA[ To help measure performance of our customers’ Workers, we’re beginning to integrate with the Chrome DevTools protocol. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>In the year since Cloudflare’s launch of Workers Unbound, developers have unlocked the ability to run computationally intensive workloads on the Cloudflare edge network — like image processing, game logic, and other complex algorithms. With all that additional computing power comes a host of questions around performance. Our customers often ask us how they can profile or monitor their Workers to see where they spend the most CPU time, or to see whether their changes improve performance.</p><p>Here at Cloudflare, we not only want to build the fastest, most affordable, and most flexible compute platform at the edge; we also want to make the lives of our developers easier in building their applications. To do this, Cloudflare has begun to integrate with existing tools — places our developers feel comfortable and efficient in their day-to-day work. To help measure performance of our customers’ Workers, we’re beginning to integrate with the Chrome DevTools protocol. Just like you can use chrome://inspect to debug your Node backend, you can also use it to profile your Cloudflare Workers.</p>
    <div>
      <h3>Introducing Chrome DevTools Integration (Beta)</h3>
      <a href="#introducing-chrome-devtools-integration-beta">
        
      </a>
    </div>
    <p>We’re starting off this integration with beta support for local CPU profiling, using Wrangler. To show off how to use this feature, I’m going to be optimizing a simple JavaScript program which outputs the first thousand integers separated by a space. Let’s start by installing the latest version of <a href="https://github.com/cloudflare/wrangler/#installation">Wrangler</a>. We’ll also need a Worker cloned down to your local machine. I’ll be using <a href="https://github.com/kabirsikand/workers-profiling-example/blob/main/slow-worker.js">Workers Profiling Example</a>, but feel free to use any CPU intensive Worker for this tutorial.</p><p>For reference, my sample code is below. You’ll notice this code is intentionally performing a computation that will slow down our Worker.</p>
            <pre><code>addEventListener("fetch", event =&gt; {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  let body = '';
  for (let i = 0; i &lt; 1000; ++i) {
    body = body + ' ' + i;
  }

  return new Response(body, {
    headers: { 'content-type': 'text/plain' },
  });
}</code></pre>
            <p>To confirm this, we’ll run Chrome DevTools and profile our Worker locally. In the directory of your sample project, <code>run wrangler dev --inspect</code>. To launch the DevTools, open <code>chrome://inspect</code> in Chrome or Chromium. You’ll see Chrome’s DevTools homepage pop up:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/79e6ioOYPp1SjNBLWEhLWK/d910cbfd5f0ee881541d497c60798baf/image1-22.png" />
            
            </figure><p>Click ‘Configure’ and add <code>localhost://9230</code> as one of the targets. You should see <code>wrangler[{Worker name}]</code> appear under “Remote Targets”, where {Worker name} is the name of your project.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5i0wiIOb2mQG9ZPZseyEdk/2b98a89b3e815f9be0583d10fee9e94e/image2-27.png" />
            
            </figure><p>Click ‘inspect’, then in the popup, ‘Profiler’.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Kc0LIb3k3O1ldfl18jCBP/380c4f36d6b5d97521a2e3b92aad3ddb/image4-21.png" />
            
            </figure><p>Click ‘Start’ and, in a different browser tab, make a few requests to your site, then click ‘Stop’. It should be available locally on localhost:8787.</p><p>Analyze the flame graph. For my Slow Worker, I see the following graph:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4cA7Ng90JMS4Nt6LOOZI0p/28dda2434c4244a9c9ec4c01bc5e9f1b/image3-25.png" />
            
            </figure><p>When I click on <code>handleRequest</code>, I see the following annotated source of my Worker:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2rQDvm2MGfpBNwWgT7nobE/246a004a99f6e3322a8cd4be0e02b6f6/image5-19.png" />
            
            </figure><p>In particular, it shows that, unsurprisingly, most of the time is being spent on memory allocations in the body of the loop. Note that the profiler is not always accurate with regard to exact timing, but does paint a picture of your largest bottlenecks.</p>
    <div>
      <h3>Understanding the Profile</h3>
      <a href="#understanding-the-profile">
        
      </a>
    </div>
    <p>The profiler works by gathering a stack trace of your program at a sampled rate, so remember that the profile is an approximation of where your code tends to spend the majority of its execution time, and is not meant to be perfectly accurate. In fact, functions that execute in less than 100 microseconds have a chance to not appear in the profile at all.</p>
    <div>
      <h3>What’s Next?</h3>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>With the Workers platform, we’re striving to build in the <a href="https://www.cloudflare.com/learning/performance/what-is-observability/">observability</a> our users expect out of a robust compute platform. We’d love to hear more from the community about ways we can improve visibility into the code you’re writing on the edge. If you haven’t already, please join the Cloudflare Workers Discord community at <a href="https://discord.gg/PX8s2TmJ7s">https://discord.gg/PX8s2TmJ7s</a>. Happy building!</p><div></div><p></p> ]]></content:encoded>
            <category><![CDATA[Speed Week]]></category>
            <category><![CDATA[Wrangler]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">5qfyGhFsuFodg8NQiKq0BX</guid>
            <dc:creator>Joshua Nelson</dc:creator>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
        <item>
            <title><![CDATA[Serverless Rendering with Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/serverless-rendering-with-cloudflare-workers/</link>
            <pubDate>Fri, 17 Jul 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ Learning how to do server-side rendering at the network edge using Workers Sites, Wrangler, HTMLRewriter, and tools from the broader Workers platform. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Cloudflare’s Workers platform is a powerful tool; a single compute platform for tasks as simple as manipulating requests or complex as bringing application logic to the network edge. Today I want to show you how to do server-side rendering at the network edge using Workers Sites, Wrangler, HTMLRewriter, and tools from the broader Workers platform.</p><p>Each page returned to the user will be static HTML, with dynamic content being rendered on our serverless stack upon user request. Cloudflare’s ability to run this across the global network allows pages to be rendered in a distributed fashion, close to the user, with miniscule cold start times for the application logic. Because this is all built into Cloudflare’s edge, we can implement caching logic to significantly reduce load times, support link previews, and maximize SEO rankings, all while allowing the site to feel like a dynamic application.</p>
    <div>
      <h3>A Brief History of Web Pages</h3>
      <a href="#a-brief-history-of-web-pages">
        
      </a>
    </div>
    <p>In the early days of the web pages were almost entirely static - think raw HTML. As Internet connections, browsers, and hardware matured, so did the content on the web. The world went from static sites to more dynamic content, powered by technologies like CGI, PHP, Flash, CSS, JavaScript, and many more.</p><p>A common paradigm in those maturing days was Server Side Rendering of web pages. To accomplish this, a user would request a page with some supplied parameters, a server would generate a static web page using those incoming parameters, and return that static HTML back to the user. These web pages were easily cacheable by proxies and other downstream services, an important benefit in the world of slower Internet connection speeds. Time to Interactive (TTI) in this model is usually faster than other rendering methods, as render-blocking JavaScripts are avoided.</p><p>This paradigm fell out of style as the web standardized and powerful hardware became easier to access. Time To First Byte (TTFB) is a concern with Server Side Rendering as this model incurs latency across the Internet and the latency of rendering pages on the server itself. Client side rendering allowed for a more seamless user experience for dynamic content. As a result of this shift, client applications became larger and larger, and SEO crawlers quickly had to adopt frameworks to be able to emulate the browser logic that is able to run and render these client applications. Tied into this is the idea of AJAX requests, allowing content on the single page application to change without the need for a full page reload. Application state is changed by requesting asynchronous updates from the server and allowing the client side application to update state based on the data returned by the server. This was great, it gave us amazingly interactive applications like Google Mail.</p><p>While this is a great structure for dynamic applications, rendering on the client side has a side effect of reducing shareability of content via link previews, increases time to interactive (TTI), and reduces SEO rankings on many search engines.</p><p>With Cloudflare’s Workers platform, you can get the benefits of server side rendering with greatly reduced latency concerns. The dynamic web pages in this example are delivered from any one of Cloudflare’s edge nodes, with application logic running upon request from the user. Server side rendering often leads to content that is more easily cacheable by downstream appliances; delivering better SEO rankings and obfuscating application logic from savvy users.</p><p>You get all the benefits of the old way things were done, with all the speed of the modern web.</p>
    <div>
      <h3>Peer With Cloudflare, a Dynamic Web App</h3>
      <a href="#peer-with-cloudflare-a-dynamic-web-app">
        
      </a>
    </div>
    <p>Without further ado, let’s dive into building a dynamic web page using the Cloudflare Workers platform! This example leverages <a href="https://workers.cloudflare.com/sites">Workers Sites</a>, which allows you to serve static web pages from <a href="/workers-kv-is-ga/">Cloudflare’s Key Value store</a>. From there, Workers application logic (using <a href="https://developers.cloudflare.com/workers/reference/apis/html-rewriter/">HTMLRewriter</a>) transforms that static response based on user input to deliver modified responses with the requested data embedded in the returned web page.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6XuG4SNJszaYu3CHgIsG9X/415a3248cca3ca4ca53ebfbba553e8db/image4-6.png" />
            
            </figure><p>The Peer With Cloudflare application, hosted on <a href="https://peering.rad.workers.dev/">peering.rad.workers.dev</a></p><p><a href="https://www.peeringdb.com/">PeeringDB</a> is a user-maintained public database of networks, exchanges, facilities, and interconnection on the Internet. The <a href="https://peering.rad.workers.dev/">Peer With Cloudflare</a> (PWC) application leverages the <a href="https://www.peeringdb.com/">PeeringDB</a> API to query live information on facilities and exchange points from multiple ASNs, compares the resulting networks, and lists to the user shared exchanges and facilities. In this example, we’ll also explore using templating languages in conjunction with Cloudflare’s HTMLRewriter.</p>
    <div>
      <h3>Generate a Workers Site</h3>
      <a href="#generate-a-workers-site">
        
      </a>
    </div>
    <p>We’ll start by <a href="https://developers.cloudflare.com/workers/sites/start-from-scratch/">generating a workers site</a> using <a href="https://developers.cloudflare.com/workers/tooling/wrangler/install/">wrangler</a>.</p>
            <pre><code>&gt; wrangler generate --site peering</code></pre>
            <p>PWC will be entirely served from <code>index.html</code>, which will be generated in the <code>/public</code> directory. Next, ensure that we only serve <code>index.html</code>, regardless of the path supplied by the user. Modify <code>index.js</code> to serve a single page application, using the <a href="https://github.com/cloudflare/kv-asset-handler#servesinglepageapp">serveSinglePageApp</a> method.</p>
            <pre><code>import { getAssetFromKV, serveSinglePageApp } from '@cloudflare/kv-asset-handler'

addEventListener('fetch', event =&gt; {
  try {
    event.respondWith(handleEvent(event))
  } catch (e) {
    if (DEBUG) {
      return event.respondWith(
        new Response(e.message || e.toString(), {
          status: 500,
        }),
      )
    }
    event.respondWith(new Response('Internal Error', { status: 500 }))
  }
})

async function handleEvent(event) {
  /**
   * You can add custom logic to how we fetch your assets
   * by configuring the function `mapRequestToAsset`.
   * In this case, we serve a single page app from index.html.
   */

  const response = await getAssetFromKV(event, { mapRequestToAsset: serveSinglePageApp })
  return response 
}</code></pre>
            <p>Workers Sites will now load up <code>index.html</code> (in the /public directory) regardless of the supplied URL path. This means we can apply the application to any route on the site, and have the same user experience. We define this in our <code>wrangler.toml</code> under the <code>[site]</code> section.</p>
            <pre><code>[site]
bucket="./public"
entry-point="./"</code></pre>
            
    <div>
      <h3>Use URL Parameters to Control Application State</h3>
      <a href="#use-url-parameters-to-control-application-state">
        
      </a>
    </div>
    <p>The application itself needs a way to store state between requests. There are multiple methods to do so, but in this case URL query parameters are used for two primary reasons:</p><ul><li><p>Users can use browser-based search functionality to quickly look up an ASN and compare it with Cloudflare’s network</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6zddbmbRYJKbo5A2UJUvLZ/4b74e0b0fb547f05d3da69bd579ae038/image5-3.png" />
            
            </figure><ul><li><p>State can be stored in a single search parameter for the purposes of this application, and the null state can be handled easily</p></li></ul><p>Modify <code>index.js</code> to read in the <code>asn</code> search parameter:</p>
            <pre><code>async function handleEvent(event) {
  const response = await getAssetFromKV(event, { mapRequestToAsset: serveSinglePageApp })
  const url = new URL(event.request.url) // create a URL object from the request url
  const asn = url.searchParams.get('asn') // get the 'asn' parameter
}</code></pre>
            <p>PWC will have three cases to cover with regards to application state:</p><ol><li><p>Null state (no ASN is provided). In this case we can simply return the vanilla <code>index.html</code> page</p></li><li><p>ASN is provided and has an entry on PeeringDB’s API</p></li><li><p>ASN is provided but is malformed or has no PeeringDB entry</p></li></ol>
            <pre><code>try {
  if (asn) {
    // B) asn is provided
  } else { 
    return response 
    // A) no asn is provided; return index.html
  }  
} catch (e) {
  // C) error state
}</code></pre>
            <p>To provide the initial state and styling of the PWC application, index.html uses a third party framework called <a href="https://milligram.io/">milligram</a>, chosen due to its lightweight nature, which requires <a href="https://necolas.github.io/normalize.css/">normalize.css</a> and the <a href="https://fonts.google.com/specimen/Roboto">Roboto</a> font family. Also defined is a custom style for basic formatting. For state storage, a form is defined such that upon submission a GET request is sent to #, which is effectively a request to self with supplied parameters. The parameter in this case is named asn and must be a number:</p>
            <pre><code>&lt;!doctype html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic"&gt;
        &lt;link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.css"&gt;
        &lt;link href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.min.css" rel="stylesheet"/&gt;
        &lt;style&gt;
            .centered {
                max-width: 80rem;
            }
        &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="formContainer" class="centered container"&gt;
            &lt;h2 class="title"&gt;Peer With Cloudflare&lt;/h1&gt;
            &lt;p class="description"&gt;Welcome to the peering calculator, built entirely on Cloudflare Workers. Input an ASN below to see where it peers with Cloudflare's network.&lt;/p&gt;
            &lt;form action="#" method="GET"&gt;
              &lt;fieldset&gt;
                &lt;label for="asnField" class=""&gt;ASN&lt;/label&gt;
                &lt;input type="number" placeholder="13335" id="asnField" name="asn"&gt;
              &lt;/fieldset&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
            
    <div>
      <h3>Modelling Data from a Third Party API</h3>
      <a href="#modelling-data-from-a-third-party-api">
        
      </a>
    </div>
    <p>The <a href="https://peeringdb.com/apidocs/#operation/list%20net">PeeringDB API</a> defines networks primarily with metadata outlining key information about the network and owners, as well as two lists of public peering exchange points and private peering facilities. The PWC application will list any peering points (exchanges and facilities) shared between the user-provided network and Cloudflare’s network in a single table. PWC uses a model-view paradigm to retrieve, store, and display these data from the PeeringDB API. Defined below are the three data models representing a Network, Facility, and Exchange.</p><p>To define a network, first inspect a sample response from the PeeringDB API (use <a href="https://peeringdb.com/api/net?asn__in=13335&amp;depth=2">https://peeringdb.com/api/net?asn__in=13335&amp;depth=2</a> for a sample from Cloudflare’s network). Some key pieces of information displayed in PWC are the network name, website, notes, exchanges, and facilities.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1X1V64b4YC9iQPMQfwhJv2/d68e6aab43901b6e0eb4aac1f095b374/image7.png" />
            
            </figure><p><code>Network</code> begins with a constructor to initialize itself with an Autonomous System Number. This is used for lookup of the network from the PeeringDB API:</p>
            <pre><code>export class Network {
	constructor(asn) {
		this.asn = asn
	}</code></pre>
            <p>A <code>populate()</code> function is then implemented to fetch information from a third party API and fill in required data. The <code>populate()</code> method additionally creates instances of <code>NetworkFacility</code> and <code>NetworkExchange</code> objects to be stored as attributes of the <code>Network</code> model.</p>
            <pre><code>async populate(){
		const net = await findAsn(this.asn)
		this.id = net['id']
		this.name = net['name']
		this.website = net['website']
		this.notes = net['notes']

		this.exchanges = {}
		for (let i in net['netixlan_set']) {
			const netEx = new NetworkExchange(net['netixlan_set'][i])
			this.exchanges[netEx.id] = netEx
		}

		this.facilities = {}
		for (let i in net['netfac_set']) {
			const netFac = new NetworkFacility(net['netfac_set'][i])
			this.facilities[netFac.id] = netFac
		}
		return this
	}</code></pre>
            <p>Any <code>Network</code> defined in the PWC application can compare itself to another <code>Network</code> object. This generic approach allows PWC to be extended to arbitrary network comparison in the future. To accomplish this, implement a <code>compare()</code> and <code>compareItems()</code> function to compare both <code>NetworkExchanges</code> and <code>NetworkFacilities</code>.</p>
            <pre><code>compareItems(listA, listB, sharedItems) {
		for (let key in listA) {
			if(listB[key]) {
				sharedItems[key] = listA[key]
			}
		}
		return sharedItems
	}

	async compare(network) {
		const sharedFacilities = this.compareItems(this.facilities, network.facilities, {})
		const sharedExchanges = this.compareItems(this.exchanges, network.exchanges, {})
		return await fetchAdditionalDetails(sharedFacilities, sharedExchanges)
	}</code></pre>
            <p>Both the <code>NetworkFacility</code> and <code>NetworkExchange</code> models implement a constructor to initialize with supplied data, as well as a populate method to add in extra information. These models also take care of converting PeeringDB API information into more human-readable formats.</p>
            <pre><code>export class NetworkFacility {
	constructor(netfac){
		this.name = netfac['name']
		this.id = netfac['fac_id']
		this.type = 'Facility'
		this.url = `https://peeringdb.com/fac/${this.id}`
		this.location = netfac['city'] + ", " + netfac['country']
	}

	populate(details) {
		this.networks = details['net_count']
		this.website = details['website']
	}
}

export class NetworkExchange {
	constructor(netixlan){
		this.id = netixlan['ix_id']		
		this.name = netixlan['name']
		this.type = 'Exchange'
		this.url = `https://peeringdb.com/fac/${this.id}`
	}

	populate(details) {
		this.website = details['website']
		this.networks = details['net_count']
		this.location = details['city'] + ", " + details['country']
	}
}</code></pre>
            <p>Notice that the <code>compare()</code> and <code>populate()</code> functions call out to <code>fetchAdditionalDetails</code> and <code>findAsn</code> methods; these are implemented to gather additional information for each model. Both methods are implemented in an ‘interface’ under <code>src/utils/</code>.</p>
            <pre><code>import {peeringDb} from './constants'

async function fetchPdbData(path) {
	const response = await fetch(new Request(peeringDb['baseUrl'] + path))
	const body = await response.json()
	return body['data']
}

async function fetchAdditionalDetails(facilities, exchanges) {
	const sharedItems = []
	if (Object.keys(facilities).length &gt; 0) {
		const facilityDetails = await fetchPdbData(peeringDb['facEndpoint'] + "?id__in=" + Object.keys( facilities ).join(","))
		for (const facility of facilityDetails) {
			facilities[facility.id].populate(facility)
			sharedItems.push(facilities[facility.id])
		}
	}
	if (Object.keys(exchanges).length &gt; 0) {
		const exchangeDetails = await fetchPdbData(peeringDb['ixEndpoint'] + "?id__in=" + Object.keys( exchanges ).join(","))
		for (const exchange of exchangeDetails) {
			exchanges[exchange.id].populate(exchange)
			sharedItems.push(exchanges[exchange.id])
		}
	}
	return sharedItems
}

async function findAsn(asn) {
	const data = await fetchPdbData(peeringDb['netEndpoint'] + "?" + `asn__in=${asn}&amp;depth=2`)
	return data[0]
}

export {findAsn, fetchAdditionalDetails}</code></pre>
            
    <div>
      <h3>Presenting Results using HTMLRewriter</h3>
      <a href="#presenting-results-using-htmlrewriter">
        
      </a>
    </div>
    <p>In building a single page application with workers, the PWC application needs the ability to modify HTML responses returned to the user. To accomplish this, PWC uses Cloudflare’s <a href="https://developers.cloudflare.com/workers/reference/apis/html-rewriter/">HTMLRewriter interface</a>. HTMLRewriter streams any supplied response through a transformer, applying any supplied transformations to the raw response object. This returns a modified response object that can then be returned to the user.</p><p>In the case of PWC, three cases need to be handled, and two of them require some form of transformation before returning <code>index.html</code> to the user. Define a generic <code>AsnHandler</code> to provide to the user their supplied ASN. The <code>element()</code> method in this handler will simply set a value attribute on the target element.</p>
            <pre><code>class AsnHandler {
	constructor(asn) {
		this.asn = asn
	}
	element(element) {
		element.setAttribute("value", this.asn)
	}
}</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/01eXPqcRsAXswLKSuCNZDD/d3f387616e62637a648830e33b1f35f0/image8.png" />
            
            </figure><p>The ASNHandler fills the form field with the user-supplied ASN.</p><p>For error cases, PWC needs to provide feedback to the user that the supplied ASN was not found on PeeringDB. In this case a simple header tag is appended to the target element.</p>
            <pre><code>class ErrorConditionHandler {
	constructor(asn) {
		this.asn = asn
	}
	element(element) {
		element.append(`&lt;h4&gt;ASN ${this.asn} Not Found on PeeringDB&lt;/h4&gt;`, {html: true})
	}
}</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2xhrCWxNRY1k50sFpml5pq/ccb975e2953d6fbba89fdad0b4d1dedf/image6-1.png" />
            
            </figure><p>The ErrorConditionHandler provides feedback on invalid user-supplied input.</p><p>For cases where a result needs to be returned, a NetworkComparisonHandler is implemented. Instead of defining raw HTML in a string format, NetworkComparisonHandler uses a templating language (<a href="https://handlebarsjs.com/">Handlebars</a>) to provide a dynamic transformation based on data returned from PeeringDB. First, install both handlebars and handlebars loader with npm:</p>
            <pre><code>&gt; npm install handlebars handlebars-loader</code></pre>
            <p>Now define the <code>NetworkComparisonHandler</code>, including an import of the <code>networkTable</code> template.</p>
            <pre><code>import networkTable from '../templates/networktable.hbs'

class NetworkComparisonHandler {
	constructor({cfNetwork, otherNetwork, sharedItems}) {
		this.sharedItems = sharedItems
		this.otherNetwork = otherNetwork
		this.cfNetwork = cfNetwork
	}

	element(element) {
		element.append(networkTable(this), { html: true })
	}
}</code></pre>
            <p>The Handlebars template itself uses conditional logic to handle cases where there is no direct overlap between the two supplied networks, and a custom helper to provide references to each piece of returned data. Handlebars provides an easy-to-read interface for conditional logic, iteration, and custom views.</p>
            <pre><code>{{#if this.sharedItems.length}}
  &lt;h4&gt;Shared facilities and exchanges between {{this.cfNetwork.name}} and {{this.otherNetwork.name}}&lt;/h4&gt;
  &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;Name&lt;/th&gt;
            &lt;th&gt;Location&lt;/th&gt;
            &lt;th&gt;Networks&lt;/th&gt;
            &lt;th&gt;Type&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
        {{#each this.sharedItems}}
          &lt;tr&gt;
            &lt;td&gt;{{link this.name this.url}}&lt;/td&gt;
            &lt;td&gt;{{this.location}}&lt;/td&gt;
            &lt;td&gt;{{this.networks}}&lt;/td&gt;
            &lt;td&gt;{{this.type}}&lt;/td&gt;
          &lt;/tr&gt;
        {{/each}}
      &lt;/tbody&gt;
  &lt;/table&gt;
{{else}}
  &lt;h4&gt;No shared exchanges or facilities between {{this.cfNetwork.name}} and {{this.otherNetwork.name}}&lt;/h4&gt;
{{/if}}</code></pre>
            <p>A custom link helper is used to display an <code>&lt;a&gt;</code> tag with a reference to each datum.</p>
            <pre><code>import handlebars from 'handlebars'

export default function(text, url) {
	return new handlebars.SafeString("&lt;a href='" + handlebars.escapeExpression(url) + "'&gt;" + handlebars.escapeExpression(text) + "&lt;/a&gt;");
}</code></pre>
            <p>Great! Handlebars and other templating languages are extremely useful for building complex view logic into Cloudflare’s HTMLRewriter. To tie Handlebars into our build process, and have wrangler understand the currently foreign code, modify <code>wrangler.toml</code> to use a custom webpack configuration:</p>
            <pre><code>type = "webpack"
webpack_config = "webpack.config.js"</code></pre>
            <p>In <code>webpack.config.js</code>, configure any .hbs files to be compiled using the handlebars-loader module. <a href="https://developers.cloudflare.com/workers/tooling/wrangler/webpack">Custom webpack configurations</a> can be used in conjunction with Wrangler to create more complex build schemes, including environment-specific schemes.</p>
            <pre><code>module.exports = {
  target: 'webworker',
  entry: './index.js',
  module: {
    rules: [{ test: /\.hbs$/, loader: 'handlebars-loader' }],
  }
}</code></pre>
            <p>Time to tie it all together in <code>index.js</code>! Handle each case by returning to the user either a raw HTML response or a modified response using HTMLRewriter. The <code>#asnField</code> will be updated, and the <code>#formContainer</code> will be used to present either an error message or a table of results.</p>
            <pre><code>async function handleEvent(event) {
  const response = await getAssetFromKV(event, { mapRequestToAsset: serveSinglePageApp })
  const url = new URL(event.request.url)
  const asn = url.searchParams.get('asn')

  try {
    if (asn) {
      const cfNetwork = await new Network(cloudflare['asn']).populate()
      const otherNetwork = await new Network(asn).populate()
      const sharedItems = await cfNetwork.compare(otherNetwork)
      return await new HTMLRewriter()
        .on('#asnField', new AsnHandler(asn))
        .on('#formContainer', new NetworkComparisonHandler({cfNetwork, otherNetwork, sharedItems}))
        .transform(response)
    } else { return response }  
  } catch (e) {
    return await new HTMLRewriter()
      .on('#asnField', new AsnHandler(asn))
      .on('#formContainer', new ErrorConditionHandler(asn))
      .transform(response)
  }
}</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/11celWv8AT3jGI8zBpT6hN/58a0ff4e216d0a3c6078e30c917564df/image2-6.png" />
            
            </figure><p>The NetworkComparisonHandler and associated Handlebars template allows PWC to present PeeringDB information in a user-friendly format.</p>
    <div>
      <h3>Publish to Cloudflare</h3>
      <a href="#publish-to-cloudflare">
        
      </a>
    </div>
    <p>You can view the final code <a href="https://github.com/kabirsikand/peering">on Github</a>, or navigate to <a href="https://peering.rad.workers.dev/">peering.rad.workers.dev</a> to see a working example. The final <code>wrangler.toml</code> includes instructions to publish the code up to a workers.dev site, allowing you to easily build, deploy, and test without a domain - simply by setting <code>workers_dev</code> to “true”.</p>
            <pre><code>name = "peering"
type = "webpack"
webpack_config = "webpack.config.js"
account_id = "&lt;REDACTED&gt;"
workers_dev = true
route = "&lt;REDACTED&gt;"
zone_id = "&lt;REDACTED&gt;"

[site]
bucket="./public"
entry-point="./"</code></pre>
            <p>Finally, publish your code using wrangler.</p>
            <pre><code>&gt; wrangler publish</code></pre>
            
    <div>
      <h3>Cache At The Edge</h3>
      <a href="#cache-at-the-edge">
        
      </a>
    </div>
    <p>Taking advantage of our server-rendered content is as simple as matching the request against any previously cached assets. To accomplish this, add a few simple lines to the top of our <code>handleEvent</code> function using Cloudflare’s <a href="https://developers.cloudflare.com/workers/reference/apis/cache/">Cache API</a>. If an asset is found, return the response without going into the application logic.</p>
            <pre><code>async function handleEvent(event) {
  let cache = caches.default
  let response = await cache.match(event.request)
  if (response) {
    return response
  }
  response = await getAssetFromKV(event, { mapRequestToAsset: serveSinglePageApp })</code></pre>
            
    <div>
      <h3>What’s Next?</h3>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>Using the Workers platform to deploy applications allow users to load lightweight and static html, with all application logic residing on the network edge. While there are certainly a host of improvements which can be made to the Peer With Cloudflare application (use of Workers KV, more input validation, or mixing in other APIs to present more interesting information); it should present a compelling introduction to the possibilities of Workers!</p><p>Check out <a href="https://workers.cloudflare.com/built-with">Built With Workers</a> for more examples of applications built on the Workers platform, or build your own projects at <a href="https://workers.cloudflare.com/">workers.cloudflare.com</a>! For more information on peering with Cloudflare, please visit our <a href="https://www.cloudflare.com/partners/peering-portal/">Peering Portal</a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <guid isPermaLink="false">5OlY2Mj48sgHYMu0EkVBck</guid>
            <dc:creator>Kabir Sikand</dc:creator>
        </item>
    </channel>
</rss>