
<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>Sun, 12 Apr 2026 17:05:48 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Connecting to production: the architecture of remote bindings]]></title>
            <link>https://blog.cloudflare.com/connecting-to-production-the-architecture-of-remote-bindings/</link>
            <pubDate>Wed, 12 Nov 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Remote bindings allow you to connect your local Worker code to deployed Cloudflare resources like R2 and D1. Come along on the technical journey of how we built this feature to create a seamless local development experience. ]]></description>
            <content:encoded><![CDATA[ <p>Remote bindings are bindings that connect to a deployed resource on your Cloudflare account <i>instead </i>of a locally simulated resource – and recently, we announced that <a href="https://blog.cloudflare.com/cloudflare-developer-platform-keeps-getting-better-faster-and-more-powerful/#connect-to-production-services-and-resources-from-local-development-with-remote-bindings-now-ga"><u>remote bindings are now generally available</u></a>. </p><p>With this launch, you can now connect to deployed resources like <a href="https://developers.cloudflare.com/r2/"><u>R2 buckets</u></a> and <a href="https://www.cloudflare.com/developer-platform/products/d1/"><u>D1 databases</u></a> while running Worker code on your local machine. This means you can test your local code changes against real data and services, without the overhead of deploying for each iteration. </p><p>In this blog post, we’ll dig into the technical details of how we built it, creating a seamless local development experience.</p>
    <div>
      <h3>Developing on the Workers platform</h3>
      <a href="#developing-on-the-workers-platform">
        
      </a>
    </div>
    <p>A key part of the <a href="https://www.cloudflare.com/developer-platform/products/workers/"><u>Cloudflare Workers platform</u></a> has been the ability to develop your code locally without having to deploy it every time you wanted to test something – though the way we’ve supported this has changed greatly over the years. </p><p>We started with <code>wrangler</code> dev running in remote mode. This works by deploying and connecting to a preview version of your Worker that runs on Cloudflare’s network every time you make a change to your code, allowing you to test things out as you develop. However, remote mode isn’t perfect — it’s complex and hard to maintain. And the developer experience leaves a lot to be desired: slow iteration speed, unstable debugging connections, and lack of support for multi-worker scenarios. </p><p>Those issues and others motivated a significant investment in a fully local development environment for Workers, which was released in mid-2023 and became the <a href="https://blog.cloudflare.com/wrangler3/"><u>default experience for wrangler dev</u></a>. Since then, we've put a huge amount of work into the local dev experience with <a href="https://developers.cloudflare.com/workers/wrangler/"><u>Wrangler</u></a>, the <a href="https://developers.cloudflare.com/workers/vite-plugin/"><u>Cloudflare Vite plugin</u></a> (alongside <a href="https://developers.cloudflare.com/workers/testing/vitest-integration/"><u>@cloudflare/vitest-pool-workers</u></a>) &amp; <a href="https://developers.cloudflare.com/workers/testing/miniflare/"><u>Miniflare</u></a>.</p><p>Still, the original remote mode remained accessible via a flag: <code>wrangler dev --remote</code>. When using remote mode, all the DX benefits of a fully local experience and the improvements we’ve made over the last few years are bypassed. So why do people still use it? It enables one key unique feature: binding to remote resources while locally developing. When you use local mode to develop a Worker locally, all of your <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/"><u>bindings</u></a> are simulated locally using local (initially empty) data. This is fantastic for iterating on your app’s logic with test data – but sometimes that’s not enough, whether you want to share resources across your team, reproduce bugs tied to real data, or just be confident that your app will work in production with real resources.</p><p>Given this, we saw an opportunity: If we could bring the best parts of remote mode (i.e. access to remote resources) to <code>wrangler dev</code>, there’d be one single flow for developing Workers that would enable many use cases, while not locking people out of the advancements we’ve made to local development. And that’s what we did! </p><p>As of Wrangler v4.37.0 you can pick on a per-binding basis whether a binding should use remote or local resources, simply by specifying the <code>remote</code> option. It’s important to re-emphasise this—you only need to add <code>remote: true!</code> There’s no complex management of API keys and credentials involved, it all just works using Wrangler’s existing Oauth connection to the Cloudflare API.</p>
            <pre><code>{
  "name": "my-worker",
  "compatibility_date": "2025-01-01",
  "kv_namespaces": [{
    "binding": "KV",
    "id": "my-kv-id",
  },{
    "binding": "KV_2",
    "id": "other-kv-id",
    "remote": true
  }],
  "r2_buckets": [{
    "bucket_name": "my-r2-name",
    "binding": "R2"
  }]
}</code></pre>
            <p>The eagle-eyed among you might have realised that some bindings already worked like this, accessing remote resources from local dev. Most prominently, the <a href="https://developers.cloudflare.com/workers-ai/configuration/bindings/"><u>AI binding</u></a> was a trailblazer for what a general remote bindings solution could look like. From its introduction, the AI binding always connected to a remote resource, since a true local experience that supports all the different models you can use with Workers AI would be impractical and require a huge upfront download of AI models. </p><p>As we realised different products within Workers needed something similar to remote bindings (Images and Hyperdrive, for instance), we ended up with a bit of a patchwork of different solutions. We’ve now unified under a single remote bindings solution that works for all binding types.</p>
    <div>
      <h3>How we built it</h3>
      <a href="#how-we-built-it">
        
      </a>
    </div>
    <p>We wanted to make it really easy for developers to access remote resources without having to change their production Workers code, and so we landed on a solution that required us to fetch data from the remote resource at the point of use in your Worker.</p>
            <pre><code>const value = await env.KV.get("some-key")</code></pre>
            <p><sup><i>The above code snippet shows accessing the “some-key” value in the env.KV </i></sup><a href="https://developers.cloudflare.com/kv/api/read-key-value-pairs/"><sup><i><u>KV namespace</u></i></sup></a><sup><i>, which is not available locally and needs to be fetched over the network.</i></sup></p><p>So if that was our requirement, how would we get there? For instance, how would we get from a user calling <code>env.KV.put(“key”, “value”)</code> in their Worker to actually storing that in a remote KV store? The obvious solution was perhaps to use the <a href="https://developers.cloudflare.com/api/resources/kv/subresources/namespaces/subresources/values/methods/update/"><u>Cloudflare API</u></a>. We could have just replaced the entire env locally with stub objects that made API calls, transforming <code>env.KV.put()</code> into PUT <code>http:///accounts/{account_id}/storage/kv/namespaces/{namespace_id}/values/{key_name}</code>. </p><p>This would’ve worked great for KV, R2, D1, and other bindings with mature HTTP APIs, but it would have been a pretty complex solution to implement and maintain. We would have had to replicate the entire bindings API surface and transform every possible operation on a binding to an equivalent API call. Additionally, some binding operations don’t have an equivalent API call, and wouldn’t be supportable using this strategy.</p><p>Instead, we realised that we already had a ready-made API waiting for us — the one we use in production! </p>
    <div>
      <h3>How bindings work under the hood in production</h3>
      <a href="#how-bindings-work-under-the-hood-in-production">
        
      </a>
    </div>
    <p>Most bindings on the Workers platform boil down to essentially a <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/"><u>service binding</u></a>. A service binding is a link between two Workers that allows them to communicate over HTTP or <a href="https://blog.cloudflare.com/javascript-native-rpc/"><u>JSRPC</u></a> (we’ll come back to JSRPC later). </p><p>For example, the KV binding is implemented as a service binding between your authored Worker and a platform Worker, speaking HTTP. The JS API for the KV binding is implemented in the Workers runtime, and translates calls like <code>env.KV.get()</code> to HTTP calls to the Worker that implements the KV service. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4PwugWAyVPMq086BKt9cjh/cfea7dbb50b9b2983bd21fcb7b80334d/image2.png" />
          </figure><p><i><sup>Diagram showing a simplified model of how a KV binding works in production</sup></i></p><p>You may notice that there’s a natural async network boundary here — between the runtime translating the <code>env.KV.get()</code> call and the Worker that implements the KV service. We realised that we could use that natural network boundary to implement remote bindings. Instead of the <i>production</i> runtime translating <code>env.KV.get()</code> to an HTTP call, we could have the <i>local</i> runtime (<a href="https://github.com/cloudflare/workerd"><u>workerd</u></a>) translate <code>env.KV.get()</code> to an HTTP call, and then send it directly to the KV service, bypassing the production runtime. And so that’s what we did!</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7JNPZ2MM5IZ4TSXfN0qywV/804b7997d115405bc3f4d335cb21e626/image3.png" />
          </figure><p><sup><i>Diagram showing a locally run worker with a single KV binding, with a single remote proxy client that communicates to the remote proxy server, which in turn communicates with the remote KV</i></sup></p><p>The above diagram shows a local Worker running with a remote KV binding. Instead of being handled by the local KV simulation, it’s now being handled by a remote proxy client. This Worker then communicates with a remote proxy server connected to the real remote KV resource, ultimately allowing the local Worker to communicate with the remote KV data seamlessly.</p><p>Each binding can independently either be handled by a remote proxy client (all connected to the same remote proxy server) or by a local simulation, allowing for very dynamic workflows where some bindings are locally simulated while others connect to the real remote resource, as illustrated in the example below:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6yUYvFjpxGv7GcC12l7zr1/a7d46f33869448e1f3f13540d741ff40/image1.png" />
          </figure><p><sup><i>The above diagram and config shows a Worker (running on your computer) bound to 3 different resources—two local (KV &amp; R2), and one remote (KV_2)</i></sup></p>
    <div>
      <h3>How JSRPC fits in</h3>
      <a href="#how-jsrpc-fits-in">
        
      </a>
    </div>
    <p>The above section deals with bindings that are backed by HTTP connections (like KV and R2), but modern bindings use <a href="https://blog.cloudflare.com/javascript-native-rpc/"><u>JSRPC</u></a>. That means we needed a way for the locally running <code>workerd </code>to speak JSRPC to a production runtime instance. </p><p>In a stroke of good luck, a parallel project was going on to make this possible, as detailed in the <a href="https://blog.cloudflare.com/capnweb-javascript-rpc-library/"><u>Cap’n Web blog</u></a>. We integrated that by making the connection between the local <code>workerd</code> instance and the remote runtime instance communicate over <a href="https://github.com/cloudflare/capnweb"><u>websockets using Cap’n Web</u></a>, enabling bindings backed by JSRPC to work. This includes newer bindings like <a href="https://developers.cloudflare.com/images/transform-images/transform-via-workers/"><u>Images</u></a>, as well as JSRPC service bindings to your own Workers.</p>
    <div>
      <h3>Remote bindings with Vite, Vitest and the JavaScript ecosystem</h3>
      <a href="#remote-bindings-with-vite-vitest-and-the-javascript-ecosystem">
        
      </a>
    </div>
    <p>We didn't want to limit this exciting new feature to only <code>wrangler dev</code>. We wanted to support it in our Cloudflare Vite Plugin and vitest-pool-workers packages, as well as allowing any other potential tools and use cases from the JavaScript ecosystem to also benefit from it.</p><p>In order to achieve this, the wrangler package now exports utilities such as <code>startRemoteProxySession</code> that allow tools not leveraging <code>wrangler dev</code> to also support remote bindings. You can find more details in the <a href="https://developers.cloudflare.com/workers/development-testing/#remote-bindings"><u>official remote bindings documentation</u></a>.</p>
    <div>
      <h3>How do I try this out?</h3>
      <a href="#how-do-i-try-this-out">
        
      </a>
    </div>
    <p>Just use <code>wrangler dev</code>! As of Wrangler v4.37.0 (<code>@cloudflare/vite-plugin</code> v1.13.0, <code>@cloudflare/vitest-pool-workers</code> v0.9.0), remote bindings are available in all projects, and can be turned on a per-binding basis by adding <code>remote: true</code> to the binding definition in your <a href="https://developers.cloudflare.com/workers/wrangler/configuration/"><u>Wrangler config file</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[R2]]></category>
            <category><![CDATA[D1]]></category>
            <guid isPermaLink="false">60miu6u2Fk0E5CPdKOCYWX</guid>
            <dc:creator>Samuel Macleod</dc:creator>
            <dc:creator>Dario Piotrowicz</dc:creator>
        </item>
        <item>
            <title><![CDATA[Deploy your Next.js app to Cloudflare Workers with the Cloudflare adapter for OpenNext]]></title>
            <link>https://blog.cloudflare.com/deploying-nextjs-apps-to-cloudflare-workers-with-the-opennext-adapter/</link>
            <pubDate>Tue, 08 Apr 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ With the 1.0-beta release of the Cloudflare adapter for OpenNext, you can host your Next.js 14 and 15 applications on Cloudflare Workers. ]]></description>
            <content:encoded><![CDATA[ <p>We first announced the Cloudflare adapter for OpenNext at <a href="https://blog.cloudflare.com/builder-day-2024-announcements/#cloudflare-joins-opennext"><u>Builder Day 2024</u></a>. It transforms Next.js applications to enable them to run on Cloudflare’s infrastructure.</p><p>Over the seven months since that September announcement, we have been working hard to improve the adapter. It is now more tightly integrated with OpenNext to enable supporting many more Next.js features. We kept improving the <a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>Node.js compatibility</u></a> of Workers and <a href="https://github.com/unjs/unenv"><u>unenv</u></a> was also improved to <a href="https://developer.mozilla.org/en-US/docs/Glossary/Polyfill"><u>polyfill</u></a> the Node.js features not yet implemented by the runtime.</p><p>With all of this work, we are proud to announce the 1.0.0-beta release of <a href="https://www.npmjs.com/package/@opennextjs/cloudflare"><u>@opennextjs/cloudflare</u></a>. Using the Cloudflare adapter is now the preferred way to deploy Next applications to the Cloudflare platform, instead of <a href="https://blog.cloudflare.com/next-on-pages/"><u>Next on Pages</u></a>.</p><p>Read on to learn what is possible today, and about our plans for the coming months.</p>
    <div>
      <h2>OpenNext</h2>
      <a href="#opennext">
        
      </a>
    </div>
    <p><a href="https://opennext.js.org/"><u>OpenNext</u></a> is a build tool designed to transform Next.js applications into packages optimized for deployment across various platforms. Initially created for serverless environments on AWS Lambda, OpenNext has expanded its capabilities to support a wider range of environments, including Cloudflare Workers and traditional Node.js servers.</p><p>By integrating with the OpenNext codebase, the Cloudflare adapter is now able to support many more features than its original version. We are also leveraging the end-to-end (e2e) test suite of OpenNext to validate the implementation of these features.

Being part of OpenNext allows us to support future Next.js features shortly after they are released. We intend to support the latest minor version of Next.js 14 and all the minor versions of Next.js 15.</p>
    <div>
      <h2>Features</h2>
      <a href="#features">
        
      </a>
    </div>
    <p>Most of the Next.js 15 features are supported in <a href="https://www.npmjs.com/package/@opennextjs/cloudflare"><code><u>@opennextjs/cloudflare</u></code></a>. You can find an exhaustive list on the <a href="https://opennext.js.org/cloudflare#supported-nextjs-features"><u>OpenNext website</u></a>, but here are a few highlights:</p><table><tr><td><p><a href="https://nextjs.org/docs/app/building-your-application/caching"><u>Caching</u></a></p></td><td><p>The Cloudflare adapter provides a cache handler for Next.js, optimizing the management of ISR/SSG and the data cache to speed up your apps.</p></td></tr><tr><td><p><a href="https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering"><u>Partial Prerendering (PPR)</u></a></p></td><td><p>PPR immediately sends the pre-rendered HTML and begins streaming from the server in parallel.</p></td></tr><tr><td><p><a href="https://nextjs.org/docs/app/building-your-application/routing/middleware"><u>Middleware</u></a></p></td><td><p>Middleware allows modifying the response by rewriting, redirecting, or modifying the request and response headers, or responding directly before the request hits the app.</p></td></tr><tr><td><p><a href="https://nextjs.org/docs/app"><u>App</u></a> and <a href="https://nextjs.org/docs/pages"><u>Pages</u></a> routers</p></td><td><p>Both the new App router and Pages routers are supported.</p></td></tr><tr><td><p><a href="https://nextjs.org/docs/pages/building-your-application/optimizing/images"><u>Image Optimization</u></a></p></td><td><p>The adapter easily integrates with <a href="https://developers.cloudflare.com/images/"><u>Cloudflare Images</u></a> to deliver optimized images.</p></td></tr></table><p>We are working on adding more features:</p><ul><li><p>Microsoft Windows is not yet fully supported by the adapter. We plan to fully support Windows for development in the 1.0 release.</p></li><li><p>The adapter currently only supports the Node runtime of Next.js. You can opt-out of the <a href="https://nextjs.org/docs/app/api-reference/edge"><u>Edge runtime</u></a> by removing <code>export const runtime = "edge"</code> from your application. We plan to add support for the edge runtime in the next major release. Note that applications deployed to Cloudflare Workers run close to the user, whatever the Next.js runtime used, giving similar performance.</p></li><li><p>Composable caching (<code>use cache</code>) should also be supported in the next major release. It is a canary feature of Next.js that is still in development. It will be supported in OpenNext once it stabilizes.</p></li></ul>
    <div>
      <h2>Evolution in the ecosystem</h2>
      <a href="#evolution-in-the-ecosystem">
        
      </a>
    </div>
    <p>While the adapter has vastly improved over the last several months, we should also mention the updates to the ecosystem that are enabling more applications to be supported.</p><p><a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"><u>NodeJS compatibility</u></a> for Workers is becoming more comprehensive with the <code>crypto</code>, <code>dns</code>, <code>timers</code>, <code>tls</code>, and <code>net</code> NodeJS modules now being natively implemented by the Workers runtime. The remaining modules that are not yet implemented are supported through <a href="https://github.com/unjs/unenv"><u>unenv</u></a>.</p><p>The Worker size limit <a href="https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits"><u>was bumped</u></a> from 1 MiB to 3 MiB on free plans and from 10 MiB to 15 MiB for paid plans. </p>
    <div>
      <h2>1.0 and the road ahead</h2>
      <a href="#1-0-and-the-road-ahead">
        
      </a>
    </div>
    <p>With the release of 1.0-beta, we expect most Next.js 14 and 15 applications to be able to run seamlessly on Cloudflare.</p><p>We have already tackled a lot of the issues reported on <a href="https://github.com/opennextjs/opennextjs-cloudflare"><u>GitHub</u></a> by early adopters, and once the adapter stabilizes, we will release the 1.0 version.</p><p>After that, we are planning a v2 release with a focus on:</p><ul><li><p>Reducing the bundle size.</p></li><li><p>Improving the application performance. The reduced bundle size and more work on the caching layer will make applications faster.</p></li><li><p>Allowing users to deploy to multiple Workers.</p></li></ul>
    <div>
      <h2>Deploy your first application to Workers</h2>
      <a href="#deploy-your-first-application-to-workers">
        
      </a>
    </div>
    <p>Developing and deploying a Next.js app on Workers is pretty simple, and you can do it today by following these steps:</p><p>Start by creating your application from a template:</p>
            <pre><code>npm create cloudflare@latest -- my-next-app --framework=next 
--platform=workers</code></pre>
            <p>You can then iterate on your application using the Next.js dev server by running <code>npm run dev</code>.</p><p>Once you are happy with your application in the development server, you can run the application on Workers locally by executing <code>npm run preview</code>, or deploy the application with <code>npm run deploy</code>.</p><div>
  
</div>
<p></p><p>You can find more details in the documentation, on both the <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/nextjs/"><u>Cloudflare site</u></a> and the <a href="https://opennext.js.org/cloudflare"><u>OpenNext site</u></a>.</p><p>We want your feedback! Report issues and contribute code at <a href="https://github.com/opennextjs/opennextjs-cloudflare/"><u>opennextjs/opennextjs-cloudflare on Github</u></a>, and join the discussion on the <a href="https://discord.gg/WUNsBM69"><u>OpenNext Discord</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <guid isPermaLink="false">2xXeB9HQ7cEwSs5NT84Wrx</guid>
            <dc:creator>Dario Piotrowicz</dc:creator>
            <dc:creator>Victor Berchet</dc:creator>
            <dc:creator>Nicolas (Guest Author)</dc:creator>
            <dc:creator>James Anderson (Guest Author)</dc:creator>
        </item>
        <item>
            <title><![CDATA["Just use Vite”… with the Workers runtime]]></title>
            <link>https://blog.cloudflare.com/introducing-the-cloudflare-vite-plugin/</link>
            <pubDate>Tue, 08 Apr 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ The Cloudflare Vite plugin integrates Vite, one of the most popular build tools for web development, with Workers runtime. Today, we announce the 1.0 release and official support for React Router v7. ]]></description>
            <content:encoded><![CDATA[ <p>Today, we are announcing the 1.0 release of the Cloudflare Vite plugin, as well as official support for React Router v7!</p><p>Over the past few years, <a href="https://vite.dev/"><u>Vite</u></a>’s meteoric rise has seen it become one of the most popular build tools for web development, with a large ecosystem and vibrant community. The <a href="https://developers.cloudflare.com/workers/vite-plugin/"><u>Cloudflare Vite plugin</u></a> brings the Workers runtime right into its beating heart! Previously, the Vite dev server would always run your server code in Node.js, even if you were deploying to Cloudflare Workers. By using the new <a href="https://vite.dev/guide/api-environment"><u>Environment API</u></a>, released experimentally in <a href="https://vite.dev/blog/announcing-vite6"><u>Vite 6</u></a>, your Worker code can now run inside the native Cloudflare Workers runtime (<a href="https://github.com/cloudflare/workerd"><u>workerd</u></a>). This means that the dev server matches the production behavior as closely as possible, and provides confidence as you develop and deploy your applications.
</p><p>Vite 6 includes the most significant changes to Vite’s architecture since its inception and unlocks many new possibilities for the ecosystem. Fundamental to this is the Environment API, which enables the Vite dev server to interact with any number of custom runtime environments. This means that it is now possible to run server code in alternative JavaScript runtimes, such as our own workerd.</p><p>We are grateful to have collaborated closely with the <a href="https://vite.dev/team"><u>Vite team</u></a> on its design and implementation. When you see first-hand the thoughtful and generous way in which they go about their work, it’s no wonder that Vite and its ecosystem are in such great shape!</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Nu8hnh2qcICkWBlXlEoFO/9e71cd44ad126d0c08960a94140dd540/unnamed__5_.png" />
          </figure><p><sup><i>Vite 6 with a Cloudflare Worker environment</i></sup><sup><b> </b></sup></p><p>Here you can see how it all fits together. The user views a page in the browser (1), which triggers a request to the Vite Dev Server (2). Vite processes the request, resolving, loading, and transforming source files into modules that are added to the client and Worker environments. The client modules are downloaded to the browser to be run as client-side JavaScript, and the Worker modules are sent to the Cloudflare Workers runtime to handle server-side requests. The request is handled by the Worker (3 and 4) and the Vite Dev Server returns the response to the browser (5), which displays the result to the user (6).</p>
    <div>
      <h2>Single-page applications</h2>
      <a href="#single-page-applications">
        
      </a>
    </div>
    <p>Vite has become the go-to choice for developing single-page applications (<a href="https://developer.mozilla.org/en-US/docs/Glossary/SPA"><u>SPAs</u></a>), whether your preferred frontend framework is <a href="https://react.dev/"><u>React</u></a>, <a href="https://vuejs.org/"><u>Vue</u></a>, <a href="https://svelte.dev/"><u>Svelte</u></a>, or one of many others.</p>
    <div>
      <h3>Create a new app</h3>
      <a href="#create-a-new-app">
        
      </a>
    </div>
    <p>Let’s try out the new Cloudflare Vite plugin by creating a new React SPA using the <code>create-cloudflare </code>CLI.</p>
            <pre><code>npm create cloudflare@latest my-react-app -- --framework=react --platform=workers</code></pre>
            <p>This command runs <code>create-vite</code> and then makes the necessary changes to incorporate the Cloudflare Vite plugin.</p><p>Using the button below, you can also create a React SPA project on Cloudflare Workers, connected to a git repository of your choice, configured with <a href="https://developers.cloudflare.com/workers/ci-cd/builds/"><u>Cloudflare Workers Builds</u></a> to automatically deploy, and set up to use the new Vite plugin for local development.</p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/staging/vite-react-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p>
    <div>
      <h3>Update an existing app</h3>
      <a href="#update-an-existing-app">
        
      </a>
    </div>
    <p>If you would instead like to update an existing Vite SPA project in the same way, you can follow these two steps:</p><p>Add the <code>@cloudflare/vite-plugin</code> dependency to the list of plugins:</p>
            <pre><code>import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { cloudflare } from "@cloudflare/vite-plugin";

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), cloudflare()],
});</code></pre>
            <p>Add a <code>wrangler.jsonc</code> configuration file alongside your Vite config:</p>
            <pre><code>{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-react-app",
  "compatibility_date": "2025-04-01",
  "assets": {
    "not_found_handling": "single-page-application",
  },
}</code></pre>
            <p>For a purely front-end application, the Cloudflare plugin integrates the Vite dev server with <a href="https://developers.cloudflare.com/workers/static-assets/"><u>Workers Assets</u></a> to ensure that settings such as <code>html_handling</code> and <code>not_found_handling</code> behave the same way as they do in production. This is just the beginning, however. The real magic happens when you add a Worker backend that is seamlessly integrated into your development and deployment workflow.</p>
    <div>
      <h3>Develop the app</h3>
      <a href="#develop-the-app">
        
      </a>
    </div>
    <p>To see this in action, start the Vite development server, which will run your Worker in the Cloudflare Workers runtime:</p>
            <pre><code>npm run dev</code></pre>
            <p>In your browser, click the first displayed button a few times to increment the counter. This is a classic SPA running JavaScript in your browser. Next, click the second button to fetch the response from the API. Notice that it displays <code>Name from API is: Cloudflare</code>. This is making an API request to a Cloudflare Worker running inside Vite.</p><p>Have a look at <code>api/index.ts</code>. This file contains a Worker that is invoked for any request not matching a static asset. It returns a JSON response if the <code>pathname</code> starts with <code>/api/</code>.</p><p>Edit <code>api/index.ts</code> by changing the <code>name</code> it returns to <code>’Cloudflare Workers’</code> and save your changes. If you click the second button in the browser again, it will now display the new <code>name</code> while preserving the previously set counter value. Vite tracked your changes and updated the Worker environment without affecting the client environment. With Vite and the Cloudflare plugin, you can iterate on the client and server parts of your app together, without losing UI state between edits.</p><p>The Cloudflare Vite integration doesn’t end with the dev server. <code>vite build</code> outputs the client and server parts of your application with a single command. <code>vite preview</code> allows you to preview your build output in the Workers runtime prior to deployment. Finally, <code>wrangler deploy</code> recognises that you have generated a Vite build and deploys your application directly without any additional bundling.</p>
    <div>
      <h2>React Router v7</h2>
      <a href="#react-router-v7">
        
      </a>
    </div>
    <p>While Vite began its life primarily as a build tool for single-page applications, it has since become the foundation for the current generation of full-stack frameworks. <a href="https://astro.build/"><u>Astro</u></a>, <a href="https://qwik.dev/"><u>Qwik</u></a>, <a href="https://reactrouter.com/"><u>React Router</u></a>, <a href="https://svelte.dev/"><u>SvelteKit</u></a> and others have all adopted Vite, drawing on its development server, build pipeline, and phenomenal developer experience. In addition to working with the Vite team on the Environment API, we have also partnered closely with the Remix team on their adoption of Vite Environments. Today, we are announcing first-class support for <a href="https://reactrouter.com/"><u>React Router v7</u></a> (the successor to Remix) in the Cloudflare Vite plugin.</p><p>You can use the <code>create-cloudflare</code> CLI to create a new React Router application configured with the Cloudflare Vite plugin.</p>
            <pre><code>npm create cloudflare@latest my-react-router-app -- --framework=react-router</code></pre>
            <p>Run <code>npm run dev</code> to start the dev server. You can also try building (<code>npm run build</code>), previewing (<code>npm run preview</code>), and deploying (<code>npm run deploy)</code> your application.</p><p>Have a look at the code below, taken from <code>workers/app.ts</code>. This is the file referenced in the <code>main</code> field in <code>wrangler.jsonc:</code></p>
            <pre><code>const requestHandler = createRequestHandler(
  () =&gt; import("virtual:react-router/server-build"),
  import.meta.env.MODE
);

export default {
  async fetch(request, env, ctx) {
    return requestHandler(request, {
      cloudflare: { env, ctx },
    });
  },
} satisfies ExportedHandler&lt;CloudflareEnvironment&gt;;</code></pre>
            <p>This single file defines your Worker at both dev and build time and puts you in full control. No more build-time adapters! Notice how the <code>env</code> and <code>ctx</code> are passed down directly in the request handler. These are then accessible in your loaders and actions, which are running inside the Workers runtime along with the rest of your server code. You can add other exports to this file to suit your needs and then reference them in your Worker config. Want to add a <a href="https://developers.cloudflare.com/durable-objects/"><u>Durable Object</u></a> or a <a href="https://developers.cloudflare.com/workflows/"><u>Workflow</u></a>? Go for it!</p><p>This will be the first in a series of full-stack frameworks to be supported and we look forward to continuing discussion and collaboration with a range of teams over the coming months. If you are a framework contributor looking to improve integration with Cloudflare and/or the Vite Environment API, then please feel free to explore the <a href="https://github.com/cloudflare/workers-sdk/tree/main/packages/vite-plugin-cloudflare"><u>code</u></a> and reach out on <a href="https://github.com/cloudflare/workers-sdk/issues/new/choose"><u>GitHub</u></a> or <a href="https://discord.com/channels/595317990191398933/1356634720628248780"><u>Discord</u></a>.</p>
    <div>
      <h2>Workers</h2>
      <a href="#workers">
        
      </a>
    </div>
    <p>While this post has focused thus far on using Vite to build web applications, the Cloudflare plugin enables you to use Vite to build anything you can build with Workers. The full Cloudflare Developer Platform is supported, including <a href="https://developers.cloudflare.com/kv/"><u>KV</u></a>, <a href="https://developers.cloudflare.com/d1/"><u>D1</u></a>, <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/"><u>Service Bindings</u></a>, <a href="https://developers.cloudflare.com/workers/runtime-apis/rpc/"><u>RPC</u></a>, <a href="https://developers.cloudflare.com/durable-objects/"><u>Durable Objects</u></a>, <a href="https://developers.cloudflare.com/workflows/"><u>Workflows</u></a>, <a href="https://developers.cloudflare.com/workers-ai/"><u>Workers AI</u></a>, etc. In fact, in most cases, taking an existing Worker and developing it with Vite is as simple as following these two steps:</p><p>Install the dependencies:</p>
            <pre><code>npm install –save-dev vite @cloudflare/vite-plugin</code></pre>
            <p>
And add a Vite config:</p>
            <pre><code>// vite.config.ts

import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
  plugins: [cloudflare()],
});</code></pre>
            <p>That’s it! By default, the plugin will look for a <code>wrangler.json</code>, <code>wrangler.jsonc</code>, or <code>wrangler.toml</code> config file in the root of your Vite project. By using Vite, you can draw on its rich ecosystem of plugins and integrations and easily customize your build output.</p>
    <div>
      <h2>Wrapping up</h2>
      <a href="#wrapping-up">
        
      </a>
    </div>
    <p>In 2024, we <a href="https://blog.cloudflare.com/blazing-fast-development-with-full-stack-frameworks-and-cloudflare"><u>announced</u></a> <code>getPlatformProxy()</code> as a way to access Cloudflare bindings from development servers running in Node. At the end of that post, we <a href="https://blog.cloudflare.com/blazing-fast-development-with-full-stack-frameworks-and-cloudflare/#future-improvements-to-development-workflows-with-vite"><u>imagined a future</u></a> where it would instead be possible to develop directly in the Workers runtime. This would eliminate the many subtle ways that development and production behavior could differ. Today, that future is a reality, and we can’t wait for you to try it out!</p><p>Start a new project with our React Router, React, or Vue templates using the <code>create-cloudflare</code> CLI, use the “Deploy to Cloudflare” button below, or try adding <code>@cloudflare/vite-plugin</code> to your existing Vite applications. We’re excited to see what you build!</p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/staging/vite-react-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p><p>Read more in our <a href="https://developers.cloudflare.com/workers/vite-plugin/"><u>Cloudflare Vite Plugin documentation</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <guid isPermaLink="false">4NaEhSL9wVMXjcPnibDIs1</guid>
            <dc:creator>James Opstad</dc:creator>
            <dc:creator>Dario Piotrowicz</dc:creator>
            <dc:creator>Peter Bacon Darwin</dc:creator>
            <dc:creator>Igor Minar</dc:creator>
        </item>
        <item>
            <title><![CDATA[Blazing fast development with full-stack frameworks and Cloudflare]]></title>
            <link>https://blog.cloudflare.com/blazing-fast-development-with-full-stack-frameworks-and-cloudflare/</link>
            <pubDate>Fri, 05 Apr 2024 13:00:44 GMT</pubDate>
            <description><![CDATA[ You can now use your framework’s development server while accessing D1 databases, R2 object stores, AI models, and more. Iterate locally in milliseconds to build sophisticated web apps that run on Cloudflare ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Hello web developers! <a href="/making-cloudflare-for-web">Last year we released</a> a slew of improvements that made deploying web applications on Cloudflare much easier, and in response we’ve seen a large growth of Astro, Next.js, Nuxt, Qwik, Remix, SolidStart, SvelteKit, and other web apps hosted on Cloudflare. Today we are announcing major improvements to our integration with these web frameworks that makes it easier to develop sophisticated applications that use our <a href="https://www.cloudflare.com/developer-platform/products/d1/">D1 SQL database</a>, <a href="https://developers.cloudflare.com/r2/">R2</a> object store, <a href="https://developers.cloudflare.com/ai/">AI</a> models, and other powerful features of <a href="https://www.cloudflare.com/developer-platform/">Cloudflare’s developer platform.</a></p><p>In the past, if you wanted to develop a web framework-powered application with D1 and run it locally, you’d have to build a production build of your application, and then run it locally using `wrangler pages dev`. While this worked, each of your code iterations would take seconds, or tens of seconds for big applications. Iterating using production builds is simply too slow, pulls you out of the <a href="https://en.wikipedia.org/wiki/Flow_(psychology)">flow</a>, and doesn’t allow you to take advantage of all the DX optimizations that framework authors have put a lot of hard work into. This is changing today!</p><p>Our goal is to integrate with web frameworks in the most natural way possible, without developers having to learn and adopt significant workflow changes or custom APIs when deploying their app to Cloudflare. Whether you are a Next.js developer, a Nuxt developer, or prefer another framework, you can now keep on using the blazing fast local development workflow familiar to you, and ship your application on Cloudflare.</p><p>All full-stack web frameworks come with a local development server (dev server) that is custom tailored to the framework and often provides an excellent development experience, with only one exception — they don't natively support some important features of Cloudflare’s development platform, especially our <a href="https://www.cloudflare.com/developer-platform/products/#storage">storage solutions</a>.</p><p>So up until recently, you had to make a tough choice. You could use the framework-specific dev server to develop your application, but forgo access to many of Cloudflare’s features. Alternatively, you could take full advantage of Cloudflare’s platform including various resources like D1 or R2<a href="https://developers.cloudflare.com/workers/configuration/bindings/">, but you would have to give up using the framework specific developer tooling. In that case, your iteration cycle would slow down, and it would take seconds rather than milliseconds for you to see results of your code changes in the browser.</a> But not anymore! Let’s take a look.</p>
    <div>
      <h3>Let’s build an application</h3>
      <a href="#lets-build-an-application">
        
      </a>
    </div>
    <p>Let’s create a new application using <a href="https://developers.cloudflare.com/pages/get-started/c3/">C3</a> — our create-cloudflare CLI. We could use any npm client of our choice (pnpm anyone?!?), but to keep things simple in this post, we’ll stick with the default npm client. To get started, just run:</p><p><code>$ npm create cloudflare@latest</code></p><p>Provide a name for your app, or stick with the randomly generated one. Then select the “Website or web app” category, and pick a full-stack framework of your choice. We support many: <a href="https://astro.build/">Astro</a>, <a href="https://nextjs.org/">Next.js</a>, <a href="https://nuxt.com/">Nuxt</a>, <a href="https://qwik.dev/">Qwik</a>, <a href="https://remix.run/">Remix</a>, <a href="https://start.solidjs.com/getting-started/what-is-solidstart">SolidStart</a>, and <a href="https://kit.svelte.dev/">SvelteKit</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/pa9natISsY3icyIUpu9Ul/cd6aa0baebe85d1b0a85eaba684d66e6/Screenshot-2024-03-28-at-7.25.10-AM.png" />
            
            </figure><p>Since C3 delegates the application scaffolding to the latest version of the framework-specific CLI, you will scaffold the application exactly as the framework authors intended without missing out on any of the framework features or options. C3 then adds to your application everything necessary for integrating and deploying to Cloudflare so that you don’t have to configure it yourself.</p><p>With our application scaffolded, let’s get it to display a list of products stored in a database with just a few steps. First, we add the configuration for our database to our <a href="https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases">wrangler.toml</a> config file:</p>
            <pre><code>[[d1_databases]]
binding = "DB"
database_name = "blog-products-db"
database_id = "XXXXXXXXXXXXXXXX"</code></pre>
            <p>Yes, that’s right! You can now configure your <a href="https://developers.cloudflare.com/workers/configuration/bindings/">bound resources</a> via the <a href="https://developers.cloudflare.com/pages/functions/wrangler-configuration/">wrangler.toml file</a>, even for full-stack apps deployed to Pages. We’ll share much more about configuration enhancements to Pages in a <a href="/browser-rendering-api-ga-rolling-out-cloudflare-snippets-swr-and-bringing-workers-for-platforms-to-our-paygo-plans/">dedicated announcement</a>.</p><p>Now let’s create a simple schema.sql file representing our database schema:</p>
            <pre><code>CREATE TABLE products(product_id INTEGER PRIMARY KEY, name TEXT, price INTEGER);
INSERT INTO products (product_id, name, price) VALUES (1, 'Apple', 250), (2, 'Banana', 100), (3, 'Cherry', 375);</code></pre>
            <p>And initialize our database:</p><p><code>$ npx wrangler d1 execute blog-products-db --local --file schema.sql</code></p><p>Notice that we used the <code>–local</code> flag of <a href="https://developers.cloudflare.com/workers/wrangler/commands/#execute"><code>wrangler d1 execute</code></a> to apply the changes to our local D1 database. This is the database that our dev server will connect to.</p><p>Next, if you use TypeScript, let TypeScript know about your database by running:</p><p><code>$ npm run build-cf-types</code></p><p>This command is preconfigured for all full-stack applications created via C3 and executes <a href="https://developers.cloudflare.com/workers/wrangler/commands/#types"><code>wrangler types</code></a> to update the interface of Cloudflare’s environment containing all configured bindings.</p><p>We can now start the dev server provided by your framework via a handy shortcut:</p><p><code>$ npm run dev</code></p><p>This shortcut will start your framework’s dev server, whether it’s powered by <a href="https://nextjs.org/docs/app/api-reference/next-cli#development">next dev</a>, <a href="https://nitro.unjs.io/">nitro</a>, or <a href="https://vitejs.dev/">vite</a>.</p><p>Now to access our database and list the products, we can now use a framework specific approach. For example, in a Next.js application that uses the App router, we could update <code>app/api/hello/route.ts</code> with the following:</p>
            <pre><code>const db = getRequestContext().env.DB;
 const productsResults = await db.prepare('SELECT * FROM products').all();
 return Response.json(productsResults.results);</code></pre>
            <p>Or in a Nuxt application, we can create a <code>server/api/hello.ts</code> file and populate it with:</p>
            <pre><code>export default defineEventHandler(async ({ context }) =&gt; {
   const db = context.cloudflare.env.DB;
   const productsResults = await db.prepare('SELECT * FROM products').all();
   return productsResults.results;
 });</code></pre>
            <p>Assuming that the framework dev server is running on port 3000, you can test the new API route in either framework by navigating to <a href="http://localhost:3000/api/hello">http://localhost:3000/api/hello</a>. For simplicity, we picked API routes in these examples, but the same applies to any UI-generating routes as well.</p><p>Each web framework has its own way to define routes and pass contextual information about the request throughout the application, so how you access your databases, object stores, and other resources will depend on your framework. You can read our updated full-stack framework guides to learn more:</p><ul><li><p><a href="https://developers.cloudflare.com/pages/framework-guides/deploy-an-astro-site/">Astro guide</a></p></li><li><p><a href="https://developers.cloudflare.com/pages/framework-guides/nextjs/deploy-a-nextjs-site/">Next.js guide</a></p></li><li><p><a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-nuxt-site/">Nuxt guide</a></p></li><li><p><a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-qwik-site/">Qwik guide</a></p></li><li><p><a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-remix-site/">Remix guide</a></p></li><li><p><a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-solid-site/">SolidStart guide</a></p></li><li><p><a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site/">SvelteKit guide</a></p></li></ul><p>Now that you know how to access Cloudflare’s resources in the framework of your choice, everything else you know about your framework remains the same. You can now develop your application locally, using the development server optimized for your framework, which often includes support for hot module replacement (HMR), custom dev tools, enhanced debugging support and more, all while still benefiting from Cloudflare-specific APIs and features. Win-win!</p>
    <div>
      <h3>What has actually changed to enable these development workflows?</h3>
      <a href="#what-has-actually-changed-to-enable-these-development-workflows">
        
      </a>
    </div>
    <p>To decrease the development latency and preserve the custom framework-specific experiences, we needed to enable web frameworks and their dev servers to integrate with wrangler and miniflare in a seamless, almost invisible way.</p><p><a href="https://miniflare.dev/">Miniflare</a> is a key component in this puzzle. It is our local simulator for Cloudflare-specific resources, which is powered by <a href="https://github.com/cloudflare/workerd">workerd</a>, our JavaScript (JS) runtime. By relying on workerd, we ensure that Cloudflare’s JavaScript APIs run locally in a way that faithfully simulates our production environment. The trouble is that framework dev servers already rely on Node.js to run the application, so bringing another JS runtime into the mix breaks many assumptions in how these dev servers have been architected.</p><p>Our team however came up with an interesting approach to bridging the gap between these two JS runtimes. We call it the <a href="https://developers.cloudflare.com/workers/wrangler/api/#getplatformproxy">getPlatformProxy()</a> API, which is now part of wrangler and is super-powered by <a href="https://github.com/cloudflare/miniflare/pull/639">miniflare’s magic proxy</a>. This API exposes a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy">JS proxy object</a> that behaves just like the usual <a href="https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#parameters">Workers env object</a> containing all bound resources. The proxy object enables code from Node.js to transparently invoke JavaScript code running in workerd, as well access Cloudflare-specific runtime APIs.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1MPXvdyK3eYsY3YKnOC8co/480cdd00d7a921ecb65eb738caf17ff2/pasted-image-0--6-.png" />
            
            </figure><p>With this bridge between the Node.js and workerd runtimes, your application can now access Cloudflare simulators for D1, R2, KV and other storage solutions directly while running in a dev server powered by Node.js. Or you could even write an Node.js script to do the same:</p>
            <pre><code> import {getPlatformProxy} from 'wrangler';


 const {env} = getPlatformProxy();
 console.dir(env);
 const db = env.DB;


 // Now let’s execute a DB query that runs in a local D1 db
 // powered by miniflare/workerd and access the result from Node.js
 const productsResults = await db.prepare('SELECT * FROM products').all();
 console.log(productsResults.results);</code></pre>
            <p>With the <code>getPlatformProxy()</code> API available, the remaining work was all about updating all framework adapters, plugins, and in some cases frameworks themselves to make use of this API. We are grateful for the support we received from framework teams on this journey, especially <a href="https://github.com/alexanderniebuhr">Alex</a> from Astro, <a href="https://github.com/pi0">pi0</a> from Nuxt, <a href="https://github.com/pcattori">Pedro</a> from Remix, <a href="https://github.com/ryansolid">Ryan</a> from Solid, <a href="https://github.com/benmccann">Ben</a> and <a href="https://github.com/Rich-Harris">Rich</a> from Svelte, and our collaborator on the <a href="https://github.com/cloudflare/next-on-pages">next-on-pages</a> project, <a href="https://github.com/james-elicx">James Anderson</a>.</p>
    <div>
      <h3>Future improvements to development workflows with Vite</h3>
      <a href="#future-improvements-to-development-workflows-with-vite">
        
      </a>
    </div>
    <p>While the <a href="https://developers.cloudflare.com/workers/wrangler/api/#getplatformproxy"><code>getPlatformProxy()</code></a> API is a good solution for many scenarios, we can do better. If we could run the entire application in our JS runtime rather than Node.js, we could even more faithfully simulate the production environment and reduce developer friction and production surprises.</p><p>In the ideal world, we’d like you to develop against the same runtime that you deploy to in production, and this can only be achieved by integrating workerd directly into the dev servers of all frameworks, which is not a small feat considering the number of frameworks out there and the differences between them.</p><p>We however got a bit lucky. As we kicked off this effort, we quickly realized that <a href="https://vitejs.dev/">Vite</a>, a popular dev server used by many full-stack frameworks, was gaining increasingly greater adoption. In fact, Remix switched over to <a href="https://remix.run/blog/remix-vite-stable">Vite just recently</a> and confirmed the popularity of Vite as the common foundation for web development today.</p><p>If Vite had first-class support for running a full-stack application in an alternative JavaScript runtime, we could enable anyone using Vite to develop their applications locally with complete access to the Cloudflare developer platform. No more framework specific custom integrations and workarounds — all the features of a full-stack framework, Vite, and Cloudflare accessible to all developers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3hj4Tb9Ex2NnwrnqZ5z6M2/e049e4300b188381259f60bc69ad054c/pasted-image-0--7-.png" />
            
            </figure><p>Sounds too good to be true? Maybe. We are very stoked to be working with the Vite team on the <a href="https://github.com/vitejs/vite/pull/16089">Vite environments</a> proposal, which could enable just that. This proposal is still evolving, so stay tuned for updates.</p>
    <div>
      <h3>What will you build today?</h3>
      <a href="#what-will-you-build-today">
        
      </a>
    </div>
    <p>We aim to make Cloudflare the best development platform for web developers. Making it quick and easy to develop your application with frameworks and tools you are already familiar with is a big part of our story. Start your journey with us by running a single command:</p><p><code>$ npm create cloudflare@latest</code></p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Full Stack]]></category>
            <category><![CDATA[Wrangler]]></category>
            <category><![CDATA[Miniflare]]></category>
            <guid isPermaLink="false">14PqvEOUknz9TI2FQGbZT3</guid>
            <dc:creator>Igor Minar</dc:creator>
            <dc:creator>Dario Piotrowicz</dc:creator>
            <dc:creator>James Culveyhouse</dc:creator>
            <dc:creator>Peter Bacon Darwin</dc:creator>
        </item>
        <item>
            <title><![CDATA[Incremental adoption of micro-frontends with Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/fragment-piercing/</link>
            <pubDate>Thu, 17 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ With Cloudflare Workers, our fragment-based micro-frontend architecture, and fragment piercing technique, engineering teams can incrementally improve large frontends in a fraction of the time, yielding significant user and developer experience gains. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Q4iw5pvW4frJry6aDHbW1/72ed68595be7d127566b976c0a4114a6/image5-11.png" />
            
            </figure>
    <div>
      <h2>Bring micro-frontend benefits to legacy Web applications</h2>
      <a href="#bring-micro-frontend-benefits-to-legacy-web-applications">
        
      </a>
    </div>
    <p>Recently, we wrote about <a href="/better-micro-frontends/">a new fragment architecture</a> for building Web applications that is fast, cost-effective, and scales to the largest projects, while enabling a fast iteration cycle. The approach uses multiple collaborating Cloudflare Workers to render and stream micro-frontends into an application that is interactive faster than traditional client-side approaches, leading to better user experience and SEO scores.</p><p>This approach is great if you are starting a new project or have the capacity to rewrite your current application from scratch. But in reality most projects are too large to be rebuilt from scratch and can adopt architectural changes only in an incremental way.</p><p>In this post we propose a way to replace only selected parts of a legacy client-side rendered application with server-side rendered fragments. The result is an application where the most important views are interactive sooner, can be developed independently, and receive all the benefits of the micro-frontend approach, while avoiding large rewrites of the legacy codebase. This approach is framework-agnostic; in this post we demonstrate fragments built with React, Qwik, and SolidJS.</p>
    <div>
      <h2>The pain of large frontend applications</h2>
      <a href="#the-pain-of-large-frontend-applications">
        
      </a>
    </div>
    <p>Many large frontend applications developed today fail to deliver good user experience. This is often caused by architectures that require large amounts of JavaScript to be downloaded, parsed and executed before users can interact with the application. Despite efforts to defer non-critical JavaScript code via lazy loading, and the use of server-side rendering, these large applications still take too long to become interactive and respond to the user's inputs.</p><p>Furthermore, large monolithic applications can be complex to build and deploy. Multiple teams may be collaborating on a single codebase and the effort to coordinate testing and deployment of the project makes it hard to develop, deploy and iterate on individual features.</p><p>As outlined in our <a href="/better-micro-frontends/">previous post</a>, micro-frontends powered by <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> can solve these problems but converting an application monolith to a micro-frontend architecture can be difficult and expensive. It can take months, or even years, of engineering time before any benefits are perceived by users or developers.</p><p>What we need is an approach where a project can incrementally adopt micro-frontends into the most impactful parts of the application incrementally, without needing to rewrite the whole application in one go.</p>
    <div>
      <h2>Fragments to the rescue</h2>
      <a href="#fragments-to-the-rescue">
        
      </a>
    </div>
    <p>The goal of a fragment based architecture is to significantly <a href="https://www.cloudflare.com/solutions/ecommerce/optimization/">decrease loading and interaction latency</a> for large web applications (as measured via <a href="https://web.dev/vitals/">Core Web Vitals</a>) by breaking the application into micro-frontends that can be quickly rendered (and cached) in Cloudflare Workers. The challenge is how to integrate a micro-frontend fragment into a legacy client-side rendered application with minimal cost to the original project.</p><p>The technique we propose allows us to convert the most valuable parts of a legacy application’s UI, in isolation from the rest of the application.</p><p>It turns out that, in many applications, the most valuable parts of the UI are often nested within an application “shell” that provides header, footer, and navigational elements. Examples of these include a login form, product details panel in an <a href="https://www.cloudflare.com/ecommerce/">e-commerce application</a>, the inbox in an email client, etc.</p><p>Let’s take a login form as an example. If it takes our application several seconds to display the login form, the users will dread logging in, and we might lose them. We can however convert the login form into a server-side rendered fragment, which is displayed and interactive immediately, while the rest of the legacy application boots up in the background. Since the fragment is interactive early, the user can even submit their credentials before the legacy application has started and rendered the rest of the page.</p><div></div>
<p><small>Animation showing the login form being available before the main application</small></p><p>This approach enables engineering teams to deliver valuable improvements to users in just a fraction of the time and engineering cost compared to traditional approaches, which either sacrifice user experience improvements, or require a lengthy and high-risk rewrite of the entire application. It allows teams with monolithic single-page applications to adopt a micro-frontend architecture incrementally, target the improvements to the most valuable parts of the application, and therefore front-load the return on investment.</p><p>An interesting challenge in extracting parts of the UI into server-side rendered fragments is that, once displayed in the browser, we want the legacy application and the fragments to feel like a single application. The fragments should be neatly embedded within the legacy application shell, keeping the application accessible by correctly forming the DOM hierarchy, but we also want the server-side rendered fragments to be displayed and become interactive as quickly as possible — even before the legacy client-side rendered application shell comes into existence. How can we embed UI fragments into an application shell that doesn’t exist yet? We resolved this problem via a technique we devised, which we call “fragment piercing”.</p>
    <div>
      <h2>Fragment piercing</h2>
      <a href="#fragment-piercing">
        
      </a>
    </div>
    <p>Fragment piercing combines HTML/DOM produced by server-side rendered micro-frontend fragments with HTML/DOM produced by a legacy client-side rendered application.</p><p>The micro-frontend fragments are rendered directly into the top level of the HTML response, and are designed to become immediately interactive. In the background, the legacy application is client-side rendered as a sibling of these fragments. When it is ready, the fragments are “pierced” into the legacy application – the DOM of each fragment is moved to its appropriate place within the DOM of the legacy application – without causing any visual side effects, or loss of client-side state, such as focus, form data, or text selection. Once “pierced”, a fragment can begin to communicate with the legacy application, effectively becoming an integrated part of it.</p><p>Here, you can see a “login” fragment and the empty legacy application “root” element at the top level of the DOM, before piercing.</p>
            <pre><code>&lt;body&gt;
  &lt;div id="root"&gt;&lt;/div&gt;
  &lt;piercing-fragment-host fragment-id="login"&gt;
    &lt;login q:container...&gt;...&lt;/login&gt;
  &lt;/piercing-fragment-host&gt;
&lt;/body&gt;</code></pre>
            <p>And here you can see that the fragment has been pierced into the “login-page” div in the rendered legacy application.</p>
            <pre><code>&lt;body&gt;
  &lt;div id="root"&gt;
    &lt;header&gt;...&lt;/header&gt;
    &lt;main&gt;
      &lt;div class="login-page"&gt;
        &lt;piercing-fragment-outlet fragment-id="login"&gt;
          &lt;piercing-fragment-host fragment-id="login"&gt;
            &lt;login  q:container...&gt;...&lt;/login&gt;
          &lt;/piercing-fragment-host&gt;
        &lt;/piercing-fragment-outlet&gt;
      &lt;/div&gt;
    &lt;/main&gt;
    &lt;footer&gt;...&lt;/footer&gt;
  &lt;/div&gt;
&lt;/body&gt;</code></pre>
            <p>To keep the fragment from moving and causing a visible layout shift during this transition, we apply CSS styles that position the fragment in the same way before and after piercing.</p><p>At any time an application can be displaying any number of pierced fragments, or none at all. This technique is not limited only to the initial load of the legacy application. Fragments can also be added to and removed from an application, at any time. This allows fragments to be rendered in response to user interactions and client-side routing.</p><p>With fragment piercing, you can start to incrementally adopt micro-frontends, one fragment at a time. You decide on the granularity of fragments, and which parts of the application to turn into fragments. The fragments don’t all have to use the same Web framework, which can be useful when switching stacks, or during a post-acquisition integration of multiple applications.</p>
    <div>
      <h2>The “Productivity Suite” demo</h2>
      <a href="#the-productivity-suite-demo">
        
      </a>
    </div>
    <p>As a demonstration of fragment piercing and incremental adoption we have developed a <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite">“productivity suite” demo</a> application that allows users to manage to-do lists, read hacker news, etc. We implemented the shell of this application as a client-side rendered React application — a common tech choice in corporate applications. This is our “legacy application”. There are three routes in the application that have been updated to use micro-frontend fragments:</p><ul><li><p><code>/login</code> - a simple dummy login form with client-side validation, displayed when users are not authenticated (implemented in <a href="https://qwik.builder.io/">Qwik</a>).</p></li><li><p><code>/todos</code> - manages one or more todo lists, implemented as two collaborating fragments:</p><ul><li><p>Todo list selector - a component for selecting/creating/deleting Todo lists (implemented in <a href="https://qwik.builder.io/">Qwik</a>).</p></li><li><p>Todo list editor - a clone of the <a href="https://todomvc.com/">TodoMVC</a> app (implemented in <a href="https://reactjs.org/docs/react-dom-server.html">React</a>).</p></li></ul></li><li><p><code>/news</code> - a clone of the <a href="https://github.com/solidjs/solid-hackernews">HackerNews</a> demo (implemented in <a href="https://www.solidjs.com/">SolidJS</a>).</p></li></ul><p>This demo showcases that different independent technologies can be used for both the legacy application and for each of the fragments.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/38jTYqRteZyGozPUoqXd8D/60b003aa2b53395b4adcb0cf31dcd2fc/image2-41.png" />
            
            </figure><p>A visualization of the fragments that are pierced into the legacy application</p><p>The application is deployed at <a href="https://productivity-suite.web-experiments.workers.dev/">https://productivity-suite.web-experiments.workers.dev/</a>.</p><p>To try it out, you first need to log in – simply use any username you like (no password needed). The user’s data is saved in a cookie, so you can log out and back in using the same username. After you’ve logged in, navigate through the various pages using the navigation bar at the top of the application. In particular, take a look at the “<a href="https://productivity-suite.web-experiments.workers.dev/todos">Todo Lists</a>” and “<a href="https://productivity-suite.web-experiments.workers.dev/news">News</a>” pages to see the piercing in action.</p><p>At any point, try reloading the page to see that fragments are rendered instantly while the legacy application loads slowly in the background. Try interacting with the fragments even before the legacy application has appeared!</p><p>At the very top of the page there are controls to let you see the impact of fragment piercing in action.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/11Y8DDksAEPz1AMQT8jjSG/8c69e3cc1d99b8a67a01410b146f2c02/image1-56.png" />
            
            </figure><ul><li><p>Use the “Legacy app bootstrap delay” slider to set the simulated delay before the legacy application starts.</p></li><li><p>Toggle “Piercing Enabled” to see what the user experience would be if the app did not use fragments.</p></li><li><p>Toggle “Show Seams” to see where each fragment is on the current page.</p></li></ul>
    <div>
      <h2>How it works</h2>
      <a href="#how-it-works">
        
      </a>
    </div>
    <p>The application is composed of a number of building blocks.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/47B8E1C6o3kWhsEzUWYL7J/ca1e6348128985d560bd28f0dc32615f/Frame-653.png" />
            
            </figure><p>An overview of the collaborating Workers and legacy application host</p><p>The <b>Legacy application host</b> in our demo serves the files that define the client-side React application (HTML, JavaScript and stylesheets). Applications built with other tech stacks would work just as well. The <b>Fragment Workers</b> host the micro-frontend fragments, as described in our previous <a href="/better-micro-frontends/">fragment architecture</a> post. And the <b>Gateway Worker</b> handles requests from the browser, selecting, fetching and combining response streams from the legacy application and micro-frontend fragments.</p><p>Once these pieces are all deployed, they work together to handle each request from the browser. Let’s look at what happens when you go to the `/login` route.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2atnhkVHu0tNa1MKGWnOPs/b80838e713fd7b64a177a82b06377a16/image4-22.png" />
            
            </figure><p>The flow of requests when viewing the login page</p><p>The user navigates to the application and the browser makes a request to the Gateway Worker to get the initial HTML. The Gateway Worker identifies that the browser is requesting the login page. It then makes two parallel sub-requests – one to fetch the index.html of the legacy application, and another to request the server-side rendered login fragment. It then combines these two responses into a single response stream containing the HTML that is delivered to the browser.</p><p>The browser displays the HTML response containing the empty root element for the legacy application, and the server-side rendered login fragment, which is immediately interactive for the user.</p><p>The browser then requests the legacy application’s JavaScript. This request is proxied by the Gateway Worker to the Legacy application host. Similarly, any other assets for the legacy application or fragments get routed through the Gateway Worker to the legacy application host or appropriate Fragment Worker.</p><p>Once the legacy application’s JavaScript has been downloaded and executed, rendering the shell of the application in the process, the fragment piercing kicks in, moving the fragment into the appropriate place in the legacy application, while preserving all of its UI state.</p><p>While focussed on the login fragment to explain fragment piercing, the same ideas apply to the other fragments implemented in the <code>/todos</code> and <code>/news</code> routes.</p>
    <div>
      <h2>The piercing library</h2>
      <a href="#the-piercing-library">
        
      </a>
    </div>
    <p>Despite being implemented using different Web frameworks, all the fragments are integrated into the legacy application in the same way using helpers from a “<a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite/piercing-library">Piercing Library</a>”. This library is a collection of server-side and client-side utilities that we developed, for the demo, to handle integrating the legacy application with micro-frontend fragments. The main features of the library are the <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-gateway.ts#L82"><code>PiercingGateway</code></a> class, <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-host/piercing-fragment-host.ts#L5">fragment host</a> and <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-outlet.ts#L31">fragment outlet</a> custom elements, and the <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/message-bus/message-bus.ts#L18"><code>MessageBus</code></a> class.</p>
    <div>
      <h3>PiercingGateway</h3>
      <a href="#piercinggateway">
        
      </a>
    </div>
    <p>The <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-gateway.ts#L82"><code>PiercingGateway</code></a> class can be used to instantiate a Gateway Worker that handles all requests for our application’s HTML, JavaScript and other assets. The `PiercingGateway` routes requests through to the appropriate Fragment Workers or to the host of the Legacy Application. It also combines the HTML response streams from these fragments with the response from the legacy application into a single HTML stream that is returned to the browser.</p><p>Implementing a Gateway Worker is straightforward using the Piercing Library. Create a new <code>gateway</code> instance of <code>PiercingGateway</code>, passing it the URL to the legacy application host and a function to determine whether piercing is enabled for the given request. Export the <code>gateway</code> as the default export from the Worker script so that the Workers runtime can wire up its <code>fetch()</code> handler.</p>
            <pre><code>const gateway = new PiercingGateway&lt;Env&gt;({
  // Configure the origin URL for the legacy application.
  getLegacyAppBaseUrl: (env) =&gt; env.APP_BASE_URL,
  shouldPiercingBeEnabled: (request) =&gt; ...,
});
...

export default gateway;</code></pre>
            <p>Fragments can be registered by calling the <code>registerFragment()</code> method so that the <code>gateway</code> can automatically route requests for a fragment’s HTML and assets to its Fragment Worker. For example, registering the login fragment would look like:</p>
            <pre><code>gateway.registerFragment({
  fragmentId: "login",
  prePiercingStyles: "...",
  shouldBeIncluded: async (request) =&gt; !(await isUserAuthenticated(request)),
});</code></pre>
            
    <div>
      <h3>Fragment host and outlet</h3>
      <a href="#fragment-host-and-outlet">
        
      </a>
    </div>
    <p>Routing requests and combining HTML responses in the Gateway Worker is only half of what makes piercing possible. The other half needs to happen in the browser where the fragments need to be pierced into the legacy application using the technique we described earlier.</p><p>The fragment piercing in the browser is facilitated by a pair of <a href="https://html.spec.whatwg.org/multipage/custom-elements.html">custom elements</a>, the fragment host (<a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-host/piercing-fragment-host.ts#L5"><code>&lt;piercing-fragment-host&gt;</code></a>) and the fragment outlet (<a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/piercing-fragment-outlet.ts#L31"><code>&lt;piercing-fragment-outlet&gt;</code></a>).</p><p>The Gateway Worker wraps the HTML for each fragment in a fragment host. In the browser, the fragment host manages the life-time of the fragment and is used when moving the fragment’s DOM into position in the legacy application.</p>
            <pre><code>&lt;piercing-fragment-host fragment-id="login"&gt;
  &lt;login q:container...&gt;...&lt;/login&gt;
&lt;/piercing-fragment-host&gt;</code></pre>
            <p>In the legacy application, the developer marks where a fragment should appear when it is pierced by adding a fragment outlet. Our demo application’s Login route looks as follows:</p>
            <pre><code>export function Login() {
  …
  return (
    &lt;div className="login-page" ref={ref}&gt;
      &lt;piercing-fragment-outlet fragment-id="login" /&gt;
    &lt;/div&gt;
  );
}</code></pre>
            <p>When a fragment outlet is added to the DOM, it searches the current document for its associated fragment host. If found, the fragment host and its contents are moved inside the outlet. If the fragment host is not found, the outlet will make a request to the gateway worker to fetch the fragment HTML, which is then streamed directly into the fragment outlet, using the <a href="https://github.com/marko-js/writable-dom">writable-dom library</a> (a small but powerful library developed by the <a href="https://markojs.com/">MarkoJS</a> team).</p><p>This fallback mechanism enables client-side navigation to routes that contain new fragments. This way fragments can be rendered in the browser via both initial (hard) navigation and client-side (soft) navigation.</p>
    <div>
      <h3>Message bus</h3>
      <a href="#message-bus">
        
      </a>
    </div>
    <p>Unless the fragments in our application are completely presentational or self-contained, they also need to communicate with the legacy application and other fragments. The <code>[MessageBus](https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/piercing-library/src/message-bus/message-bus.ts#L18)</code> is a simple asynchronous, isomorphic, and framework-agnostic communication bus that the legacy application and each of the fragments can access.</p><p>In our demo application the login fragment needs to inform the legacy application when the user has authenticated. This <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/fragments/login/src/components/LoginForm.tsx#L51-L57">message dispatch</a> is implemented in the Qwik <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/fragments/login/src/components/LoginForm.tsx#L38"><code>LoginForm</code></a> component as follows:</p>
            <pre><code>const dispatchLoginEvent = $(() =&gt; {
  getBus(ref.value).dispatch("login", {
    username: state.username,
    password: state.password,
  });
  state.loading = true;
});</code></pre>
            <p>The legacy application can then <a href="https://github.com/cloudflare/workers-web-experiments/blob/df50b60cfff7bc299cf70ecfe8f7826ec9313b84/productivity-suite/app/legacy-app/src/auth.tsx#L24-L34">listen for these messages</a> like this:</p>
            <pre><code>useEffect(() =&gt; {
  return getBus().listen&lt;LoginMessage&gt;("login", async (user) =&gt; {
    setUser(user);
    await addUserDataIfMissing(user.username);
    await saveCurrentUser(user.username);
    getBus().dispatch("authentication", user);
    navigate("/", { replace: true, });
  });
}, []);</code></pre>
            <p>We settled on this message bus implementation because we needed a solution that was framework-agnostic, and worked well on both the server as well as client.</p>
    <div>
      <h2>Give it a go!</h2>
      <a href="#give-it-a-go">
        
      </a>
    </div>
    <p>With fragments, fragment piercing, and Cloudflare Workers, you can improve performance as well as the development cycle of legacy client-side rendered applications. These changes can be adopted incrementally, and you can even do so while implementing fragments with a Web framework for your choice.</p><p>The “Productivity Suite” application demonstrating these capabilities can be found at <a href="https://productivity-suite.web-experiments.workers.dev/">https://productivity-suite.web-experiments.workers.dev/</a>.</p><p>All the code we have shown is open-source and published to Github: <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite">https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite</a>.</p><p>Feel free to clone the repository. It is easy to run locally and even deploy your own version (for free) to Cloudflare. We tried to make the code as reusable as possible. Most of the core logic is in the <a href="https://github.com/cloudflare/workers-web-experiments/tree/main/productivity-suite/piercing-library">piercing library</a> that you could try in your own projects. We’d be thrilled to receive feedback, suggestions, or hear about applications you’d like to use it for. Join our <a href="https://github.com/cloudflare/workers-web-experiments/discussions/64">GitHub discussion</a> or also reach us on our <a href="https://discord.com/channels/595317990191398933/1041751020340002907">discord channel</a>.</p><p>We believe that combining Cloudflare Workers with the latest ideas from frameworks will drive the next big steps forward in improved experiences for both users and developers in Web applications. Expect to see more demos, blog posts and collaborations as we continue to push the boundaries of what the Web can offer. And if you’d also like to be directly part of this journey, we are also happy to share that <a href="https://boards.greenhouse.io/cloudflare/jobs/4619341">we are hiring</a>!</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Micro-frontends]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">4vnFkyih2W2DD0QcaALcdf</guid>
            <dc:creator>Peter Bacon Darwin</dc:creator>
            <dc:creator>Dario Piotrowicz</dc:creator>
            <dc:creator>James Culveyhouse</dc:creator>
            <dc:creator>Igor Minar</dc:creator>
        </item>
    </channel>
</rss>