
<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>Wed, 15 Apr 2026 19:32:00 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Cloudflare Queues: globally distributed queues without the egress fees]]></title>
            <link>https://blog.cloudflare.com/introducing-cloudflare-queues/</link>
            <pubDate>Tue, 27 Sep 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare Queues is a message queuing service that allows applications to reliably send and receive messages using Cloudflare Workers ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Developers continue to build more complex applications on Cloudflare’s Developer Platform. We started with Workers, which brought compute, then introduced KV, Durable Objects, R2, and soon D1, which enabled persistence. Now, as we enable developers to build larger, more sophisticated, and more reliable applications, it’s time to unlock another foundational building block: messaging.</p><p>Thus, we’re excited to announce the private beta of Cloudflare Queues, a global message queuing service that allows applications to reliably send and receive messages using <a href="https://workers.cloudflare.com/">Cloudflare Workers</a>. It offers at-least once message delivery, supports batching of messages, and charges no bandwidth egress fees. Let’s queue up the details.</p>
    <div>
      <h3>What is a Queue?</h3>
      <a href="#what-is-a-queue">
        
      </a>
    </div>
    <p>Queues enable developers to send and receive messages with a guarantee of delivery. Think of it like the postal service for the Internet. You give it a message, then it handles all the hard work to ensure the message gets delivered in a timely manner. Unlike the <i>real</i> postal service, where it’s possible for a message to get lost, Queues provide a guarantee that each message is delivered at-least once; no matter what. This lets you focus on your application, rather than worry about the chaos of transactions, retries, and backoffs to prevent data loss.</p><p>Queues also allow you to scale your application to process large volumes of data. Imagine a million people send you a package in the mail, at the same time. Instead of a million postal workers suddenly showing up at your front door, you would want them to aggregate your mail into batches, then ask you when you’re ready to receive each batch. This lets you decouple and spread load among services that have different throughput characteristics.</p>
    <div>
      <h3>How does it work?</h3>
      <a href="#how-does-it-work">
        
      </a>
    </div>
    <p>Queues are integrated into the fabric of the Cloudflare Workers runtime, with simple APIs that make it easy to send and receive messages. First, you’ll want to send messages to the Queue. You can do this by defining a Worker, referred to as a "producer," which has a binding to the Queue.</p><p>In the example below, a Worker catches JavaScript exceptions and sends them to a Queue. You might notice that any object can be sent to the Queue, including an error. That’s because messages are encoded using the standard <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm">structuredClone()</a> algorithm.</p>
            <pre><code>export default {
  async fetch(request: Request, env: Environment) {
     try {
       return await doRequest(request);
     } catch (error) {
       await env.ERROR_QUEUE.send(error);
       return new Response(error.stack, { status: 500 });
     }
  }
}</code></pre>
            <p>Second, you’ll want to process messages in the Queue. You can do this by defining a Worker, referred to as the "consumer," which will receive messages from the Queue. To facilitate this, there is a new type of event handler, named "queue," which receives the messages sent to the consumer.</p><p>This Worker is configured to receive messages from the previous example. It appends the stack trace of each Error to a log file, then saves it to an R2 bucket.</p>
            <pre><code>export default {
  async queue(batch: MessageBatch&lt;Error&gt;, env: Environment) {
     let logs = "";
     for (const message of batch.messages) {
        logs += message.body.stack;
     }
     await env.ERROR_BUCKET.put(`errors/${Date.now()}.log`, logs);
  }
}</code></pre>
            <p>Configuration is also easy. You can change the message batch size, message retries, delivery wait time, and dead-letter queue. Here’s a snippet of the <code>wrangler.toml</code> configuration when deploying with <a href="https://github.com/cloudflare/wrangler2">wrangler</a>, our command-line interface</p>
            <pre><code>name = "my-producer"
[queues]
producers = [{ queue = "errors", binding = "ERROR_QUEUE" }]
# ---
name = "my-consumer"
[queues]
consumers = [{ queue = "errors", max_batch_size = 100, max_retries = 3 }]</code></pre>
            <p>Above are two different <code>wrangler.toml</code>s, one for a producer and another for a consumer. It is also possible for a producer and consumer to be implemented by the same Worker. To see the full list of options and examples, see the <a href="https://developers.cloudflare.com/queues">documentation</a>.</p>
    <div>
      <h3>What can you build with it?</h3>
      <a href="#what-can-you-build-with-it">
        
      </a>
    </div>
    <p>You can use Cloudflare Queues to defer tasks and guarantee they get processed, decouple load among different services, batch events and process them together, and send messages from Worker to Worker.</p><p>To demonstrate, we’ve put together a demo <a href="https://github.com/Electroid/queues-demo#cloudflare-queues-demo">application</a> that you can run on your local machine using <a href="https://github.com/cloudflare/wrangler2">wrangler</a>. It shows how Queues can batch messages and handle failures in your code, here’s a preview of it in action:</p><div></div><p>In addition to batching, here are other examples of what you can build with Queues:</p><ul><li><p>Off-load tasks from the critical path of a Workers request.</p></li><li><p>Guarantee messages get delivered to a service that talks HTTP.</p></li><li><p>Transform, filter, and fan-out messages to multiple Queues.</p></li></ul><p>Cloudflare Queues gives you the flexibility to decide where to route messages. Instead of static configuration files that define routing keys and patterns, you can use JavaScript to define custom logic for how you filter and fan-out to multiple Queues. In the next example, you can distribute work to different Queues based on the attributes of a user.</p>
            <pre><code>export default {
  async queue(batch: MessageBatch, env: Environment) {
    for (const message of batch.messages) {
      const user = message.body;
      if (isEUResident(user)) {
        await env.EU_QUEUE.send(user);
      }
      if (isForgotten(user)) {
        await env.DELETION_QUEUE.send(user);
      }
    }
  }
}</code></pre>
            <p>We will also support integrations with Cloudflare products, like R2. For example, you might configure an R2 bucket to send lifecycle events to a Queue or archive messages to a R2 bucket for long-term storage.</p>
    <div>
      <h3>How much does it cost?</h3>
      <a href="#how-much-does-it-cost">
        
      </a>
    </div>
    <p>Cloudflare Queues has a simple, transparent pricing model that’s easy to predict. It costs $0.40 per million operations, which is defined for every 64 KB chunk of data that is written, read, or deleted. There are also no <a href="/aws-egregious-egress/">egregious</a> bandwidth fees for data in <i>or</i> out -- unlike Amazon's SQS or Google’s Pub/Sub.</p><p>To effectively deliver a message, it usually takes three operations: one write, one read, and one acknowledgement. You can estimate your usage by considering the cost of messages delivered, which is $1.20 per million. (calculated as 3 x \$0.40)</p>
    <div>
      <h3>When can I try it?</h3>
      <a href="#when-can-i-try-it">
        
      </a>
    </div>
    <p>You can <a href="http://www.cloudflare.com/lp/queues">register</a> to join the waitlist as we work towards a beta launch. You’ll have an opportunity to try it out, for free. Once it’s ready, we’ll launch an open beta for everyone to try.</p><p>In the meantime, you can read the <a href="https://developers.cloudflare.com/queues">documentation</a> to view our code samples, see which features will be supported, and learn what you can build. If you’re in our developer Discord, you stay up-to-date by joining the <a href="https://discord.gg/rrZXVVcKQF">#queues-beta</a> channel. If you’re an Enterprise customer, reach out to your account team to schedule a session with our Product team.</p><p>We’re excited to see what you build with Cloudflare Queues. Let the queuing begin!</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Queues]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">3qv2Vf3jyVruQHqBPiBzh3</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare Observability]]></title>
            <link>https://blog.cloudflare.com/vision-for-observability/</link>
            <pubDate>Fri, 18 Mar 2022 21:03:00 GMT</pubDate>
            <description><![CDATA[ Being a single pane of glass for all network activity has always been one of Cloudflare’s goals. Today, we’re outlining the future vision for Cloudflare observability. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3bfLNf93KkwbzGrHAI7Eoo/c9afa97526fe27a4d3c1350bb97237aa/Observability---Bringing-Logs-in-Dash.png" />
            
            </figure><p>Whether you’re a software engineer deploying a new feature, network engineer updating routes, or a security engineer configuring a new firewall rule: You need visibility to know if your system is behaving as intended — and if it’s not, to know how to fix it.</p><p>Cloudflare is committed to helping our customers get visibility into the services they have protected behind Cloudflare. Being a single pane of glass for all network activity has always been one of Cloudflare’s goals. Today, we’re outlining the future vision for Cloudflare observability.</p>
    <div>
      <h3>What is observability?</h3>
      <a href="#what-is-observability">
        
      </a>
    </div>
    <p><a href="https://www.cloudflare.com/learning/performance/what-is-observability/">Observability</a> means gaining visibility into the internal state of a system. It’s used to <a href="https://www.cloudflare.com/application-services/solutions/app-performance-monitoring/">give users the tools</a> to figure out what’s happening, where it’s happening, and why.</p><p>At Cloudflare, we believe that observability has three core components: monitoring, analytics, and forensics. Monitoring measures the health of a system - it tells you when something is going wrong. Analytics give you the tools to visualize data to identify patterns and insights. Forensics helps you answer very specific questions about an event.</p><p>Observability becomes particularly important in the context of security to validate that any mitigating actions performed by our security products, such as Firewall or Bot Management, are not false positives. Was that request correctly classified as malicious? And if it wasn’t, which detection system classified it as such?</p><p>Cloudflare, additionally, has products to improve performance of applications and corporate networks and allow developers to write lightning fast code that runs on our global network. We want to be able to provide our customers with insights into every request, packet, and fetch that goes through Cloudflare’s network.</p>
    <div>
      <h3>Monitoring and Notifying</h3>
      <a href="#monitoring-and-notifying">
        
      </a>
    </div>
    <p>Analytics are fantastic for summarizing data, but how do you know <i>when</i> to look at them? No one wants to sit on the dashboard clicking refresh over and over again just in case something looks off. That’s where notifications come in.</p><p>When we talk about something “looking off” on an analytics page, what we really mean is that there’s a significant change in your traffic or network which is reflected by spikes or drops in our analytics. Availability and performance directly affect end users, and our goal is to monitor and notify our customers as soon as we see things going wrong.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1l5DpBwxWnfWRiyAIpwMxo/b85ae5d80dc26967acc17190e3ad633f/Untitled--1-.png" />
            
            </figure><p>Today, we have many different types of notifications from <a href="/smarter-origin-service-level-monitoring/">Origin Error Rates</a>, <a href="/get-notified-when-your-site-is-under-attack/">Security Events, and Advanced Security Events</a> to Usage Based Billing and <a href="/health-check-analytics-and-how-you-can-use-it/">Health Checks</a>. We’re continuously adding more notification types to have them correspond with our awesome analytics. As our analytics get more customizable, our notifications will as well.</p><p>There’s tons of different algorithms that can be used to detect spikes, including using burn rates and z-scores. We’re continuing to iterate on the algorithms that we use for detections to offer more variations, make them smarter, and make sure that our notifications are both accurate and not too noisy.</p>
    <div>
      <h3>Analytics</h3>
      <a href="#analytics">
        
      </a>
    </div>
    <p>So, you’ve received an alert from Cloudflare. What comes next?</p><p>Analytics can be used to get a birds eye view of traffic or focus on specific types of events by adding filters and time ranges. After you receive an alert, we want to show you exactly what’s been triggered through graphs, high level metrics, and top Ns on the Cloudflare dashboard.</p><p>Whether you’re a developer, security analyst, or network engineer, the Cloudflare dashboard should be the spot for you to see everything you need. We want to make the dashboard more customizable to serve the diverse use cases of our customers. Analyze data by specifying a timeframe and filter through dropdowns on the dashboard, or build your own metrics and graphs that work alongside the raw logs to give you a clear picture of what's happening.</p><p>Focusing on security, we believe analytics are the best tool to build confidence before deploying security policies. Moving forward, we plan to layer all of our security related detection signals on top of HTTP analytics so you can use the dashboard to answer questions such as: if I were to block all requests that the <a href="https://www.cloudflare.com/learning/ddos/glossary/web-application-firewall-waf/">WAF</a> identifies as an XSS attack, what would I block?</p><p>Customers using our enterprise Bot Management may already be familiar with this experience, and as we improve it and build upon it further, all of our other security products will follow.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1bJEpCdWDMbU6pcepmsoXW/565e8bbd48406f35bc8faffc42d4de0f/Screenshot-2022-03-18-at-15.24.11.png" />
            
            </figure><p>Analytics are a powerful tool to see high level patterns and identify anomalies that indicate that something unusual is happening. We’re working on new dashboards, customizations, and features that widen the use cases for our customers. Stay tuned!</p>
    <div>
      <h3>Logs</h3>
      <a href="#logs">
        
      </a>
    </div>
    <p>Logs are used when you want to examine specific details about an event. They consist of a timestamp and fields that describe the event and are used to get visibility on a granular level when you need a play-by-play.</p><p>In each of our datasets, an event measures something different. For example, in HTTP request logs, an event is when an end user requests content from or sends content to a server. For Firewall logs, an event occurs when the Firewall takes an action on an HTTP request. There can be multiple Firewall events for each HTTP request.</p><p>Today, our customers access logs using Logpull, Logpush, or Instant Logs. Logpull and Logpush are great for customers that want to send their logs to third parties (like our <a href="https://www.cloudflare.com/partners/analytics/">Analytics Partners</a>) to store, analyze, and correlate with other data sources. With Instant Logs, our customers can monitor and troubleshoot their traffic in real-time straight from the dashboard or CLI. We’re planning on building out more capabilities to dig into logs on Cloudflare. We’re hard at work on building <a href="/store-your-cloudflare-logs-on-r2/">log storage on R2</a> - but what’s next?</p><p>We’ve heard from customers that the activity log on the Firewall analytics dashboard is incredibly useful. We want to continue to bring the power of logs to the dashboard by adding the same functionality across our products. For customers that will store their logs on Cloudflare R2, this means that we can minimize the use of sampled data.</p><p>If you’re looking for something very specific, querying logs is also important, which is where forensics comes in. The goal is to let you investigate from high level analytics all the way down to individual logs lines that make them up. Given a unique identifier, such as the ray ID, you should be able to look up a single request, and then correlate it with all other related activity. Find out the client IP of that ray ID and from there, use cases are plentiful: what other requests from this IP are malicious? What paths did the client follow?</p>
    <div>
      <h3>Tracing</h3>
      <a href="#tracing">
        
      </a>
    </div>
    <p>Logs are really useful, but they don’t capture the context around a request. Traces show the end-to-end life cycle of a request from when a user requests a resource to each of the systems that are involved in its delivery. They’re another way of applying forensics to help you find something very specific.</p><p>These are used to differentiate each part of the application to identify where errors or bottlenecks are occurring. Let's say that you have a Worker that performs a fetch event to your origin and a third party API. Analytics can show you average execution times and error rates for your Worker, but it doesn’t give you visibility into each of these operations.</p><p>Using wrangler dev and console.log statements are really helpful ways to test and debug your code. They bring some of the visibility that’s needed, but it can be tedious to instrument your code like this.</p><p>As a developer, you should have the tools to understand what’s going on in your applications so you can deliver the best experience to your end users. We can help you answer questions like: Where is my Worker execution failing? Which operation is causing a spike in latency in my application?</p>
    <div>
      <h3>Putting it all together</h3>
      <a href="#putting-it-all-together">
        
      </a>
    </div>
    <p>Notifications, analytics, logs, and tracing each have their distinct use cases, but together, these are powerful tools to provide analysts and developers visibility. Looking forward, we’re excited to bring more and more of these capabilities on the Cloudflare dashboard.</p><p>We would love to hear from you as we build these features out. If you’re interested in sharing use cases and helping shape our roadmap, contact your account team!</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Analytics]]></category>
            <category><![CDATA[Bots]]></category>
            <guid isPermaLink="false">23Q8iqhgSAOgTyAsHRfZ6s</guid>
            <dc:creator>Tanushree Sharma</dc:creator>
            <dc:creator>Natasha Wissmann</dc:creator>
            <dc:creator>Ashcon Partovi</dc:creator>
            <dc:creator>Michael Tremante</dc:creator>
        </item>
        <item>
            <title><![CDATA[wrangler 2.0 — a new developer experience for Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/wrangler-v2-beta/</link>
            <pubDate>Tue, 16 Nov 2021 13:59:22 GMT</pubDate>
            <description><![CDATA[ We're excited to announce the second-generation of our developer tooling for Cloudflare Workers. It’s a new developer experience that’s out-of-the-box, lightning fast, and can even run Workers on a local machine. (Yes!) ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Much of a developer’s work is about making trade-offs: consistency versus availability, speed over correctness, you name it. While there will always be different technical trade-offs to consider, we believe there are some that you, as a developer, should never need to make.</p><p>One of those decisions is an easy-to-use development environment. Whether you’re onboarding a new developer to your team or you simply want to develop faster, it’s important that even the smallest of things are optimized for speed and simplicity.</p><p>That’s why we're excited to announce the second-generation of our developer tooling for Cloudflare Workers. It’s a new developer experience that’s out-of-the-box, lightning fast, and can even run Workers on a local machine. (Yes!)</p><p>If you’re already familiar with our existing tools, we’re not just talking about the wrangler CLI, we’re talking about its next major release: wrangler 2.0. Stick around to get a sneak-peak at the new experience.</p>
    <div>
      <h3>No config? No problem</h3>
      <a href="#no-config-no-problem">
        
      </a>
    </div>
    <p>We’ve made it much easier to get started with Cloudflare Workers. All you need is a single JavaScript file to run a Worker -- no configuration needed. You don't even need to decide on a name!</p><p>When you run <code>wrangler dev &lt;filename&gt;</code>, your code is automatically bundled and deployed to a development environment. Then, you can send HTTP requests to that environment using a localhost proxy. Here’s what that looks like in-action:</p><div></div>
    <div>
      <h3>Live debugging, just like that</h3>
      <a href="#live-debugging-just-like-that">
        
      </a>
    </div>
    <p>Now there’s a completely redesigned experience for debugging a Worker. With a simple command you get access to a remote debugger, the same used by Chrome when you click “Inspect,” which provides an interactive view of logs, exceptions, and requests. It feels like your Worker is running locally, yet it’s actually running on the Cloudflare network.</p><div></div><p>A debugger that “just works” and auto-detects your changes makes <i>all</i> the difference when you’re just trying to code. We’ve also made a number of improvements to make the debugging experience even easier:</p><ul><li><p>Keybind shortcuts, to quickly toggle features or open a window.</p></li><li><p>Support for “--public ”, to automatically serve your static assets.</p></li><li><p>Faster and more reliable file-watching.</p></li></ul><p>To start a debugging session, just run: <code>wrangler dev &lt;filename&gt;</code>, then hit the “D” keybind.</p>
    <div>
      <h3>Local mode? Flip a switch</h3>
      <a href="#local-mode-flip-a-switch">
        
      </a>
    </div>
    <p>Another aspect of the new debugging experience is the ability to switch to “local mode,” which runs your Worker on your local machine. In fact, you can easily switch between “network” and “local” mode with just a keybind shortcut.</p><div></div><p>How does this work? Recently, we announced that <a href="https://github.com/cloudflare/miniflare">Miniflare</a> (created by <a href="https://twitter.com/_mrbbot">Brendan Coll</a>), a project to locally emulate Workers in Node.js, has joined the Cloudflare organization. Miniflare is great for unit testing and situations where you’d like to debug Workers without an Internet connection. Now we’ve integrated it directly into the local development experience, so you can enjoy the benefits of both the network and your localhost!</p>
    <div>
      <h3>Let us know what you think!</h3>
      <a href="#let-us-know-what-you-think">
        
      </a>
    </div>
    <p>Serverless should be simple. We’re really excited about these improvements to the developer experience for Workers, and we have a <i>lot</i> more planned.</p><p>While we’re still working on wrangler 2.0, you can try the beta release by running: <code>npm install wrangler@beta</code> or by visiting the <a href="https://github.com/cloudflare/wranglerv2">repository</a> to see what we’re working on. If you’re already using wrangler to deploy existing applications, we recommend continuing to use wrangler 1.0 until the 2.0 release is officially out. We will continue to develop and maintain wrangler 1.0 until we’re finished with backwards-compatibility for 2.0.</p><p>If you’re starting a project or want to try out the new experience, we’d love to hear your feedback! Let us know what we’re missing or what you’d like to see in wrangler 2.0. You can create a feature request or start a discussion in the <a href="https://github.com/cloudflare/wranglerv2">repository</a>. (we’ll merge them into the existing wrangler repository when 2.0 is out of beta).</p><p>Thank you to all of our developers out there, and we look forward to seeing what you build!</p>
    <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[Wrangler]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">1bz4lkxX4UPI3IDoREY8Ll</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
            <dc:creator>Sunil Pai</dc:creator>
        </item>
        <item>
            <title><![CDATA[JavaScript modules are now supported on Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/workers-javascript-modules/</link>
            <pubDate>Tue, 16 Nov 2021 13:58:42 GMT</pubDate>
            <description><![CDATA[ Now you can use JavaScript modules, also known as ECMAScript or “ES” modules, on Cloudflare Workers. This replaces the old “addEventListener” syntax with a new “import” and “export” semantics that makes it really easy to write reusable, modular code. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>We’re excited to announce that JavaScript modules are now supported on Cloudflare Workers. If you’ve ever taken look at an example Worker written in JavaScript, you might recognize the following code snippet that has been floating around the Internet the past few years:</p>
            <pre><code>addEventListener("fetch", (event) =&gt; {
  event.respondWith(new Response("Hello Worker!"));
}</code></pre>
            <p>The above syntax is known as the “<a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Worker</a>” API, and it was proposed to be standardized for use in web browsers. The idea is that you can attach a JavaScript file to a web page to modify its HTTP requests and responses, acting like a virtual endpoint. It was exactly what we needed for Workers, and it even integrated well with standard Web APIs like <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch"><code>fetch()</code></a> and <a href="https://developers.cloudflare.com/workers/runtime-apis/cache"><code>caches</code></a>.</p><p>Before introducing modules, we want to make it clear that we will continue to support the Service Worker API. No developer wants to get an email saying that <i>you</i> need to rewrite <i>your</i> code because an <a href="https://www.cloudflare.com/learning/security/api/what-is-an-api/">API</a> or feature is being deprecated; and you won’t be getting one from us. If you’re interested in learning why we made this decision, you can read about our commitment to <a href="/backwards-compatibility-in-cloudflare-workers/">backwards-compatibility</a> for Workers.</p>
    <div>
      <h3>What are JavaScript modules?</h3>
      <a href="#what-are-javascript-modules">
        
      </a>
    </div>
    <p>JavaScript modules, also known as ECMAScript (abbreviated. “ES”) modules, is the standard API for importing and exporting code in JavaScript. It was introduced by the “ES6” language specification for JavaScript, and has been implemented by most Web browsers, Node.js, Deno, and now Cloudflare Workers. Here’s an example to demonstrate how it works:</p>
            <pre><code>// filename: ./src/util.js
export function getDate(time) {
  return new Date(time).toISOString().split("T")[0]; // "YYYY-MM-DD"
}</code></pre>
            <p>The "export" keyword indicates that the "getDate" function should be exported from the current module. Then, you can use "import" from another module to use that function.</p>
            <pre><code>// filename: ./src/index.js
import { getDate } from "./util.js"

console.log("Today’s date:", getDate());</code></pre>
            <p>Those are the basics, but there’s a lot more you can do with modules. It allows you to organize, maintain, and re-use your code elegantly that just works. While we can’t go over <i>every</i> aspect of modules here, if you’d like to learn more we’d encourage you to read the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules">MDN guide</a> on modules, or a more <a href="https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/">technical deep-dive</a> by Lin Clark.</p>
    <div>
      <h3>How can I use modules in Workers?</h3>
      <a href="#how-can-i-use-modules-in-workers">
        
      </a>
    </div>
    <p>You can export a default module, which will represent your Worker. Instead of using "addEventListener," each event handler is defined as a function on that module. Today, we support "<a href="https://developers.cloudflare.com/workers/runtime-apis/fetch-event#syntax-module-worker">fetch</a>" for HTTP and WebSocket requests and "<a href="https://developers.cloudflare.com/workers/runtime-apis/scheduled-event#syntax-module-worker">scheduled</a>" for cron triggers.</p>
            <pre><code>export default {
  async fetch(request, environment, context) {
    return new Response("I’m a module!");
  },
  async scheduled(controller, environment, context) {
    // await doATask();
  }
}</code></pre>
            <p>You may also notice some other differences, such as the parameters on each event handler. Instead of a single "Event" object, the parameters that you need the most are spread out on their own. The first parameter is specific to the event type: for "fetch" it’s the <a href="https://developers.cloudflare.com/workers/runtime-apis/request">Request</a> object, and for "scheduled" it’s a <a href="https://developers.cloudflare.com/workers/runtime-apis/scheduled-event#properties-1">controller</a> that contains the cron schedule.</p><p>The second parameter is an object that contains your environment variables (also known as "<a href="https://developers.cloudflare.com/workers/platform/environment-variables">bindings</a>"). Previously, each variable was inserted into the global scope of the Worker. While a simple solution, it was confusing to have variables magically appear in your code. Now, with an environment object, you can control which modules and libraries get access to your environment variables. This mechanism is more secure, as it can prevent a compromised or nosy third-party library from enumerating all your variables or secrets.</p><p>The third parameter is a context object, which allows you to register background tasks using <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch-event#waituntil"><code>waitUntil()</code></a>. This is useful for tasks like logging or error reporting that should not block the execution of the event.</p><p>When you put that all together, you can import and export multiple modules, as well as use the new event handler syntax.</p>
            <pre><code>// filename: ./src/error.js
export async function logError(url, error) {
  await fetch(url, {
     method: "POST",
     body: error.stack
  })
}</code></pre>
            
            <pre><code>// filename: ./src/worker.js
import { logError } from "./error.js"

export default {
  async fetch(request, environment, context) {
    try {
       return await fetch(request);
    } catch (error) {
       context.waitUntil(logError(environment.ERROR_URL, error));
       return new Response("Oops!", { status: 500 });
    }
  }
}</code></pre>
            <p>Let’s not forget about Durable Objects, which became <a href="/durable-objects-ga">generally available</a> earlier this week! You can also export classes, which is how you define a Durable Object class. Here’s another example with a "Counter" Durable Object, that responds with an incrementing value.</p>
            <pre><code>// filename: ./src/counter.js
export class Counter {
  value = 0;
  fetch() {
    this.value++;
    return new Response(this.value.toString());
  }
}</code></pre>
            
            <pre><code>// filename: ./src/worker.js
// We need to re-export the Durable Object class in the Worker module.
export { Counter } from "./counter.js"

export default {
  async fetch(request, environment) {
    const clientId = request.headers.get("cf-connecting-ip");
    const counterId = environment.Counter.idFromName(clientId);
    // Each IP address gets its own Counter.
    const counter = environment.Counter.get(counterId);
    return counter.fetch("https://counter.object/increment");
  }
}</code></pre>
            
    <div>
      <h3>Are there non-JavaScript modules?</h3>
      <a href="#are-there-non-javascript-modules">
        
      </a>
    </div>
    <p>Yes! While modules are primarily for JavaScript, we also support other modules types, some of which are not yet standardized.</p><p>For instance, you can import <a href="https://developer.mozilla.org/en-US/docs/WebAssembly">WebAssembly</a> as a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/Module#synchronously_compiling_a_webassembly_module">module</a>. Previously, with the Service Worker API, WebAssembly was included as a binding. We think that was a mistake, since WebAssembly should be represented as code and not an external resource. With modules, here’s the new way to import WebAssembly:</p>
            <pre><code>import module from "./lib/hello.wasm"

export default {
  async fetch(request) {
    const instance = await WebAssembly.instantiate(module);
    const result = instance.exports.hello();
    return new Response(result);
  }
}</code></pre>
            <p>While not supported today, we look forward to a future where WebAssembly and JavaScript modules can be more tightly integrated, as outlined in this <a href="https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration#webassemblyes-module-integration">proposal</a>. The ergonomics improvement, demonstrated below, can go a long way to make WebAssembly more included in the JavaScript ecosystem.</p>
            <pre><code>import { hello } from "./lib/hello.wasm"

export default {
  async fetch(request) {
    return new Response(hello());
  }
}</code></pre>
            <p>We’ve also added support for text and binary modules, which allow you to import a <code>String</code> and <code>ArrayBuffer</code>, respectively. While not standardized, it allows you to easily import resources like an HTML file or an image.</p>
            <pre><code>&lt;!-- filename: ./public/index.html --&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;Hello!&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>
            
            <pre><code>import html from "../public/index.html"

export default {
  fetch(request) {
    if (request.url.endsWith("/index.html") {
       return new Response(html, {
          headers: { "Content-Type": "text/html" }
       });
    }
    return fetch(request);
  }
}</code></pre>
            
    <div>
      <h3>How can I get started?</h3>
      <a href="#how-can-i-get-started">
        
      </a>
    </div>
    <p>There are many ways to get started with modules.</p><p>First, you can try out modules in your browser using our <a href="https://cloudflareworkers.com/#a47f44190209e87bc95d5127ae0b2c84:https://welcome.developers.workers.dev">playground</a> (which doesn’t require an account) or by using the dashboard <a href="https://workers.new">quick editor</a>. Your browser will automatically detect when you’re using modules to allow you to seamlessly switch from the Service Worker API. For now, you’ll only be able to create one JavaScript module in the browser, though supporting multiple modules is something we’ll be improving soon.</p><p>If you’re feeling adventurous and want to start a <i>new</i> project using modules, you can try out the beta release of <a href="/wrangler-v2-beta">wrangler 2.0</a>, the next-generation of the command-line interface (CLI) for Workers.</p><p>For existing projects, we still recommend using <a href="https://github.com/cloudflare/wrangler">wrangler 1.0</a> (release 1.17 or later). To enable modules, you can adapt your "<a href="https://developers.cloudflare.com/workers/cli-wrangler/configuration#modules">wrangler.toml</a>" configuration to the following example:</p>
            <pre><code>name = "my-worker"
type = "javascript"
workers_dev = true

[build.upload]
format = "modules"
dir = "./src"
main = "./worker.js" # becomes "./src/worker.js"

[[build.upload.rules]]
type = "ESModule"
globs = ["**/*.js"]

# Uncomment if you have a build script.
# [build]
# command = "npm run build"</code></pre>
            <p>We’ve updated our <a href="https://developers.cloudflare.com/workers/">documentation</a> to provide more details about <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch-event#syntax-module-worker">modules</a>, though some examples will still be using the Service Worker API as we transition to showing both formats. (and TypeScript as a bonus!)</p><p>If you experience an issue or notice something strange with modules, please <a href="#">let us know</a>, and we’ll take a look. Happy coding, and we’re excited to see what you build with modules!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ZcNNYExuFZtWS9zT5TZ9E/04b138284d4ba8bfbf3b73eba1f18cdf/image2-11.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[JavaScript]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">7Alvb0yCnnm4ly3qnIFPk7</guid>
            <dc:creator>Ashcon Partovi</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 logs from the dashboard for Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/introducing-workers-dashboard-logs/</link>
            <pubDate>Tue, 24 Aug 2021 14:00:00 GMT</pubDate>
            <description><![CDATA[ Many developers know the feeling: “It worked in the local testing suite, it worked in our staging environment, but… it’s broken in production?” Testing can reduce mistakes and debugging can help find them, but logs give us the tools to understand and improve what we are creating. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Mixl8Zirzu4SzNfiqgXgm/a7c1855d9797cf5d815d61d4cf53e735/Workers-Observability-Shim-Github-Header.png" />
            
            </figure><p>If you’re writing code: what can go wrong, will go wrong.</p><p>Many developers know the feeling: “It worked in the local testing suite, it worked in our staging environment, but… it’s broken in production?” Testing can reduce mistakes and debugging can help find them, but logs give us the tools to understand and improve what we are creating.</p>
            <pre><code>if (this === undefined) {
  console.log("there’s no way… right?") // Narrator: there was.
}</code></pre>
            <p>While logging can help you understand when the seemingly impossible is actually possible, it’s something that no developer really wants to set up or maintain on their own. That’s why we’re excited to launch a new addition to the Cloudflare Workers platform: logs and exceptions from the dashboard.</p><p>Starting today, you can view and filter the <code>console.log</code> output and exceptions from a Worker… at no additional cost with no configuration needed!</p>
    <div>
      <h3>View logs, just a click away</h3>
      <a href="#view-logs-just-a-click-away">
        
      </a>
    </div>
    <p>When you view a Worker in the dashboard, you’ll now see a “Logs” tab which you can click on to view a detailed stream of logs and exceptions. Here’s what it looks like in action:</p><div></div>
<p></p><p>Each log entry contains an event with a list of logs, exceptions, and request headers if it was triggered by an HTTP request. We also automatically redact sensitive URLs and headers such as Authorization, Cookie, or anything else that appears to have a sensitive name.</p><p>If you are in the Durable Objects <a href="/durable-objects-open-beta/">open beta</a>, you will also be able to view the logs and requests sent to <i>each</i> Durable Object. This is a great tool to help you understand and debug the interactions between your Worker and a Durable Object.</p><p>For now, we support filtering by event status and type. Though, you can expect more filters to be added to the dashboard very soon! Today, we support advanced filtering with the wrangler CLI, which will be discussed later in this blog.</p>
    <div>
      <h3>console.log(), and you’re all set</h3>
      <a href="#console-log-and-youre-all-set">
        
      </a>
    </div>
    <p>It’s really simple to get started with logging for Workers. Simply invoke one of the standard <a href="https://developer.mozilla.org/en-US/docs/Web/API/console">console</a> APIs, such as <code>console.log()</code>, and we handle the rest. That’s it! There’s no extra setup, no configuration needed, and no hidden logging fees.</p>
            <pre><code>function logRequest (request) {
  const { cf, headers } = request
  const { city, region, country, colo, clientTcpRtt  } = cf
  
  console.log("Detected location:", [city, region, country].filter(Boolean).join(", "))
  if (clientTcpRtt) {
     console.debug("Round-trip time from client to", colo, "is", clientTcpRtt, "ms")
  }

  // You can also pass an object, which will be interpreted as JSON.
  // This is great if you want to define your own structured log schema.
  console.log({ headers })
}</code></pre>
            <p>In fact, you don’t even need to use <code>console.log</code> to view an event from the dashboard. If your Worker doesn’t generate any logs or exceptions, you will still be able to see the request headers from the event.</p>
    <div>
      <h3>Advanced filters, from your terminal</h3>
      <a href="#advanced-filters-from-your-terminal">
        
      </a>
    </div>
    <p>If you need more advanced filters you can use <a href="https://github.com/cloudflare/wrangler">wrangler</a>, our command-line tool for deploying Workers. We’ve updated the <code>wrangler tail</code> command to support sampling and a new set of advanced filters. You also no longer need to install or configure <code>cloudflared</code> to use the command. Not to mention it’s <i>much</i> faster, no more waiting around for logs to appear. Here are a few examples:</p>
            <pre><code># Filter by your own IP address, and if there was an uncaught exception.
wrangler tail --format=pretty --ip-address=self --status=error

# Filter by HTTP method, then apply a 10% sampling rate.
wrangler tail --format=pretty --method=GET --sampling-rate=0.1

# Filter using a generic search query.
wrangler tail --format=pretty --search="TypeError"</code></pre>
            <p>We recommend using the “pretty” format, since wrangler will output your logs in a colored, human-readable format. (We’re also working on a similar display for the dashboard.)</p><div></div>
<p></p><p>However, if you want to access structured logs, you can use the “json” format. This is great if you want to pipe your logs to another tool, such as <a href="https://stedolan.github.io/jq/tutorial/">jq</a>, or save them to a file. Here are a few more examples:</p>
            <pre><code># Parses each log event, but only outputs the url.
wrangler tail --format=json | jq .event.request?.url

# You can also specify --once to disconnect the tail after receiving the first log.
# This is useful if you want to run tests in a CI/CD environment.
wrangler tail --format=json --once &gt; event.json</code></pre>
            
    <div>
      <h3>Try it out!</h3>
      <a href="#try-it-out">
        
      </a>
    </div>
    <p>Both logs from the dashboard and <code>wrangler tail</code> are available and free for existing Workers customers. If you would like more information or a step-by-step guide, check out any of the resources below.</p><ul><li><p>Go to the <a href="https://dash.cloudflare.com?to=/:account/workers/overview">dashboard</a> and look at some logs!</p></li><li><p>Read the <a href="https://developers.cloudflare.com/workers/learning/logging-workers">getting started</a> guide for logging.</p></li><li><p>Look at the tail logs API <a href="https://api.cloudflare.com/#worker-tail-logs-properties">reference</a>.</p></li></ul> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Logs]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Dashboard]]></category>
            <category><![CDATA[Wrangler]]></category>
            <guid isPermaLink="false">4YuFI6zhgNP0dnoJv7d0NH</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing workers.new, custom builds, and improved logging for Workers]]></title>
            <link>https://blog.cloudflare.com/workers-new-custom-builds-and-improved-logging/</link>
            <pubDate>Fri, 16 Apr 2021 13:00:00 GMT</pubDate>
            <description><![CDATA[ In the spirit of quickly solving problems, we’re excited to launch three new improvements to the Workers experience, so you can take your next idea and ship it even faster. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Cloudflare Workers® aims to be the easiest and most powerful platform for developers to build and deploy their applications. With Workers, you can quickly solve problems without having to wonder: “is this going to scale?”</p><p>You write the JavaScript and we handle the rest, from distribution to scaling and concurrency.</p><p>In the spirit of quickly solving problems, we’re excited to launch three new improvements to the Workers experience, so you can take your next idea and ship it even faster.</p>
    <div>
      <h3>Introducing... workers.new</h3>
      <a href="#introducing-workers-new">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6UTg9II2dZ8I5FpUREylkU/a3f3fe46379174ebe33db97fad860f25/unnamed-4.png" />
            
            </figure><p>First, we’re introducing <a href="https://workers.new">https://workers.new</a>, a shortcut that takes you directly to a JavaScript editor for creating a new Worker. Anytime you have a cool idea, need a quick fix to a problem, or just want to debug some JavaScript, you now have a simple way to go from idea to prototype. What’s more is you don’t even need to deploy the Worker to try it out!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5owqpkMvx9ZvZypSdLbE1Z/083f9e136c9043cf5b54378b64b12357/unnamed-1.gif" />
            
            </figure><p>We’ve also updated the default Worker template to help you go a few steps beyond the typical “Hello, World!”. When you open the editor, you’ll now see a few examples that demonstrate how to redirect requests, modify headers, and parse responses.</p>
    <div>
      <h3>Customize your build scripts</h3>
      <a href="#customize-your-build-scripts">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7luFgXD6s8A1HJw9L2gXPY/34c3c78668b0d4437c51ea22d54f88a8/unnamed--1--2.png" />
            
            </figure><p>For developers who want to deploy more complex applications, like those with external libraries or written in other languages like TypeScript, we recommend using our command-line tool, <a href="https://github.com/cloudflare/wrangler#-wrangler">wrangler</a>.</p><p>Previously, wrangler has supported three project types: JavaScript, Rust, and webpack. By far, we’ve found that webpack is the most widely used among the community, since it has built-in support for bundling npm packages. Yet, as more developers started to use wrangler, they’ve wanted to customize their webpack configurations or even use other JavaScript bundlers.</p><p>That’s why we’re excited to release custom builds for wrangler! Now you can provide wrangler with a custom build script and upload directory, which works with all of the wrangler commands including <code>wrangler dev</code> and <code>wrangler publish</code>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/71hynCM98BoveG11Ozv8LL/b8c209da0cf2634f7c74ae5036be5f7a/unnamed-5.png" />
            
            </figure><p>This means you can use any JavaScript bundler you want: webpack, Rollup, Parcel, esbuild, and many more! We’ve put together a few templates that you can copy if you want to get started with custom builds. Make sure to <a href="https://developers.cloudflare.com/workers/cli-wrangler/install-update#update">update</a> wrangler to release 1.16 or later and check out our <a href="https://developers.cloudflare.com/workers/cli-wrangler/configuration#build">documentation</a> about custom builds and additional configuration options.</p><ul><li><p><a href="https://github.com/cloudflare/service-worker-custom-build">webpack</a></p></li><li><p><a href="https://github.com/cloudflare/durable-objects-webpack-commonjs">webpack with Durable Objects</a></p></li><li><p><a href="https://github.com/cloudflare/modules-rollup-esm">Rollup</a></p></li><li><p><a href="https://github.com/cloudflare/durable-objects-rollup-esm">Rollup with Durable Objects</a></p></li></ul>
    <div>
      <h3>Viewing logs and exceptions</h3>
      <a href="#viewing-logs-and-exceptions">
        
      </a>
    </div>
    <p>Ever wanted to see the <code>console.log()</code> output or uncaught exceptions from a deployed Worker?</p><p>In case you missed it, <code>wrangler tail</code> allows you to see a live stream of your Worker’s logs and exceptions. Today, we’re improving <code>wrangler tail</code> by making it easier to use and more accessible to developers.</p><p>First, we wanted to improve the terminal output. Previously, <code>wrangler tail</code> would only output log entries as JSON. While this was useful when piping through to tools like <code>jq</code>, it was generally hard to digest. That’s why we created a new “pretty” format that is much easier to read and takes full advantage of your terminal’s color codes.</p>
            <pre><code>$ wrangler tail --format=&lt;json|pretty&gt;</code></pre>
            
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3QeRUqMOW6FfedIi03nLgQ/1e0f669424c6cacaa216c7c149d2eb5c/unnamed--1-.gif" />
            
            </figure><p>If you have suggestions for how we can improve <code>wrangler tail</code>, feel free to pop in the #wrangler channel on the Workers <a href="https://discord.com/invite/cloudflaredev">Discord</a>. We’re excited to see what you ship with these improvements!</p>
    <div>
      <h3>A sneak preview</h3>
      <a href="#a-sneak-preview">
        
      </a>
    </div>
    <p>As we like to say at Cloudflare, we’re just getting started. While we have plans to add improvements to wrangler... where else could we introduce logs?</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/GXS8Vlb9Hfq5Jufmsgc7Y/baae39c001495d15521539b7f04abcd7/image3-8.png" />
            
            </figure><p>Stay tuned.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Wrangler]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">4mqJidVbcm65xGJzWTA6Ro</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
        </item>
        <item>
            <title><![CDATA[Asynchronous HTMLRewriter for Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/asynchronous-htmlrewriter-for-cloudflare-workers/</link>
            <pubDate>Fri, 28 Aug 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ HTMLRewriter for Cloudflare Workers now supports asynchronous handlers, allowing developers to prefetch assets or user-specific content from a remote service. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Last year, we launched <a href="/introducing-htmlrewriter/">HTMLRewriter</a> for Cloudflare Workers, which enables developers to make streaming changes to HTML on the edge. Unlike a traditional DOM parser that loads the entire HTML document into memory, we developed a <a href="https://github.com/cloudflare/lol-html">streaming parser</a> written in Rust. Today, we’re announcing support for asynchronous handlers in HTMLRewriter. Now you can perform asynchronous tasks based on the content of the HTML document: from prefetching fonts and image assets to fetching user-specific content from a CMS.</p>
    <div>
      <h3>How can I use HTMLRewriter?</h3>
      <a href="#how-can-i-use-htmlrewriter">
        
      </a>
    </div>
    <p>We designed HTMLRewriter to have a jQuery-like experience. First, you define a handler, then you assign it to a CSS selector; Workers does the rest for you. You can look at our new and improved <a href="https://developers.cloudflare.com/workers/runtime-apis/html-rewriter#selectors">documentation</a> to see our supported list of selectors, which now include <code>nth-child</code> selectors. The example below changes the alternative text for every second image in a document.</p>
            <pre><code>async function editHtml(request) {
  return new HTMLRewriter()
     .on("img:nth-child(2)", new ElementHandler())
     .transform(await fetch(request))
}

class ElementHandler {
   element(e) {
      e.setAttribute("alt", "A very interesting image")
   }
}</code></pre>
            <p>Since these changes are applied using streams, we maintain a low TTFB (time to first byte) and users never know the HTML was transformed. If you’re interested in how we’re able to accomplish this technically, you can read our blog post about <a href="/html-parsing-2/">HTML parsing.</a></p>
    <div>
      <h3>What’s new with HTMLRewriter?</h3>
      <a href="#whats-new-with-htmlrewriter">
        
      </a>
    </div>
    <p>Now you can define an <code>async</code> handler which allows any code that uses <code>await</code>. This means you can make dynamic HTML injection, based on the contents of the document, without having prior knowledge of what it contains. This allows you to customize HTML based on a particular user, feature flag, or even an integration with a CMS.</p>
            <pre><code>class UserCustomizer {
   // Remember to add the `async` keyword to the handler method
   async element(e) {
      const user = await fetch(`https://my.api.com/user/${e.getAttribute("user-id")}/online`)
      if (user.ok) {
         // Add the user’s name to the element
         e.setAttribute("user-name", await user.text())
      } else {
         // Remove the element, since this user not online
         e.remove()
      }
   }
}
</code></pre>
            
    <div>
      <h3>What can I build with HTMLRewriter?</h3>
      <a href="#what-can-i-build-with-htmlrewriter">
        
      </a>
    </div>
    <p>To illustrate the flexibility of HTMLRewriter, I wrote an example that you can deploy on your own website. If you manage a website, you know that old links and images can expire with time. Here’s an excerpt from a years’ old post I wrote on the Cloudflare Blog:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7Mxl8SXuSpH1wvXxf0hMkv/46a4a6907d35f7ef2722768304916375/image1-18.png" />
            
            </figure><p>As you might see, that missing image is not the prettiest sight. However, we can easily fix this using async handlers in HTMLRewriter. Using a service like the <a href="https://archive.org/help/wayback_api.php">Internet Archive</a> API, we can check if an image no longer exists and rewrite the URL to use the latest archive. That means users don’t see an ugly placeholder and won’t even know the image was replaced.</p>
            <pre><code>async function fetchAndFixImages(request) {
   return new HTMLRewriter()
      .on("img", new ImageFixer())
      .transform(await fetch(request))
}

class ImageFixer {
   async element(e) {
    var url = e.getAttribute("src")
    var response = await fetch(url)
    if (!response.ok) {
       var archive = await fetch(`https://archive.org/wayback/available?url=${url}`)
       if (archive.ok) {
          var snapshot = await archive.json()
          e.setAttribute("src", snapshot.archived_snapshots.closest.url)
       } else {
          e.remove()
       }
    }
  }
}</code></pre>
            <p>Using the Workers Playground, you can view a <a href="https://cloudflareworkers.com/#42b5ec636af37ae988c3041e61da1870:https://blog.cloudflare.com/minecraft-api-with-workers-coffeescript/">working sample</a> of the above code. A more complex example could even alert a service like Sentry when a missing image is detected. Using the previous missing image, now you can see the image is restored and users are none of the wiser.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4kRP0BDTrkjfY5m9NiCZn1/03374dbe5cc041a4a73cd9f653f65ad4/image2-9.png" />
            
            </figure><p>If you’re interested in deploying this to your own website, click on the button below:</p>
            <figure>
            <a href="https://deploy.workers.cloudflare.com/?url=https://github.com/Electroid/wayback-worker">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5emZJNvGrSvi1OJFweWSka/418708c24fca206de7016c57fed5460a/button.svg_xml" />
            </a>
            </figure>
    <div>
      <h3>What else can I build with HTMLRewriter?</h3>
      <a href="#what-else-can-i-build-with-htmlrewriter">
        
      </a>
    </div>
    <p>We’ve been blown away by developer projects using HTMLRewriter. Here are a few projects that caught our eye and are great examples of the power of Cloudflare Workers and HTMLRewriter:</p><ul><li><p><a href="https://web.scraper.workers.dev/">An elegant web scraper</a></p></li><li><p><a href="https://stanislas.blog/2020/05/native-image-lazy-loading-ghost-cloudflare-worker/">Optimizing images with lazy loading</a></p></li><li><p><a href="https://dev.to/cloudflareworkers/localizing-applications-with-cloudflare-worker-s-new-streaming-html-rewriter-1k41">Localizing a website</a></p></li><li><p><a href="https://jross.me/using-cloudflare-workers-htmlrewriter-to-extend-ghost-pro/">Extending a Ghost blog</a></p></li><li><p><a href="https://community.cloudflare.com/t/2020-6-18-workers-runtime-release-notes/183460/2">Prefetching and embedding Google fonts</a></p></li></ul><p>If you’re interested in using HTMLRewriter, check out our <a href="https://developers.cloudflare.com/workers/runtime-apis/html-rewriter">documentation</a>. Also be sure to share any creations you’ve made with <a href="https://twitter.com/CloudflareDev">@CloudflareDev</a>, we love looking at the awesome projects you build.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">76Vtl6ZnNzBh69aIwtPl0M</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
        </item>
        <item>
            <title><![CDATA[Eliminating cold starts with Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/eliminating-cold-starts-with-cloudflare-workers/</link>
            <pubDate>Thu, 30 Jul 2020 13:00:00 GMT</pubDate>
            <description><![CDATA[ A “cold start” is the time it takes to load and execute a new copy of a serverless function for the first time. It’s a problem that’s both complicated to solve and costly to fix. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1oYOYNxDVXur9l8h5l6ZHe/39237ee0a2d46e8e7b7562d087bb8c40/Serverless-Week-Cold-Starts_2x-3.png" />
            
            </figure><p>A “cold start” is the time it takes to load and execute a new copy of a serverless function for the first time. It’s a problem that’s both complicated to solve and costly to fix. Other serverless platforms make you choose between suffering from random increases in execution time, or paying your way out with synthetic requests to keep your function warm. Cold starts are a horrible experience, especially when serverless containers can take full <i>seconds</i> to warm up.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7I5ZmdHAJG6sALtXOcCJvD/095f74d232b47b895ff0fd957a67086b/cold-start-clock_2x.png" />
            
            </figure><p>Unlike containers, Cloudflare Workers utilize isolate technology, which measure cold starts in single-digit milliseconds. Well, at least they <i>did</i>. Today, we’re removing the need to worry about cold starts entirely, by introducing support for Workers that have no cold starts at all – that’s right, zero. Forget about cold starts, warm starts, or... any starts, with Cloudflare Workers you get always-hot, raw performance in more than 200 cities worldwide.</p>
    <div>
      <h3>Why is there a cold start problem?</h3>
      <a href="#why-is-there-a-cold-start-problem">
        
      </a>
    </div>
    <p>It’s impractical to keep everyone’s functions warm in memory <i>all</i> the time. Instead, serverless providers only warm up a function after the first request is received. Then, after a period of inactivity, the function becomes cold again and the cycle continues.</p><p>For Workers, this has never been much of a problem. In contrast to containers that can spend full seconds spinning up a new containerized process for each function, the isolate technology behind Workers allows it to warm up a function in under 5 milliseconds.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CyIMgGPifYO2zfgd8Y2XV/a7e33fef97b56afc626870d09704ceed/isolates-model_2x-1.png" />
            
            </figure><p><i>Learn more about how isolates enable Cloudflare Workers to be performant and secure</i> <a href="/cloud-computing-without-containers/"><i>here.</i></a></p><p>Cold starts are ugly. They’re unexpected, unavoidable, and cause unpredictable code execution times. You shouldn’t have to compromise your customers’ experience to enjoy <a href="https://www.cloudflare.com/learning/serverless/what-is-serverless/">the benefits of serverless</a>. In a collaborative effort between our Workers and Protocols teams, we set out to create a solution where you never have to worry about cold starts, warm starts, or pre-warming ever again.</p>
    <div>
      <h3>How is a zero cold start even possible?</h3>
      <a href="#how-is-a-zero-cold-start-even-possible">
        
      </a>
    </div>
    <p>Like many features at Cloudflare, security and encryption make our network more intelligent. Since 95% of Worker requests are securely handled over HTTPS, we engineered a solution that uses the Internet’s encryption protocols to our advantage.</p><p>Before a client can send an HTTPS request, it needs to establish a secure channel with the server. This process is known as “handshaking” in the <a href="https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/">TLS</a>, or Transport Layer Security, protocol. Most clients also send a hostname (e.g. cloudflare.com) in that handshake, which is referred to as the <a href="https://www.cloudflare.com/learning/ssl/what-is-sni/">SNI</a>, or Server Name Indication. The server receives the handshake, sends back a certificate, and now the client is allowed to send its original request, encrypted.</p><p>Previously, Workers would only load and compile after the <i>entire</i> handshake process was complete, which involves two round-trips between the client and server. But wait, we thought, if the hostname is present in the handshake, why wait until the entire process is done to preload the Worker? Since the handshake takes some time, there is an opportunity to warm up resources during the waiting time before the request arrives.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/20vUSqi8x0NQ2FPLeQ807y/ce0d66bee2ffdae836174d2649a0f65f/Workers-handshake-after_2x.png" />
            
            </figure><p>With our newest optimization, when Cloudflare receives the first packet during TLS negotiation, the “ClientHello,” we hint the Workers runtime to eagerly load that hostname’s Worker. After the handshake is done, the Worker is warm and ready to receive requests. Since it only takes 5 milliseconds to load a Worker, and the average latency between a client and Cloudflare is more than that, the cold start is zero. The Worker starts executing code the moment the request is received from the client.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7G67cFXFAeoyLca4YxgeqC/c33e1c41c983c8480af36e6418676ae7/Workers-handshake-before-_2x.png" />
            
            </figure>
    <div>
      <h3>When are zero cold starts available?</h3>
      <a href="#when-are-zero-cold-starts-available">
        
      </a>
    </div>
    <p>Now, and for everyone! We’ve rolled out this optimization to all Workers customers and it is in production today. There’s no extra fee and no configuration change required. When you build on Cloudflare Workers, you build on an intelligent, distributed network that is constantly pushing the bounds of what's possible in terms of performance.</p><p>For now, this is only available for Workers that are deployed to a “root” hostname like “example.com” and not specific paths like “example.com/path/to/something.” We plan to introduce more optimizations in the future that can preload specific paths.</p>
    <div>
      <h3>What about performance beyond cold starts?</h3>
      <a href="#what-about-performance-beyond-cold-starts">
        
      </a>
    </div>
    <p>We also recognize that performance is more than just zero cold starts. That’s why we announced the beta of <a href="https://www.cloudflare.com/workers-unbound-beta/">Workers Unbound</a> earlier this week. Workers Unbound has the simplicity and performance of Workers with no limits, just raw performance.</p><p>Workers, equipped with zero cold starts, no CPU limits, and a network that spans over 200 cities is prime and ready to take on any serious workload. Now that’s performance.</p>
    <div>
      <h3>Interested in deploying with Workers?</h3>
      <a href="#interested-in-deploying-with-workers">
        
      </a>
    </div>
    <ul><li><p>Learn more about <a href="https://workers.dev">Cloudflare Workers</a></p></li><li><p>Join the Workers Unbound <a href="https://www.cloudflare.com/workers-unbound-beta/">Beta</a></p></li><li><p>Try our new language support for <a href="https://github.com/cloudflare/python-worker-hello-world">Python</a> and <a href="https://github.com/cloudflare/kotlin-worker-hello-world">Kotlin</a></p></li></ul> ]]></content:encoded>
            <category><![CDATA[Serverless Week]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">7FBdTQH1q8YprEn2w4ea41</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
        </item>
        <item>
            <title><![CDATA[Workers KV — Cloudflare's distributed database]]></title>
            <link>https://blog.cloudflare.com/workers-kv-is-ga/</link>
            <pubDate>Tue, 21 May 2019 13:00:00 GMT</pubDate>
            <description><![CDATA[ Today, we’re excited to announce Workers KV is entering general availability and is ready for production use! ]]></description>
            <content:encoded><![CDATA[ <p>Today, we’re excited to announce Workers KV is entering general availability and is ready for production use!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3188jggqN4vJJchIXvTReV/e9476e1e9e5783948d4255f92bcb2e47/Workers-KV-GA_2x.png" />
            
            </figure>
    <div>
      <h3>What is Workers KV?</h3>
      <a href="#what-is-workers-kv">
        
      </a>
    </div>
    <p><a href="https://www.cloudflare.com/products/workers-kv/">Workers KV</a> is a highly distributed, eventually consistent, key-value store that spans Cloudflare's global edge. It allows you to store billions of key-value pairs and read them with ultra-low latency anywhere in the world. Now you can build entire applications with the performance of a CDN static cache.</p>
    <div>
      <h3>Why did we build it?</h3>
      <a href="#why-did-we-build-it">
        
      </a>
    </div>
    <p><a href="https://www.cloudflare.com/products/cloudflare-workers/">Workers</a> is a platform that lets you run JavaScript on Cloudflare's global edge of 175+ data centers. With only a few lines of code, you can route HTTP requests, modify responses, or even create new responses without an origin server.</p>
            <pre><code>// A Worker that handles a single redirect,
// such a humble beginning...
addEventListener("fetch", event =&gt; {
  event.respondWith(handleOneRedirect(event.request))
})

async function handleOneRedirect(request) {
  let url = new URL(request.url)
  let device = request.headers.get("CF-Device-Type")
  // If the device is mobile, add a prefix to the hostname.
  // (eg. example.com becomes mobile.example.com)
  if (device === "mobile") {
    url.hostname = "mobile." + url.hostname
    return Response.redirect(url, 302)
  }
  // Otherwise, send request to the original hostname.
  return await fetch(request)
}</code></pre>
            <p>Customers quickly came to us with use cases that required a way to store persistent data. Following our example above, it's easy to handle a single redirect, but what if you want to handle <b>billions</b> of them? You would have to hard-code them into your Workers script, fit it all in under 1 MB, and re-deploy it every time you wanted to make a change — yikes! That’s why we built Workers KV.</p>
            <pre><code>// A Worker that can handle billions of redirects,
// now that's more like it!
addEventListener("fetch", event =&gt; {
  event.respondWith(handleBillionsOfRedirects(event.request))
})

async function handleBillionsOfRedirects(request) {
  let prefix = "/redirect"
  let url = new URL(request.url)
  // Check if the URL is a special redirect.
  // (eg. example.com/redirect/&lt;random-hash&gt;)
  if (url.pathname.startsWith(prefix)) {
    // REDIRECTS is a custom variable that you define,
    // it binds to a Workers KV "namespace." (aka. a storage bucket)
    let redirect = await REDIRECTS.get(url.pathname.replace(prefix, ""))
    if (redirect) {
      url.pathname = redirect
      return Response.redirect(url, 302)
    }
  }
  // Otherwise, send request to the original path.
  return await fetch(request)
}</code></pre>
            <p>With only a few changes from our previous example, we scaled from one redirect to billions − that's just a taste of what you can build with Workers KV.</p>
    <div>
      <h3>How does it work?</h3>
      <a href="#how-does-it-work">
        
      </a>
    </div>
    <p>Distributed data stores are often modeled using the <a href="https://en.wikipedia.org/wiki/CAP_theorem">CAP Theorem</a>, which states that distributed systems can only pick between <b>2 out of the 3</b> following guarantees:</p><ul><li><p><b>C</b>onsistency - is my data the same everywhere?</p></li><li><p><b>A</b>vailability - is my data accessible all the time?</p></li><li><p><b>P</b>artition tolerance - is my data resilient to regional outages?</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2q0UXWQCbVcPVUX9nrkB88/2c384717f1db76f9ecdf7065c08b770f/workers-kv-venn-diagram_2x-1.png" />
            
            </figure><p>Workers KV chooses to guarantee <b>A</b>vailability and <b>P</b>artition tolerance. This combination is known as <a href="https://en.wikipedia.org/wiki/Eventual_consistency">eventual consistency</a>, which presents Workers KV with two unique competitive advantages:</p><ul><li><p>Reads are ultra fast (median of 12 ms) since its powered by our caching technology.</p></li><li><p>Data is available across 175+ edge data centers and resilient to regional outages.</p></li></ul><p>Although, there are tradeoffs to eventual consistency. If two clients write different values to the same key at the same time, the last client to write <b>eventually</b> "wins" and its value becomes globally consistent. This also means that if a client writes to a key and that same client reads that same key, the values may be inconsistent for a short amount of time.</p><p>To help visualize this scenario, here's a real-life example amongst three friends:</p><ul><li><p>Suppose Matthew, Michelle, and Lee are planning their weekly lunch.</p></li><li><p>Matthew decides they're going out for sushi.</p></li><li><p>Matthew tells Michelle their sushi plans, Michelle agrees.</p></li><li><p>Lee, not knowing the plans, tells Michelle they're actually having pizza.</p></li></ul><p>An hour later, Michelle and Lee are waiting at the pizza parlor while Matthew is sitting alone at the sushi restaurant — what went wrong? We can chalk this up to eventual consistency, because after waiting for a few minutes, Matthew looks at his updated calendar and <b>eventually</b> finds the new truth, they're going out for pizza instead.</p><p>While it may take minutes in real-life, Workers KV is much faster. It can achieve global consistency in less than 60 seconds. Additionally, when a Worker writes to a key, then <b>immediately</b> reads that same key, it can expect the values to be consistent if both operations came from the same location.</p>
    <div>
      <h3>When should I use it?</h3>
      <a href="#when-should-i-use-it">
        
      </a>
    </div>
    <p>Now that you understand the benefits and tradeoffs of using eventual consistency, how do you determine if it's the right storage solution for your application? Simply put, if you want global availability with ultra-fast reads, Workers KV is right for you.</p><p>However, if your application is <b>frequently</b> writing to the <b>same</b> key, there is an additional consideration. We call it "the Matthew question": Are you okay with the Matthews of the world <b>occasionally</b> going to the wrong restaurant?</p><p>You can imagine use cases (like our redirect Worker example) where this doesn't make any material difference. But if you decide to keep track of a user’s bank account balance, you would not want the possibility of two balances existing at once, since they could purchase something with money they’ve already spent.</p>
    <div>
      <h3>What can I build with it?</h3>
      <a href="#what-can-i-build-with-it">
        
      </a>
    </div>
    <p>Here are a few examples of applications that have been built with KV:</p><ul><li><p>Mass redirects - handle billions of HTTP redirects.</p></li><li><p>User authentication - validate user requests to your API.</p></li><li><p>Translation keys - dynamically localize your web pages.</p></li><li><p>Configuration data - manage who can access your origin.</p></li><li><p>Step functions - sync state data between multiple APIs functions.</p></li><li><p>Edge file store - <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">host</a> large amounts of small files.</p></li></ul><p>We’ve highlighted several of those <a href="/building-with-workers-kv/">use cases</a> in our previous blog post. We also have some more in-depth code walkthroughs, including a recently published blog post on how to build an online <a href="/building-a-to-do-list-with-workers-and-kv/">To-do list with Workers KV</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3oQnAPwVXWgsjCuSXuYL7q/755458cfb741e45f78c468a4729f7fc7/GQ4hrfQ.png" />
            
            </figure>
    <div>
      <h3>What's new since beta?</h3>
      <a href="#whats-new-since-beta">
        
      </a>
    </div>
    <p>By far, our most common request was to make it easier to write data to Workers KV. That's why we're releasing three new ways to make that experience even better:</p>
    <div>
      <h4>1. Bulk Writes</h4>
      <a href="#1-bulk-writes">
        
      </a>
    </div>
    <p>If you want to import your existing data into Workers KV, you don't want to go through the hassle of sending an HTTP request for <b>every</b> key-value pair. That's why we added a <a href="https://api.cloudflare.com/#workers-kv-namespace-write-multiple-key-value-pairs">bulk endpoint</a> to the Cloudflare API. Now you can upload up to 10,000 pairs (up to 100 MB of data) in a single PUT request.</p>
            <pre><code>curl "https://api.cloudflare.com/client/v4/accounts/ \
     $ACCOUNT_ID/storage/kv/namespaces/$NAMESPACE_ID/bulk" \
  -X PUT \
  -H "X-Auth-Key: $CLOUDFLARE_AUTH_KEY" \
  -H "X-Auth-Email: $CLOUDFLARE_AUTH_EMAIL" \
  -d '[
    {"key": "built_by",    value: "kyle, alex, charlie, andrew, and brett"},
    {"key": "reviewed_by", value: "joaquin"},
    {"key": "approved_by", value: "steve"}
  ]'</code></pre>
            <p>Let's walk through an example use case: you want to off-load your website translation to Workers. Since you're reading translation keys frequently and only occasionally updating them, this application works well with the eventual consistency model of Workers KV.</p><p>In this example, we hook into <a href="https://crowdin.com/">Crowdin</a>, a popular platform to manage translation data. This Worker responds to a <code>/translate</code> endpoint, downloads all your translation keys, and bulk writes them to Workers KV so you can read it later on our edge:</p>
            <pre><code>addEventListener("fetch", event =&gt; {
  if (event.request.url.pathname === "/translate") {
    event.respondWith(uploadTranslations())
  }
})

async function uploadTranslations() {
  // Ask crowdin for all of our translations.
  var response = await fetch(
    "https://api.crowdin.com/api/project" +
    "/:ci_project_id/download/all.zip?key=:ci_secret_key")
  // If crowdin is responding, parse the response into
  // a single json with all of our translations.
  if (response.ok) {
    var translations = await zipToJson(response)
    return await bulkWrite(translations)
  }
  // Return the errored response from crowdin.
  return response
}

async function bulkWrite(keyValuePairs) {
  return fetch(
    "https://api.cloudflare.com/client/v4/accounts" +
    "/:cf_account_id/storage/kv/namespaces/:cf_namespace_id/bulk",
    {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        "X-Auth-Key": ":cf_auth_key",
        "X-Auth-Email": ":cf_email"
      },
      body: JSON.stringify(keyValuePairs)
    }
  )
}

async function zipToJson(response) {
  // ... omitted for brevity ...
  // (eg. https://stuk.github.io/jszip)
  return [
    {key: "hello.EN", value: "Hello World"},
    {key: "hello.ES", value: "Hola Mundo"}
  ]
}</code></pre>
            <p>Now, when you want to translate a page, all you have to do is read from Workers KV:</p>
            <pre><code>async function translate(keys, lang) {
  // You bind your translations namespace to the TRANSLATIONS variable.
  return Promise.all(keys.map(key =&gt; TRANSLATIONS.get(key + "." + lang)))
}</code></pre>
            
    <div>
      <h4>2. Expiring Keys</h4>
      <a href="#2-expiring-keys">
        
      </a>
    </div>
    <p>By default, key-value pairs stored in Workers KV last forever. However, sometimes you want your data to auto-delete after a certain amount of time. That's why we're introducing the <code>expiration</code> and <code>expirationTtl</code>options for write operations.</p>
            <pre><code>// Key expires 60 seconds from now.
NAMESPACE.put("myKey", "myValue", {expirationTtl: 60})

// Key expires if the UNIX epoch is in the past.
NAMESPACE.put("myKey", "myValue", {expiration: 1247788800})</code></pre>
            
            <pre><code># You can also set keys to expire from the Cloudflare API.
curl "https://api.cloudflare.com/client/v4/accounts/ \
     $ACCOUNT_ID/storage/kv/namespaces/$NAMESPACE_ID/ \
     values/$KEY?expiration_ttl=$EXPIRATION_IN_SECONDS"
  -X PUT \
  -H "X-Auth-Key: $CLOUDFLARE_AUTH_KEY" \
  -H "X-Auth-Email: $CLOUDFLARE_AUTH_EMAIL" \
  -d "$VALUE"</code></pre>
            <p>Let's say you want to block users that have been flagged as inappropriate from your website, but only for a week. With an expiring key, you can set the expire time and not have to worry about deleting it later.</p><p>In this example, we assume users and IP addresses are one of the same. If your application has authentication, you could use access tokens as the key identifier.</p>
            <pre><code>addEventListener("fetch", event =&gt; {
  var url = new URL(event.request.url)
  // An internal API that blocks a new user IP.
  // (eg. example.com/block/1.2.3.4)
  if (url.pathname.startsWith("/block")) {
    var ip = url.pathname.split("/").pop()
    event.respondWith(blockIp(ip))
  } else {
    // Other requests check if the IP is blocked.
   event.respondWith(handleRequest(event.request))
  }
})

async function blockIp(ip) {
  // Values are allowed to be empty in KV,
  // we don't need to store any extra information anyway.
  await BLOCKED.put(ip, "", {expirationTtl: 60*60*24*7})
  return new Response("ok")
}

async function handleRequest(request) {
  var ip = request.headers.get("CF-Connecting-IP")
  if (ip) {
    var blocked = await BLOCKED.get(ip)
    // If we detect an IP and its blocked, respond with a 403 error.
    if (blocked) {
      return new Response({status: 403, statusText: "You are blocked!"})
    }
  }
  // Otherwise, passthrough the original request.
  return fetch(request)
}</code></pre>
            
    <div>
      <h4>3. Larger Values</h4>
      <a href="#3-larger-values">
        
      </a>
    </div>
    <p>We've increased our size limit on values from <code>64 kB</code> to <code>2 MB</code>. This is quite useful if you need to store buffer-based or file data in Workers KV.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1cYcxKnB6nfE9lUg6aIUZK/4cefcd37832fc703602f0c8aa37c33d6/workers-kc-file-size-update_2x.png" />
            
            </figure><p>Consider this scenario: you want to let your users upload their favorite GIF to their profile without having to store these GIFs as binaries in your database or managing <b>another</b> cloud storage bucket.</p><p>Workers KV is a great fit for this use case! You can create a Workers KV namespace for your users’ GIFs that is fast and reliable wherever your customers are located.</p><p>In this example, users upload a link to their favorite GIF, then a Worker downloads it and stores it to Workers KV.</p>
            <pre><code>addEventListener("fetch", event =&gt; {
  var url = event.request.url
  var arg = request.url.split("/").pop()
  // User sends a URI encoded link to the GIF they wish to upload.
  // (eg. example.com/api/upload_gif/&lt;encoded-uri&gt;)
  if (url.pathname.startsWith("/api/upload_gif")) {
    event.respondWith(uploadGif(arg))
    // Profile contains link to view the GIF.
    // (eg. example.com/api/view_gif/&lt;username&gt;)
  } else if (url.pathname.startsWith("/api/view_gif")) {
    event.respondWith(getGif(arg))
  }
})

async function uploadGif(url) {
  // Fetch the GIF from the Internet.
  var gif = await fetch(decodeURIComponent(url))
  var buffer = await gif.arrayBuffer()
  // Upload the GIF as a buffer to Workers KV.
  await GIFS.put(user.name, buffer)
  return gif
}

async function getGif(username) {
  var gif = await GIFS.get(username, "arrayBuffer")
  // If the user has set one, respond with the GIF.
  if (gif) {
    return new Response(gif, {headers: {"Content-Type": "image/gif"}})
  } else {
    return new Response({status: 404, statusText: "User has no GIF!"})
  }
}</code></pre>
            <p>Lastly, we want to thank all of our beta customers. It was your valuable feedback that led us to develop these changes to Workers KV. Make sure to stay in touch with us, we're always looking ahead for what's next and we love hearing from you!</p>
    <div>
      <h3>Pricing</h3>
      <a href="#pricing">
        
      </a>
    </div>
    <p>We’re also ready to announce our GA pricing. If you're one of our Enterprise customers, your pricing obviously remains unchanged.</p><ul><li><p>$0.50 / GB of data stored, 1 GB included</p></li><li><p>$0.50 / million reads, 10 million included</p></li><li><p>$5 / million write, list, and delete operations, 1 million included</p></li></ul><p>During the beta period, we learned customers don't want to just read values at our edge, they want to write values from our edge too. Since there is high demand for these edge operations, which are more costly, we have started charging non-read operations per month.</p>
    <div>
      <h3>Limits</h3>
      <a href="#limits">
        
      </a>
    </div>
    <p>As mentioned earlier, we increased our value size limit from <code>64 kB</code> to <code>2 MB</code>. We've also removed our cap on the number of keys per namespace — it's now unlimited. Here are our GA limits:</p><ul><li><p>Up to 20 namespaces per account, each with unlimited keys</p></li><li><p>Keys of up to 512 bytes and values of up to 2 MB</p></li><li><p>Unlimited writes per second for different keys</p></li><li><p>One write per second for the same key</p></li><li><p>Unlimited reads per second per key</p></li></ul>
    <div>
      <h3>Try it out now!</h3>
      <a href="#try-it-out-now">
        
      </a>
    </div>
    <p>Now open to all customers, you can start using <a href="https://www.cloudflare.com/products/workers-kv/">Workers KV</a> today from your Cloudflare dashboard under the Workers tab. You can also look at our updated <a href="https://developers.cloudflare.com/workers/kv/">documentation.</a></p><p>We're really excited to see what you all can build with Workers KV!</p> ]]></content:encoded>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Workers KV]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Programming]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">1btTnYVZRP9LCWLguf60eI</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
        </item>
        <item>
            <title><![CDATA[Minecraft API with Workers + CoffeeScript]]></title>
            <link>https://blog.cloudflare.com/minecraft-api-with-workers-coffeescript/</link>
            <pubDate>Tue, 31 Jul 2018 08:00:00 GMT</pubDate>
            <description><![CDATA[ If you've ever played a video game in the past couple of years, chances are you know about Minecraft. You might be familiar with the game or even planted a tree or two, but what you might not know about is the vast number of Minecraft online communities.  ]]></description>
            <content:encoded><![CDATA[ <p><i>The following was originally written as a guest post by </i><a href="https://ashcon.app"><i>Ashcon Partovi</i></a><i>, while a computer science and business undergraduate at the University of British Columbia in Vancouver, Canada. He's the founder of a popular Minecraft multiplayer server, </i><a href="https://stratus.network/forums"><i>stratus.network</i></a><i>, that provides competitive, team-based gameplay to thousands of players every week. He also now works at Cloudflare.</i></p><p>If you've ever played a video game in the past couple of years, chances are you know about <a href="https://en.wikipedia.org/wiki/Minecraft"><i>Minecraft</i>.</a> You might be familiar with the game or even planted a tree or two, but what you might not know about is the vast number of <i>Minecraft</i> online communities. In this post, I'm going to describe how I used Cloudflare Workers to deploy and scale a production-grade API that solves a big problem for these <i>Minecraft</i> websites.</p>
    <div>
      <h3>Introducing the Problem</h3>
      <a href="#introducing-the-problem">
        
      </a>
    </div>
    <p>Here is an example of my <i>Minecraft</i> <a href="https://stratus.network/ElectroidFilms">player profile</a> from one of the many multiplayer websites. It shows some identity information such as my username, a bitmap of my avatar, and a preview of my friends. Although rendering this page with 49 bitmap avatars may seem like an easy task, it's far from trivial. In fact, it's unnecessarily complicated.</p><p>Here is the current workflow to render a player profile on a website given their username:</p><ol><li><p>Find the UUID from the player's username.</p></li></ol>
            <pre><code>curl api.mojang.com/users/profiles/minecraft/ElectroidFilms
{
    "id": "dad8b95ccf6a44df982e8c8dd70201e0",
    "name": "ElectroidFilms"
}</code></pre>
            <ol><li><p>Use that UUID to fetch the latest player information from the session server.</p></li></ol>
            <pre><code>curl sessionserver.mojang.com/session/minecraft/profile/dad8b95cc...
{
    "id": "dad8b95ccf6a44df982e8c8dd70201e0",
    "name": "ElectroidFilms",
    "properties": [{
      "name": "textures",
      "value": "eyJ0aW1lc3RhbXAiOjE1MzI1MDI..." // &lt;base64&gt;
    }]
}</code></pre>
            <ol><li><p>Decode the textures string which is encoded as base64.</p></li></ol>
            <pre><code>echo "eyJ0aW1lc3RhbXAiOjE1MzI1MDIwNDY5NjIsIn..." | base64 --decode
{
    "timestamp": 1532502046962,
    "profileId": "dad8b95ccf6a44df982e8c8dd70201e0",
    "profileName": "ElectroidFilms",
    "textures": {
      "SKIN": {"url": "textures.minecraft.net/texture/741df6aa0..."},
      "CAPE": {"url": "textures.minecraft.net/texture/e7dfea16d..."}
    }
}</code></pre>
            <ol><li><p>Fetch the texture from the URL in the decoded JSON payload.</p></li></ol>
            <pre><code>curl textures.minecraft.net/texture/741df6aa027... &gt; skin.png</code></pre>
            <ol><li><p>Cache the texture in a database to avoid the 60-second rate limit.</p></li></ol>
            <pre><code>mongo
&gt; db.users.findOneAndUpdate(
      { _id: "dad8b95ccf6a44df982e8c8dd70201e0" },
      { skin_png: new BinData(0, "GWA3u4F42GIH318sAlN2wfDAWTQ...") })</code></pre>
            <p>Yikes, that's 5 complex operations required to render a single avatar! But that's not all, in my example profile, there are 49 avatars, which would require a total of <code>5 * 49 = 245</code> operations.</p><p>And that's just <i>fetching</i> the data, we haven't even started to serve it to players! Then you have to setup a host to serve the web traffic, ensure that the service scales with demand, handle cache expiration of assets, and deploy across multiple regions. Then you have to deploy There has to be a better way!</p>
    <div>
      <h3>Prototyping with Workers</h3>
      <a href="#prototyping-with-workers">
        
      </a>
    </div>
    <p>I'm a strong believer in the future of serverless computing. So naturally, when I learned how <a href="https://developers.cloudflare.com/workers/about/">Cloudflare Workers</a> allow you to run JavaScript code in 150+ points of presence, I started to tinker with the possibilities of solving this problem. After looking at the documentation and using the <a href="https://cloudflareworkers.com">Workers playground</a>, I quickly put together some JavaScript code that aggregated all that profile complexity into a single request.</p>
            <pre><code>addEventListener('fetch', event =&gt; {
  event.respondWith(renderPlayerBitmap(event.request))
})

async function renderPlayerBitmap(request) {
  var username = request.url.split("/").pop()
  console.log("Starting request for... " + username)

  // Step 1: Username -&gt; UUID
  var uuid = await fetch("https://api.mojang.com/users/profiles/minecraft/" + username)
  if(uuid.ok) {
    uuid = (await uuid.json()).id
    console.log("Found uuid... " + uuid)

    // Step 2: UUID -&gt; Profile
    var session = await fetch("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid)
    if(session.ok) {
      session = await session.json()
      console.log("Found session... " + JSON.stringify(session))

      // Step 3: Profile -&gt; Texture URL
      var texture = atob(session.properties[0].value)
      console.log("Found texture... " + texture)

      // Step 4 + 5: Texture URL -&gt; Texture PNG + Caching
      texture = JSON.parse(texture)
      return fetch(texture.textures.SKIN.url, cf: {cacheTtl: 60})
    }
  }

  return new Response(undefined, {status: 500})
}
</code></pre>
            <p>Within a couple minutes I had my first Workers implementation! I gave it my username and it was able to make all the necessary sub-requests to return my player's bitmap texture.</p><p>After realizing the potential of Workers, I started to wonder if I could use it for more than just a single script. What if I could design and deploy a production-ready API for <i>Minecraft</i> that runs exclusively on Workers?</p>
    <div>
      <h3>Designing an API</h3>
      <a href="#designing-an-api">
        
      </a>
    </div>
    <p>I wanted to address an essential problem for <i>Minecraft</i> developers: too many APIs with too many restrictions. The hassle of parsing multiple requests and handling errors prevents developers from focusing on creating great experiences for players. There needs to be a solution that requires only <b>1 HTTP request with no rate limiting and no client-side caching.</b> After looking at the various use-cases for the existing APIs, I created a JSON schema that encompassed all the essential data into a single response:</p>
            <pre><code>GET: api.ashcon.app/mojang/v1/user/&lt;username|uuid&gt;</code></pre>
            
            <pre><code>{
  "uuid": "&lt;uuid&gt;",
  "username": "&lt;username&gt;",
  "username_history": [
    {
      "username": "&lt;username&gt;",
      "changed_at": "&lt;date|null&gt;"
    }
  ],
  "textures": {
    "slim": "&lt;boolean&gt;",
    "custom": "&lt;boolean&gt;",
    "skin": {
      "url": "&lt;url&gt;",
      "data": "&lt;base64&gt;"
    },
    "cape": {
      "url": "&lt;url|null&gt;",
      "data": "&lt;base64|null&gt;"
    }
  },
  "cached_at": "&lt;date&gt;"
}</code></pre>
            <p>One of the primary goals I had in mind was to minimize sub-requests by clients. For example, instead of giving developers a URL to a <code>image/png</code> static asset, why not fetch it for them and embed it as a base64 string? Now that's simplicity!</p>
    <div>
      <h3>Getting Started</h3>
      <a href="#getting-started">
        
      </a>
    </div>
    <p>For this project, I decided to use <a href="https://coffeescript.org">CoffeeScript</a>, which transcompiles to JavaScript and has a simple syntax. We'll also need to use <a href="https://webpack.js.org">Webpack</a> to bundle all of our code into a single JavaScript file to upload to Cloudflare.</p>
            <pre><code># Welcome to CoffeeScript!

str = "heyo! #{40+2}"    # 'heyo! 42'
num = 12 if str?         # 12
arr = [1, null, "apple"] # [1, null, 'apple']
val = arr[1]?.length()   # null
hash =                   # {key: 'value'}
  key: "value"

add = (a, b, {c, d} = {}) -&gt;
  c ?= 3
  d ?= 4
  a + b + c + d

add(1, 2, d: 5) # 1 + 2 + 3 + 5 = 11</code></pre>
            <p>First, let's make sure we have the proper dependencies installed for the project! These commands will create a <code>package.json</code> file and a <code>node_modules/</code> folder in our workspace.</p>
            <pre><code>mkdir -p workspace/src
cd workspace
npm init --yes
npm install --save-dev webpack webpack-cli coffeescript coffee-loader workers-preview</code></pre>
            <p>Now, we're going to edit our <code>package.json</code> to add two helper scripts for later. You can delete the default <code>"test"</code> script as well.</p>
            <pre><code>"scripts": {
  "build": "webpack",
  "build:watch": "webpack --watch",
  "preview": "workers-preview &lt; dist/bundle.js"
}</code></pre>
            <p>We also need to initialize a <code>webpack.config.js</code> file with a CoffeeScript compiler.</p>
            <pre><code>const path = require('path')

module.exports = {
  entry: {
    bundle: path.join(__dirname, './src/index.coffee'),
  },
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist'),
  },
  mode: 'production',
  watchOptions: {
    ignored: /node_modules|dist|\.js/g,
  },
  resolve: {
    extensions: ['.coffee', '.js', '.json'],
    plugins: [],
  },
  module: {
    rules: [
      {
        test: /\.coffee?$/,
        loader: 'coffee-loader',
      }
    ]
  }
}</code></pre>
            <p>Before we start coding, we'll create a <code>src/index.coffee</code> file and make sure everything is working so far.</p>
            <pre><code>addEventListener('fetch', (event) -&gt;
  event.respondWith(route(event.request)))

# We will populate this with our own logic after we test it!
route = (request) -&gt;
  fetch('https://api.ashcon.app/mojang/v1/user/ElectroidFilms')</code></pre>
            <p>Open your terminal in the <code>workspace/</code> directory and run the following commands:</p>
            <pre><code>npm run build
npm run preview</code></pre>
            <p>Your computer's default internet browser will open up a new window and preview the result of our Worker. If you see a JSON response, then everything is working properly and we're ready to go!</p>
    <div>
      <h3>Writing Production Code for Workers</h3>
      <a href="#writing-production-code-for-workers">
        
      </a>
    </div>
    <p>Now that we're setup with a working example, we can design our source code file structure. It's important that we break up our code into easily testable chunks, so I've gone ahead and outlined the approach that I took with this project:</p>
            <pre><code>src/
  index.coffee  # routing and serving requests
  api.coffee    # logic layer to mutate and package requests
  mojang.coffee # non-logic layer to send upstream requests
  http.coffee   # HTTP requesting, parsing, and responding
  util.coffee   # util methods and extensions</code></pre>
            <p>If you've feeling adventurous, I've included a simplified version of my API code that you can browse through below. If you look at each file, you'll have a fully working implementation by the end! Otherwise, you can continue reading to learn about my deployment and analysis of the APIs impact.</p>
    <div>
      <h4><code>http.coffee</code></h4>
      <a href="#http-coffee">
        
      </a>
    </div>
    <p>Since our API will be making several HTTP requests, it's a good idea to code some common <code>request</code> and <code>respond</code> methods that can be reused among multiple requests. At the very least, we need to support parsing JSON or base64 responses and sending JSON or string data back to the client.</p>
            <pre><code># Send a Http request and get a response.
#
# @param {string} url - Url to send the request.
# @param {string} method - Http method (get, post, etc).
# @param {integer} ttl - Time in seconds for Cloudflare to cache the request.
# @param {boolean} json - Whether to parse the response as json.
# @param {boolean} base64 - Whether to parse the response as a base64 string.
# @returns {promise&lt;
#   json -&gt; [err, json]
#   base64 -&gt; string|null
#   else -&gt; response
# &gt;} - A different response based on the method parameters above.
export request = (url, {method, ttl, json, base64} = {}) -&gt;
  method ?= "GET"
  response = await fetch(url, method: method, cf: {cacheTtl: ttl} if ttl)
  if json # Return a tuple of [err, json].
    if err = coerce(response.status)
      [err, null]
    else
      [null, await response.json()]
  else if base64 # Return base64 string or null.
    if response.ok
      Buffer.from(await response.arrayBuffer(), "binary").toString("base64")
  else # If no parser is specified, just return the raw response.
    response

export get = (url, options = {}) -&gt;
  request(url, Object.assign(options, {method: "GET"}))

# Respond to a client with a http response.
#
# @param {object} data - Data to send back in the response.
# @param {integer} code - Http status code.
# @param {string} type - Http content type.
# @param {boolean} json - Whether to respond in json.
# @param {boolean} text - Whether to respond in plain text.
# @returns {response} - Raw response object.
export respond = (data, {code, type, json, text} = {}) -&gt;
  code ?= 200
  if json
    type = "application/json"
    # "Pretty-print" our JSON response with 2 spaces.
    data = JSON.stringify(data, undefined, 2)
  else if text
    type = "text/plain"
    data = String(data)
  else
    type ?= "application/octet-stream"
  new Response(data, {status: code, headers: {"Content-Type": type}})

export error = (reason = null, {code, type} = {}) -&gt;
  code ?= 500
  type ?= "Internal Error"
  # An example would be: "Internal Error - 500 (this is the reason)"
  respond("#{code} - #{type}" + (if reason then " (#{reason})" else ""), code: code, text: true)

export badRequest = (reason = null) -&gt;
  error(reason, code: 400, type: "Bad Request")

export notFound = (reason = null) -&gt;
  error(reason, code: 404, type: "Not Found")

export tooManyRequests = (reason = null) -&gt;
  error(reason, code: 429, type: "Too Many Requests")

# Convert common http error codes into error responses.
#
# @param {integer} code - Http status code.
# @returns {response|null} - An error response or null if a 200 code.
export coerce = (code) -&gt;
  switch code
    when 200 then null
    # Some Minecraft APIs use 204 as a stand-in for a 404.
    when 204 then notFound()
    when 400 then invalidRequest()
    # Theoretically this should never happen, but sometimes does.
    when 429 then tooManyRequests()
    else error("Unknown Response", code: code)</code></pre>
            <p>The <code>cf</code> key can be used to control various Cloudflare features, including how sub-requests are cached. See the <a href="https://developers.cloudflare.com/workers/reference/cloudflare-features/">Workers documentation</a> for a more in-depth explanation.</p>
            <pre><code>cf:
  cacheTtl: 120 # Cache for 2 mins.
  # Pro+ only.
  polish: "lossless" # Compress image data.
  # Enterprise only.
  cacheTtlByStatus:
    "200-299": 60 # Cache for 60 secs.
    "300-399": 0  # Cache but expire instantly.
    "400-404": 10 # Cache for 10 secs.
    "405-599": -1 # Do not cache at all.
  cacheKey: url # Cache lookup key, defaults to the request URL.</code></pre>
            
    <div>
      <h4><code>mojang.coffee</code></h4>
      <a href="#mojang-coffee">
        
      </a>
    </div>
    <p>Now that we have code to send and parse requests, we can create an interface to retrieve data from the upstream APIs. It's good to note that there should be no mutation logic in this file, it's purpose is just to get the old APIs, not change them.</p>
            <pre><code>import { get } from "./http"

# Get the UUID of a username at the current time.
#
# @param {string} name - Minecraft username.
# @throws {204} - When no user exists with that name.
# @returns {[err, json]} - An error or username and UUID response.
export usernameToUuid = (name) -&gt;
  get("https://api.mojang.com/users/profiles/minecraft/#{name}", json: true)

# Get the history of usernames for the given UUID.
#
# @param {string} id - The UUID to check the username history.
# @returns {[err, json]} - An error or the username history.
export uuidToUsernameHistory = (id) -&gt;
  get("https://api.mojang.com/user/profiles/#{id}/names", json: true)

# Get the session profile of the UUID.
#
# @param {string} id - UUID to get the session profile.
# @returns {[err, json]} - An error or the session profile.
export uuidToProfile = (id) -&gt;
  get("https://sessionserver.mojang.com/session/minecraft/profile/#{id}", json: true)</code></pre>
            
    <div>
      <h4><code>api.coffee</code></h4>
      <a href="#api-coffee">
        
      </a>
    </div>
    <p>This is where the bulk of our API logic will reside. I've broken up the process into 3 interdependent tasks that are executed in order:</p><ol><li><p>Given a username, fetch its UUID.</p></li><li><p>Given a UUID, fetch the user's profile.</p></li><li><p>Given a user's profile, decode and fetch the textures.</p></li></ol>
            <pre><code>import { get, respond, error, notFound, badRequest } from "./http"
import { usernameToUuid, uuidToProfile, uuidToUsernameHistory } from "./mojang"

# Get the uuid of a user given their username.
#
# @param {string} name - Minecraft username, must be alphanumeric 16 characters.
# @returns {[err, response]} - An error or the dashed uuid of the user.
export uuid = (name) -&gt;
  if name.asUsername() # Fits regex of a Minecraft username.
    [err, res] = await usernameToUuid(name)
    if id = res?.id?.asUuid(dashed: true)
      [null, respond(id, text: true)]
    else # Response was received, but contains no UUID.
      [err || notFound(), null]
  else
    [badRequest("malformed username '#{name}'"), null]

# Get the full profile of a user given their uuid or username.
#
# @param {string} id - Minecraft username or uuid.
# @returns {[err, json]} - An error or user profile.
export user = (id) -&gt;
  if id.asUsername()
    [err, res] = await uuid(id)
    if err # Could not find a player with that username.
      [err, null]
    else # Recurse with the new UUID.
      await user(id = await res.text())
  else if id.asUuid()
    # Fetch the profile and usernames in parallel.
    [[err0, profile], [err1, history]] = await Promise.all([
      uuidToProfile(id = id.asUuid())
      uuidToUsernameHistory(id)])
    # Extract the textures from the profile.
    # Since this operation is complex, off-load
    # the logic into its own method.
    [err2, texture] = await textures(profile)
    if err = err0 || err1 || err2
      [err, null] # One of the last three operations failed.
    else
      # Everything is good, now just put the data together.
      [null, respond(
        uuid: profile.id.asUuid(dashed: true)
        username: profile.name
        username_history: history.map((item) -&gt;
          username: item.name
          changed_at: item.changedToAt?.asDate())
        textures: texture
        cached_at: new Date(),
      json: true)]
  else
    [badRequest("malformed uuid '#{id}'"), null]

# Parse and decode base64 textures from the user profile.
#
# @param {json} profile - User profile from #uuidToProfile(id).
# @returns {json} - Enhanced user profile with more convient texture fields.
textures = (profile) -&gt;
  unless profile # Will occur if the profile api failed.
    return [error("no user profile found"), null]
  properties = profile.properties
  if properties.length == 1
    texture = properties[0]
  else
    texture = properties.filter((pair) -&gt; pair.name == "textures" &amp;&amp; pair.value?)[0]
  # If a embedded texture does not exist or is empty,
  # that user does not have a custom skin.
  if !texture || (texture = JSON.parse(atob(texture.value)).textures).isEmpty()
    skinUrl = "http://assets.mojang.com/SkinTemplates/steve.png"
  # Fetch the skin and cape data in parallel, and cache for a day.
  [skin, cape] = await Promise.all([
    get(skinUrl ?= texture.SKIN?.url, base64: true, ttl: 86400)
    get(capeUrl = texture.CAPE?.url, base64: true, ttl: 86400)])
  unless skin
    [error("unable to fetch skin '#{skinUrl}'"), null]
  else
    texture =
      slim: texture.SKIN?.metadata?.model == "slim"
      skin: {url: skinUrl, data: skin}
      cape: {url: capeUrl, data: cape} if capeUrl
    [null, texture]</code></pre>
            
    <div>
      <h4><code>index.coffee</code></h4>
      <a href="#index-coffee">
        
      </a>
    </div>
    <p>Now, we parse the request's route and respond with the corresponding API.</p>
            <pre><code>import "./util"
import { notFound } from "./http"
import { uuid, user } from "./api"

addEventListener("fetch", (event) -&gt;
  event.respondWith(route(event.request)))

route = (request) -&gt;
  [base, version, method, id] = request.url.split("/")[3..6]
  if base == "mojang" &amp;&amp; id?
    if version == "v1"
      v1(method, id)
    else
      notFound("unknown api version '#{version}'")
  else
    notFound("unknown route")

v1 = (method, id) -&gt;
  if method == "uuid"
    [err, res] = await uuid(id)
  else if method == "user"
    [err, res] = await user(id)
  err || res || notFound("unknown v1 route '#{method}'")</code></pre>
            
    <div>
      <h4><code>util.coffee</code></h4>
      <a href="#util-coffee">
        
      </a>
    </div>
    <p>Finally, we'll add some prototype extensions that we used along the way.</p>
            <pre><code># Insert a string at a given index.
#
# @param {integer} i - Index to insert the string at.
# @param {string} str - String to insert.
String::insert = (i, str) -&gt;
  this.slice(0, i) + str + this.slice(i)

# Ensure that the string is a valid Uuid.
#
# If dashed is enabled, it is possible the input
# string is not the same as the output string.
#
# @param {boolean} dashed - Whether to return a dashed uuid.
# @returns {string|null} - A uuid or null.
String::asUuid = ({dashed} = {}) -&gt;
  if match = uuidPattern.exec(this)
    uuid = match[1..].join("")
    if dashed
      uuid.insert(8, "-")
          .insert(12+1, "-")
          .insert(16+2, "-")
          .insert(20+3, "-")
    else
      uuid
uuidPattern = /^([0-9a-f]{8})(?:-|)([0-9a-f]{4})(?:-|)(4[0-9a-f]{3})(?:-|)([0-9a-f]{4})(?:-|)([0-9a-f]{12})$/i

# Ensure that the string is a valid Minecraft username.
#
# @returns {string|null} - Minecraft username or null.
String::asUsername = -&gt;
  if usernamePattern.test(this) then this else false
usernamePattern = /^[0-9A-Za-z_]{1,16}$/i

# Ensure that the unix number is a Date.
#
# @returns {date} - The number as a floored date.
Number::asDate = -&gt;
  new Date(Math.floor(this))

# Determine if the object is empty.
#
# @returns {boolean} - Whether the object is empty.
Object::isEmpty = -&gt;
  Object.keys(this).length == 0</code></pre>
            
    <div>
      <h3>Analyzing a Workers Deployment</h3>
      <a href="#analyzing-a-workers-deployment">
        
      </a>
    </div>
    <p>I've had this code deployed and tested by real <i>Minecraft</i> users for the past few weeks. As a developer that has global web traffic, it's pivotal that players can quickly get access to my services. The essential advantage of Workers is that I don't need to deploy several replicas of my code to different cloud regions, it's everywhere! That means players from any part of the world get the same great web experience with minimal latency.</p><p>As of today, the API is processing over <b>400k requests per day</b> from users all over the world! Cloudflare caches responses in the closest point of presence to the client, so I don't need to setup a database and developers don't need to worry about rate-limiting.</p><p>Since each request to the API generates 4 to 5 additional sub-requests, it handles approximately <b>1.8 million fetches per day</b> with a 88% cache hit rate.</p>
    <div>
      <h3>Wrapping Up</h3>
      <a href="#wrapping-up">
        
      </a>
    </div>
    <p>Cloudflare Workers have enabled me to solve complex technical problems without worrying about host infrastructure or cloud regions. It's simple, easy to deploy, and works blazing fast all around the world. And for <b>50 cents for every 1 million requests</b>, it's incomparable to the other serverless solutions on the market.</p><p>If you're not already convinced to start using Workers, here's the deployment history of my API. I went from <b>0 to 5 million requests</b> with no scaling, no resizing, no servers, no clusters, and no containers. Just code.</p><p>If you're interested in looking at all of the code used in the post, you can find it here:<a href="https://github.com/Electroid/mojang-api">https://github.com/Electroid/mojang-api</a></p><p>And if you're a <i>Minecraft</i> developer, my API is open for you to use for free:</p>
            <pre><code>curl https://api.ashcon.app/mojang/v1/uuid/ElectroidFilms
curl https://api.ashcon.app/mojang/v1/user/ElectroidFilms</code></pre>
            <p>You can also use this <a href="https://github.com/Electroid/mojang-api/tree/master/avatar">extra goodie</a> that will crop just the face from a player texture:</p>
            <pre><code>curl https://api.ashcon.app/mojang/v1/avatar/ElectroidFilms &gt; avatar.png
open avatar.png</code></pre>
             ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Programming]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">HyCfVxEO5PIYjdk0kc5y1</guid>
            <dc:creator>Ashcon Partovi</dc:creator>
        </item>
    </channel>
</rss>