
<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>Tue, 14 Apr 2026 10:04:00 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Your frontend, backend, and database — now in one Cloudflare Worker]]></title>
            <link>https://blog.cloudflare.com/full-stack-development-on-cloudflare-workers/</link>
            <pubDate>Tue, 08 Apr 2025 14:05:00 GMT</pubDate>
            <description><![CDATA[ You can now deploy static sites and full-stack applications on Cloudflare Workers. Framework support for React Router v7, Astro, Vue and more are generally available today and Cloudflare Vite plugin.  ]]></description>
            <content:encoded><![CDATA[ <p><a href="https://blog.cloudflare.com/builder-day-2024-announcements/#static-asset-hosting"><u>In September 2024</u></a>, we introduced beta support for <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">hosting</a>, storing, and serving <a href="https://developers.cloudflare.com/workers/static-assets/"><u>static assets</u></a> for free on <a href="https://www.cloudflare.com/developer-platform/products/workers/">Cloudflare Workers</a> — something that was previously only possible on <a href="https://blog.cloudflare.com/cloudflare-pages/"><u>Cloudflare Pages</u></a>. Being able to host these assets — your client-side JavaScript, HTML, CSS, fonts, and images — was a critical missing piece for developers looking to build a full-stack application within a <b>single Worker</b>. </p><p>Today we’re announcing ten big improvements to building apps on Cloudflare. All together, these new additions allow you to build and host projects ranging from simple static sites to full-stack applications, all on Cloudflare Workers:</p><ul><li><p>Cloudflare Workers now provides production ready, <b>generally available</b> (GA) support for <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/remix/"><u>React Router v7 (Remix)</u></a>, <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/astro/"><u>Astro</u></a>, <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/hono/"><u>Hono</u></a>, <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/vue/"><u>Vue.js</u></a>, <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/nuxt/"><u>Nuxt</u></a>, <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/svelte/"><u>Svelte (SvelteKit)</u></a>, and <a href="https://developers.cloudflare.com/workers/frameworks/"><u>more</u></a>, with GA support for more frameworks including <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/nextjs/"><u>Next.js</u></a>, <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/angular/"><u>Angular</u></a>, and <a href="https://developers.cloudflare.com/workers/frameworks/framework-guides/solid/"><u>SolidJS</u></a> (SolidStart) to follow in Q2 2025. </p></li><li><p>You can build complete full-stack apps on Workers without a framework: you can “<a href="https://blog.cloudflare.com/introducing-the-cloudflare-vite-plugin/"><u>just use Vite</u></a>" and React together, and build a backend API in the same Worker. See our <a href="https://github.com/cloudflare/templates/tree/staging/vite-react-template"><u>Vite + React template</u></a> for an example.</p></li><li><p>The adapter for Next.js — <a href="https://opennext.js.org/cloudflare"><u>@opennextjs/cloudflare</u></a>, introduced in September 2024 as an early alpha, <a href="https://blog.cloudflare.com/deploying-nextjs-apps-to-cloudflare-workers-with-the-opennext-adapter"><u>is now v1.0-beta</u></a>, and will be GA in the coming weeks. Those using the OpenNext adapter will also be able to easily upgrade to the <a href="https://github.com/vercel/next.js/discussions/77740"><u>recently announced Next.js Deployments API</u></a>. </p></li><li><p>The <a href="https://blog.cloudflare.com/introducing-the-cloudflare-vite-plugin"><u>Cloudflare Vite plugin</u></a> is now v1.0 and generally available. The Vite plugin allows you to run Vite’s development server in the Workers runtime (<code>workerd</code>), meaning you get all the benefits of Vite, including <a href="https://vite.dev/guide/features.html#hot-module-replacement"><u>Hot Module Replacement</u></a>, while still being able to use features that are exclusive to Workers (like Durable Objects).</p></li><li><p>You can now use static <a href="https://developers.cloudflare.com/workers/static-assets/headers/"><u>_headers</u></a> and <a href="https://developers.cloudflare.com/workers/static-assets/redirects/"><u>_redirects</u></a> configuration files for your applications on Workers, something that was previously only available on Pages. These files allow you to add simple headers and configure redirects without executing any Worker code. </p></li><li><p>In addition to <a href="https://developers.cloudflare.com/hyperdrive/configuration/connect-to-postgres/"><u>PostgreSQL</u></a>, you can now connect to <a href="https://blog.cloudflare.com/building-global-mysql-apps-with-cloudflare-workers-and-hyperdrive"><u>MySQL databases in addition from Cloudflare Workers, via Hyperdrive</u></a>. Bring your existing Planetscale, AWS, GCP, Azure, or other MySQL database, and Hyperdrive will take care of pooling connections to your database and eliminating unnecessary roundtrips by caching queries.</p></li><li><p><a href="#node-js-compatibility"><u>More Node.js APIs are available</u></a> in the Workers Runtime — including APIs from the <code>crypto</code>, <code>tls</code>, <code>net</code>, and <code>dns </code>modules. We’ve also increased the maximum CPU time for a Workers request from 30 seconds to 5 minutes.</p></li><li><p>You can now <a href="https://blog.cloudflare.com/deploy-workers-applications-in-seconds"><u>bring any repository from GitHub or GitLab that contains a Worker application</u></a>, and <a href="https://developers.cloudflare.com/workers/ci-cd/builds/"><u>Workers Builds</u></a> will take care of deploying the app as a new Worker on your account. <a href="#workers-builds"><u>Workers Builds is also starting much more quickly</u></a> (by up to 6 seconds for every build). </p></li><li><p>You can now set up Workers Builds to <a href="https://developers.cloudflare.com/workers/ci-cd/builds/build-branches/#configure-non-production-branch-builds"><u>run on non-production branches</u></a>, and preview URLs will be <a href="https://developers.cloudflare.com/workers/ci-cd/builds/git-integration/github-integration/#pull-request-comment"><u>posted back to GitHub as a comment</u></a>. </p></li><li><p>The <a href="https://developers.cloudflare.com/images/transform-images/bindings/"><u>Images binding in Workers</u></a> is generally available, allowing you to build more flexible, programmatic workflows. </p></li></ul><p>These improvements allow you to build both simple static sites and more complex server-side rendered applications. Like <a href="https://www.cloudflare.com/developer-platform/products/pages/">Pages</a>, you only get charged when your Worker code runs, meaning you can host and serve static sites for free. When you want to do any rendering on the server or need to build an API, simply add a Worker to handle your backend. And when you need to read or write data in your app, you can connect to an existing database with <a href="https://developers.cloudflare.com/hyperdrive/"><u>Hyperdrive</u></a>, or use any of our storage solutions: <a href="https://developers.cloudflare.com/kv/"><u>Workers KV</u></a>, <a href="https://developers.cloudflare.com/r2/"><u>R2</u></a>, <a href="https://developers.cloudflare.com/durable-objects/"><u>Durable Objects</u></a>, or <a href="https://developers.cloudflare.com/d1/"><u>D1</u></a>. </p><p>If you'd like to dive straight into code, you can deploy a single-page application built with Vite and React, with the option to connect to a hosted database with Hyperdrive, by clicking this “Deploy to Cloudflare” button: </p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/react-postgres-fullstack-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/56hrgUpOebvOdzbI8j6liw/bc9e7d01dde8cb6a8a4623aec3abc883/1.jpg" />
          </figure>
    <div>
      <h2>Start with Workers</h2>
      <a href="#start-with-workers">
        
      </a>
    </div>
    <p>Previously, you needed to choose between building on Cloudflare Pages or Workers (or use Pages for one part of your app, and Workers for another) just to get started. This meant figuring out what your app needed from the start, and hoping that if your project evolved, you wouldn’t be stuck with the wrong platform and architecture. Workers was designed to be a flexible platform, allowing developers to evolve projects as needed — and so, we’ve <a href="https://blog.cloudflare.com/pages-and-workers-are-converging-into-one-experience/"><u>worked to bring pieces of Pages into Workers</u></a> over the years.  </p><p>Now that Workers supports both serving static assets <b>and </b>server-side rendering, you should <b>start with Workers</b>. Cloudflare Pages will continue to be supported, but, going forward, all of our investment, optimizations, and feature work will be dedicated to improving Workers. We aim to make Workers the best platform for building full-stack apps, building upon your feedback of what went well with Pages and what we could improve. </p><p>Before, building an app on Pages meant you got a really easy, opinionated on-ramp, but you’d eventually hit a wall if your application got more complex. If you wanted to use Durable Objects to manage state, you would need to set up an entirely separate Worker to do so, ending up with a complicated deployment and more overhead. You also were limited to real-time logs, and could only roll out changes all in one go. </p><p>When you build on Workers, you can immediately bind to any other Developer Platform service (including <a href="https://developers.cloudflare.com/durable-objects/"><u>Durable Objects</u></a>, <a href="https://developers.cloudflare.com/email-routing/email-workers/"><u>Email Workers</u></a>, and more), and manage both your front end and back end in a single project — all with a single deployment. You also get the whole suite of <a href="https://developers.cloudflare.com/workers/observability/"><u>Workers observability</u></a> tooling built into the platform, such as <a href="https://developers.cloudflare.com/workers/observability/logs/workers-logs/"><u>Workers Logs</u></a>. And if you want to rollout changes to only a certain percentage of traffic, you can do so with <a href="https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/"><u>Gradual Deployments</u></a>.  </p><p>These latest improvements are part of our goal to bring the best parts of Pages into Workers. For example, we now support static  <a href="https://developers.cloudflare.com/workers/static-assets/headers/"><u>_headers</u></a> and <a href="https://developers.cloudflare.com/workers/static-assets/redirects/"><u>_redirects</u></a> config files, so that you can easily take an existing project from Pages (or another platform) and move it over to Workers, without needing to change your project. We also directly integrate with GitHub and GitLab with <a href="https://developers.cloudflare.com/workers/ci-cd/builds/"><u>Workers Builds</u></a>, providing automatic builds and deployments. And starting today, <a href="https://developers.cloudflare.com/workers/configuration/previews/"><u>Preview URLs</u></a> are <a href="https://developers.cloudflare.com/workers/ci-cd/builds/git-integration/github-integration/#pull-request-comment"><u>posted back to your repository as a comment</u></a>, with feature branch aliases and environments coming soon. </p><p>To learn how to migrate an existing project from Pages to Workers, read our <a href="https://developers.cloudflare.com/workers/static-assets/migrate-from-pages/"><u>migration guide</u></a>. </p><p>Next, let’s talk about how you can build applications with different rendering modes on Workers.  </p>
    <div>
      <h2>Building static sites, SPAs, and SSR on Workers</h2>
      <a href="#building-static-sites-spas-and-ssr-on-workers">
        
      </a>
    </div>
    <p>As a quick primer, here are all the architectures and rendering modes we’ll be discussing that are supported on Workers: </p><ul><li><p><b>Static sites</b>: When you visit a static site, the server immediately returns pre-built static assets — HTML, CSS, JavaScript, images, and fonts. There’s no dynamic rendering happening on the server at request-time. Static assets are typically generated at build-time and served directly from a <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/"><u>CDN</u></a>, making static sites fast and easily cacheable. This approach works well for sites with content that rarely changes. </p></li><li><p><b>Single-Page Applications (SPAs)</b>:  When you load an SPA, the server initially sends a minimal HTML shell and a JavaScript bundle (served as static assets). Your browser downloads this JavaScript, which then takes over to render the entire user interface client-side. After the initial load, all navigation occurs without full-page refreshes, typically via client-side routing. This creates a fast, app-like experience. </p></li><li><p><b>Server-Side Rendered (SSR) applications</b>: When you first visit a site that uses SSR, the server generates a fully-rendered HTML page on-demand for that request. Your browser immediately displays this complete HTML, resulting in a fast first page load. Once loaded, JavaScript "<a href="https://en.wikipedia.org/wiki/Hydration_(web_development)"><u>hydrates</u></a>" the page, adding interactivity. Subsequent navigations can either trigger new server-rendered pages or, in many modern frameworks, transition into client-side rendering similar to an SPA.</p></li></ul><p>Next, we’ll dive into how you can build these kinds of applications on Workers, starting with setting up your development environment. </p>
    <div>
      <h3>Setup: build and dev</h3>
      <a href="#setup-build-and-dev">
        
      </a>
    </div>
    <p>Before uploading your application, you need to bundle all of your client-side code into a directory of <b>static assets</b>. Wrangler bundles and builds your code when you run <code>wrangler dev</code>, but we also now support Vite with our <a href="https://www.npmjs.com/package/@cloudflare/vite-plugin"><u>new Vite plugin</u></a>. This is a great option for those already using Vite’s build tooling and development server — you can continue developing (and testing with <a href="https://developers.cloudflare.com/workers/testing/vitest-integration/"><u>Vitest</u></a>) using Vite’s development server, all using the Workers runtime. </p><p>To get started using the Cloudflare Vite plugin, you can scaffold a React application using Vite and our plugin, by running: </p>
            <pre><code>npm create cloudflare@latest my-react-app -- --framework=react</code></pre>
            <p>When you open the project, you should see a directory structure like this: </p>
            <pre><code>...
├── api
│   └── index.ts
├── public
│   └── ...
├── src
│   └── ...
...
├── index.html
├── package.json
├── vite.config.ts
└── wrangler.jsonc</code></pre>
            <p>If you run <code>npm run build</code>, you’ll see a new folder appear, named <code>/dist</code>. </p>
            <pre><code>...
├── api
│   └── index.ts
├── dist
│   └── ...
├── public
│   └── ...
├── src
│   └── ...
...
├── index.html
├── package.json
├── vite.config.ts
└── wrangler.jsonc</code></pre>
            <p>The Vite plugin informs Wrangler that this <code>/dist</code> directory contains the project’s built static assets — which, in this case, includes client-side code, some CSS files, and images. </p><p>Once deployed, this single-page application (SPA) architecture will look something like this: </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6syBGxC8udJi8wlsqvLTXW/526d05095a953cb6d29526abfd4f4b3a/2.jpg" />
          </figure><p>When a request comes in, Cloudflare looks at the pathname and automatically serves any static assets that match that pathname. For example, if your static assets directory includes a <code>blog.html</code> file, requests for <code>example.com/blog</code> get that file. </p>
    <div>
      <h3>Static sites</h3>
      <a href="#static-sites">
        
      </a>
    </div>
    <p>If you have a static site created by a static site generator (SSG) like <a href="https://docs.astro.build/en/concepts/why-astro/"><u>Astro</u></a>, all you need to do is create a <code>wrangler.jsonc</code> file (or <code>wrangler.toml</code>) and tell Cloudflare where to find your built assets: </p>
            <pre><code>// wrangler.jsonc 

{
  "name": "my-static-site",
  "compatibility_date": "2025-04-01",
  "assets": {
    "directory": "./dist",
  }
}</code></pre>
            <p>Once you’ve added this configuration, you can simply build your project and run wrangler deploy.  Your entire site will then be uploaded and ready for traffic on Workers. Once deployed and requests start flowing in, your static site will be <a href="https://developers.cloudflare.com/workers/static-assets/#caching-behavior"><u>cached across Cloudflare’s network</u></a>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/OaFnoUfvhzfjwv537fh5S/763524c2ed3b2beb61304576723667ea/3.jpg" />
          </figure><p>You can try starting a fresh Astro project on Workers today by running:</p>
            <pre><code>npm create cloudflare@latest my-astro-app -- --framework=astro</code></pre>
            <p>You can see our other supported Frameworks and how to get started in our <a href="https://developers.cloudflare.com/workers/frameworks/"><u>framework guides</u></a>. </p>
    <div>
      <h3>Single-page applications (SPAs) </h3>
      <a href="#single-page-applications-spas">
        
      </a>
    </div>
    <p>If you have a single-page application, you can explicitly enable <code>single-page-application</code> mode in your Wrangler configuration: </p>
            <pre><code>{
 "name": "example-spa-worker-hyperdrive",
 "main": "api/index.js",
 "compatibility_flags": ["nodejs_compat"],
 "compatibility_date": "2025-04-01",
 },
 "assets": {
   "directory": "./dist",
   "binding": "ASSETS",
   "not_found_handling": "single-page-application"
 },
 "hyperdrive": [
   {
     "binding": "HYPERDRIVE",
     "id": "d9c9cfb2587f44ee9b0730baa692ffec",
     "localConnectionString": "postgresql://myuser:mypassword@localhost:5432/mydatabase"
   }
 ],
 "placement": {
   "mode": "smart"
 }
}</code></pre>
            <p>By enabling this, the platform assumes that any navigation request (requests which include a <code>Sec-Fetch-Mode: navigate</code> header) are intended for static assets and will serve up <code>index.html</code> whenever a matching static asset match cannot be found. For non-navigation requests (such as requests for data) that don't match a static asset, Cloudflare will invoke the Worker script. With this setup, you can render the frontend with React, use a Worker to handle back-end operations, and use Vite to help stitch the two together. This is a great option for porting over older SPAs built with <code>create-react-app</code>, <a href="https://react.dev/blog/2025/02/14/sunsetting-create-react-app"><u>which was recently sunset</u></a>. </p><p>Another thing to note in this Wrangler configuration file: we’ve defined a Hyperdrive binding and enabled <a href="https://developers.cloudflare.com/workers/configuration/smart-placement/"><u>Smart Placement</u></a>. Hyperdrive lets us use an existing database<i> and</i> handles connection pooling. This solves a long-standing challenge of connecting Workers (which run in a highly distributed, serverless environment) directly to traditional databases. By design, Workers operate in lightweight V8 isolates with no persistent TCP sockets and a strict CPU/memory limit. This isolation is great for security and speed, but it makes it difficult to hold open database connections. Hyperdrive addresses these constraints by acting as a “bridge” between Cloudflare’s network and your database, taking care of the heavy lifting of maintaining stable connections or pools so that Workers can reuse them.  By turning on Smart Placement, we also ensure that if requests to our Worker originate far from the database (causing latency), Cloudflare can choose to relocate both the Worker—which handles the database connection—and the Hyperdrive “bridge” to a location closer to the database, ​​reducing round-trip times. </p>
    <div>
      <h4>SPA example: Worker code</h4>
      <a href="#spa-example-worker-code">
        
      </a>
    </div>
    <p>Let’s look at the <a href="https://github.com/korinne/example-spa-worker"><u>“Deploy to Cloudflare” example</u></a> at the top of this blog. In <code>api/index.js</code>, we’ve defined an API (using Hono) which connects to a hosted database through Hyperdrive. </p>
            <pre><code>import { Hono } from "hono";
import postgres from "postgres";
import booksRouter from "./routes/books";
import bookRelatedRouter from "./routes/book-related";

const app = new Hono();

// Setup SQL client middleware
app.use("*", async (c, next) =&gt; {
 // Create SQL client
 const sql = postgres(c.env.HYPERDRIVE.connectionString, {
   max: 5,
   fetch_types: false,
 });

 c.env.SQL = sql;

 // Process the request
 await next();

 // Close the SQL connection after the response is sent
 c.executionCtx.waitUntil(sql.end());
});

app.route("/api/books", booksRouter);
app.route("/api/books/:id/related", bookRelatedRouter);


export default {
 fetch: app.fetch,
};</code></pre>
            <p>When deployed, our app’s architecture looks something like this: </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/56hrgUpOebvOdzbI8j6liw/bc9e7d01dde8cb6a8a4623aec3abc883/1.jpg" />
          </figure><p>If Smart Placement moves the placement of my Worker to run closer to my database, it could look like this: </p><div>
  
</div>
<p></p>
    <div>
      <h3>Server-Side Rendering (SSR)</h3>
      <a href="#server-side-rendering-ssr">
        
      </a>
    </div>
    <p>If you want to handle rendering on the server, we support a number of popular full-stack <a href="https://developers.cloudflare.com/workers/frameworks/"><u>frameworks</u></a>. </p><p>Here’s a version of our previous example, now using React Router v7’s server-side rendering:</p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/react-router-postgres-ssr-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p><p>You could also use Next.js with the <a href="https://opennext.js.org/cloudflare"><u>OpenNext adapter</u></a>, or any other <a href="https://developers.cloudflare.com/workers/frameworks/"><u>framework listed in our framework guides</u></a>. </p>
    <div>
      <h2>Deploy to Workers, with as few changes as possible</h2>
      <a href="#deploy-to-workers-with-as-few-changes-as-possible">
        
      </a>
    </div>
    
    <div>
      <h3>Node.js compatibility</h3>
      <a href="#node-js-compatibility">
        
      </a>
    </div>
    <p>We’ve also continued to make progress supporting Node.js APIs, recently adding support for the <code>crypto</code>, <code>tls</code>, <code>net</code>, and <code>dns</code> modules. This allows existing applications and libraries that rely on these Node.js modules to run on Workers. Let’s take a look at an example:</p><p>Previously, if you tried to use the <code>mongodb</code> package, you encountered the following error:</p>
            <pre><code>Error: [unenv] dns.resolveTxt is not implemented yet!</code></pre>
            <p>This occurred when <code>mongodb</code> used the <code>node:dns</code> module to do a DNS lookup of a hostname. Even if you avoided that issue, you would have encountered another error when <code>mongodb</code> tried to use <code>node:tls</code> to securely connect to a database.</p><p>Now, you can use <code>mongodb</code> as expected because <code>node:dns</code> and <code>node:tls</code> are supported. The same can be said for libraries relying on <code>node:crypto</code> and <code>node:net</code>.</p><p>Additionally, Workers <a href="https://developers.cloudflare.com/changelog/2025-03-11-process-env-support/"><u>now expose environment variables and secrets on the process.env object</u></a> when the <code>nodejs_compat</code> compatibility flag is on and the compatibility date is set to <code>2025-04-01</code> or beyond. Some libraries (and developers) assume that this object will be populated with variables, and rely on it for top-level configuration. Without the tweak, libraries may have previously broken unexpectedly and developers had to write additional logic to handle variables on Cloudflare Workers.</p><p>Now, you can just access your variables as you would in Node.js.</p>
            <pre><code>const LOG_LEVEL = process.env.LOG_LEVEL || "info";</code></pre>
            
    <div>
      <h3>Additional Worker CPU time</h3>
      <a href="#additional-worker-cpu-time">
        
      </a>
    </div>
    <p>We have also <a href="https://developers.cloudflare.com/changelog/2025-03-25-higher-cpu-limits/"><u>raised the maximum CPU time per Worker request</u></a> from 30 seconds to 5 minutes. This allows for compute-intensive operations to run for longer without timing out. Say you want to use the newly supported <code>node:crypto</code> module to hash a very large file, you can now do this on Workers without having to rely on external compute for CPU-intensive operations.</p>
    <div>
      <h3>Workers Builds </h3>
      <a href="#workers-builds">
        
      </a>
    </div>
    <p>We’ve also made improvements to <a href="https://developers.cloudflare.com/workers/ci-cd/builds/"><u>Workers Builds</u></a>, which allows you to connect a Git repository to your Worker, so that you can have automatic builds and deployments on every pushed change. Workers Builds was introduced during <a href="https://blog.cloudflare.com/builder-day-2024-announcements/#continuous-integration-and-delivery"><u>Builder Day 2024</u></a>, and initially only allowed you to connect a repository to an existing Worker. Now, you can bring a repository and <a href="https://blog.cloudflare.com/deploy-workers-applications-in-seconds/"><u>immediately deploy it as a new Worker</u></a>, reducing the amount of setup and button clicking needed to bring a project over. We’ve improved the performance of Workers Builds by reducing the latency of build starts by <b>6 seconds</b> — they now start within <b>10 seconds</b> on average. We also boosted API responsiveness, achieving a <b>7x </b>latency improvement thanks to Smart Placement. </p><ul><li><p><b>Note</b>: On April 2, 2025, Workers Builds transitioned to a new pricing model, as announced during <a href="https://blog.cloudflare.com/builder-day-2024-announcements/"><u>Builder Day 2024</u></a>. Free plan users are now capped at 3,000 minutes of build time, and Workers Paid subscription users will have a new usage-based model with 6,000 free minutes included and $0.005 per build minute pricing after. To better support concurrent builds, Paid plans will also now get six (6) concurrent builds, making it easier to work across multiple projects and monorepos. For more information on pricing, see the <a href="https://developers.cloudflare.com/workers/ci-cd/builds/limits-and-pricing/"><u>documentation</u></a>.</p></li></ul><p>You can also set up Workers Builds to <a href="https://developers.cloudflare.com/workers/ci-cd/builds/build-branches/#configure-non-production-branch-builds"><u>run on non-production branches</u></a>, and preview URLs will be <a href="https://developers.cloudflare.com/workers/ci-cd/builds/git-integration/github-integration/#pull-request-comment"><u>posted back to GitHub as a comment</u></a>. </p>
    <div>
      <h3>Bind the Images API to your Worker</h3>
      <a href="#bind-the-images-api-to-your-worker">
        
      </a>
    </div>
    <p>Last week, we wrote a <a href="https://blog.cloudflare.com/improve-your-media-pipelines-with-the-images-binding-for-cloudflare-workers/"><u>blog post</u></a> that covers how the Images binding enables more flexible, programmatic workflows for image optimization.</p><p>Previously, you could access image optimization features by calling <code>fetch()</code> in your Worker. This method requires the original image to be retrievable by URL. However, you may have cases where images aren’t accessible from a URL, like when you want to compress user-uploaded images before they are uploaded to your storage. With the Images binding, you can directly optimize an image by operating on its body as a stream of bytes.</p><p>To learn more, read our guide on <a href="https://developers.cloudflare.com/images/tutorials/optimize-user-uploaded-image"><u>transforming an image before it gets uploaded to R2</u></a>.</p>
    <div>
      <h2>Start building today</h2>
      <a href="#start-building-today">
        
      </a>
    </div>
    <p>We’re excited to see what you’ll build, and are focused on new features and improvements to make it  easier to create any application on Workers. Much of this work was made even better by community feedback, and we encourage everyone to <a href="https://discord.com/invite/cloudflaredev"><u>join our Discord</u></a> to participate in the discussion. </p><p><b>Helpful resources to get you started:</b></p><ul><li><p><a href="https://developers.cloudflare.com/workers/frameworks/"><u>Framework guides</u></a> </p></li><li><p><a href="https://developers.cloudflare.com/workers/static-assets/migrate-from-pages/"><u>Migration guide</u></a> </p></li><li><p><a href="https://developers.cloudflare.com/workers/static-assets/"><u>Static assets documentation</u></a> </p></li><li><p><a href="https://developers.cloudflare.com/workers/vite-plugin"><u>Cloudflare Vite plugin documentation</u></a></p></li></ul><p></p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Front End]]></category>
            <category><![CDATA[Full Stack]]></category>
            <category><![CDATA[General Availability]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[MySQL]]></category>
            <category><![CDATA[Hyperdrive]]></category>
            <guid isPermaLink="false">67CgcpMED2Rw0BozjKbdUz</guid>
            <dc:creator>Korinne Alpers</dc:creator>
        </item>
        <item>
            <title><![CDATA[The story of web framework Hono, from the creator of Hono]]></title>
            <link>https://blog.cloudflare.com/the-story-of-web-framework-hono-from-the-creator-of-hono/</link>
            <pubDate>Thu, 17 Oct 2024 13:00:00 GMT</pubDate>
            <description><![CDATA[ Hono is a web framework that is fast, lightweight, and built using the Web Standards API. Hear the story of Hono by the creator of Hono. ]]></description>
            <content:encoded><![CDATA[ <p><a href="https://hono.dev/"><u>Hono</u></a> is a fast, lightweight web framework that runs anywhere JavaScript does, built with Web Standards. Of course, it runs on Cloudflare Workers.</p><p>It was three years ago, in December 2021. At that time, I wanted to create applications for Cloudflare Workers, but the code became verbose without using a framework, and couldn't find a framework that suited my needs. <a href="https://itty.dev/itty-router/"><u>Itty-router</u></a> was very nice but too simple. <a href="https://github.com/lukeed/worktop"><u>Worktop</u></a> and <a href="https://sunderjs.com/"><u>Sunder</u></a> did the same things I wanted to do, but their APIs weren't quite to my liking. I was also interested in creating a router — a program that determines which action is executed based on the HTTP method and URL path of the Request — made of a <a href="https://en.wikipedia.org/wiki/Trie"><u>Trie tree</u></a> structure because it’s fast. So, I started building a web framework with a Trie tree-based router.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6eWlTEPSJmX4grfpKi795b/d6596a29931dc0e24602683742916009/image7.png" />
          </figure><p> “<i>While trying to create my applications, I ended up creating my framework for them.”</i> — a classic example of <a href="https://en.wiktionary.org/wiki/yak_shaving"><u>yak shaving</u></a>. However, Hono is now used by many developers, including Cloudflare, which uses Hono in core products. So, this journey into the depths of yak shaving was ultimately meaningful.</p>
    <div>
      <h2>Write once, run anywhere</h2>
      <a href="#write-once-run-anywhere">
        
      </a>
    </div>
    <p>Hono truly runs anywhere — not just on Cloudflare Workers. I’ll discuss why later in the post, but Hono also runs on Deno, Bun, and Node.js. This is because Hono does not depend on external libraries, but uses only the Web Standards API, and each runtime supports Web Standards.</p><p>It's a delight for developers to know that the same code can run across different runtimes. For instance, the following src/index.ts code will run on Cloudflare Workers, Deno, and Bun.</p>
            <pre><code>import { Hono } from 'hono'

const app = new Hono()
app.get('/hello', (c) =&gt; c.text('Hello Hono!'))

export default app</code></pre>
            <p>To run it on Cloudflare Workers, you execute the Wrangler command:</p>
            <pre><code>wrangler dev src/index.ts</code></pre>
            <p>The same code works on Deno:</p>
            <pre><code>deno serve src/index.ts</code></pre>
            <p>And it works on Bun too:</p>
            <pre><code>bun run src/index.ts</code></pre>
            <p>This is only a simple "Hello World" example, but more complex applications with middleware and helpers that are discussed below can be run on Cloudflare Workers or the other runtimes. As proof of this, almost all our test code for Hono itself can run the same way on these runtimes. This is a genuine "<i>write once, run anywhere</i>" experience.</p>
    <div>
      <h2>Who is using Hono?</h2>
      <a href="#who-is-using-hono">
        
      </a>
    </div>
    <p>Hono is now used by many developers and companies. For example, <a href="https://unkey.dev/"><u>Unkey</u></a> deploys their application built with <a href="https://hono.dev/examples/zod-openapi"><u>Hono's OpenAPI feature</u></a> to Cloudflare Workers. The following is a list of companies using Hono, based on my survey "<a href="https://github.com/orgs/honojs/discussions/1510"><u>Who is using Hono in production?</u></a>”.</p><ul><li><p>Cloudflare</p></li><li><p><a href="https://nodecraft.com/"><u>Nodecraft</u></a></p></li><li><p><a href="https://www.openstatus.dev/"><u>OpenStatus</u></a></p></li><li><p><a href="https://www.unkey.com/"><u>Unkey</u></a></p></li><li><p><a href="https://about.goen-s.com/"><u>Goens</u></a></p></li><li><p><a href="https://notahotel.com"><u>NOT A HOTEL</u></a></p></li><li><p><a href="https://www.cyberagent.co.jp/"><u>CyberAgent</u></a></p></li><li><p><a href="https://www.ai-shift.co.jp/"><u>AI shift</u></a></p></li><li><p><a href="http://hanabi.rest/"><u>Hanabi.rest</u></a></p></li><li><p><a href="https://baseai.dev/"><u>BaseAI</u></a>
</p></li></ul><p>There are many, many more companies not listed here. And major web services or libraries, such as <a href="https://www.prisma.io/"><u>Prisma</u></a>, <a href="https://resend.com/"><u>Resend</u></a>, <a href="https://sdk.vercel.ai/"><u>Vercel AI SDK</u></a>, <a href="https://supabase.com/"><u>Supabase</u></a>, and <a href="https://upstash.com/"><u>Upstash</u></a>, use Hono in their examples. There are also several influencers who like Hono and use it as an alternative to <a href="https://expressjs.com/"><u>Express</u></a>.</p><p>Of course, at Cloudflare, we also use Hono. <a href="https://www.cloudflare.com/developer-platform/products/d1/">D1</a> uses Hono for the internal Web API running on Workers. Workers Logs is based on code from <a href="https://blog.cloudflare.com/cloudflare-acquires-baselime-expands-observability-capabilities/"><u>Baselime (acquired by Cloudflare)</u></a> and uses Hono to migrate the applications from their original infrastructure to Cloudflare Workers. All Workers Logs internal or customer-facing APIs are run on Workers using Hono. We also use Hono as part of the internals of many other products, such as KV and Queues.</p>
    <div>
      <h2>Why are you making a “multi-runtime” framework?</h2>
      <a href="#why-are-you-making-a-multi-runtime-framework">
        
      </a>
    </div>
    <p>You might wonder “<i>Why is an employee of Cloudflare creating a framework that runs everywhere?</i>” Initially, Hono was designed to work exclusively with Cloudflare Workers. However, starting with <a href="https://github.com/honojs/hono/releases/tag/v2.0.0"><u>version 2</u></a>, I added support for Deno and Bun. This was a very wise decision. If Hono had been targeted only at Cloudflare Workers, it might not have attracted as many users. By running on more runtimes, it gains more users, leading to the discovery of bugs and receiving more feedback, which ultimately leads to higher quality software.</p>
    <div>
      <h2>Hono and Cloudflare are a perfect combo</h2>
      <a href="#hono-and-cloudflare-are-a-perfect-combo">
        
      </a>
    </div>
    <p>The combination of Hono and Cloudflare offers a delightful developer experience.</p><p>Many websites, including <a href="https://developers.cloudflare.com/"><u>our Cloudflare Docs</u></a>, introduce the following "vanilla" JavaScript as a "Hello World" for Cloudflare Workers:</p>
            <pre><code>export default {
  fetch: () =&gt; {
    return new Response('Hello World!')
  }
}
</code></pre>
            <p>This is primitive and good for understanding the Workers principle. However, if you want to create an endpoint that "returns a JSON response for GET requests that come to <code>/books</code>", you need to write something like this:</p>
            <pre><code>export default {
  fetch: (req) =&gt; {
    const url = new URL(req.url)
    if (req.method === 'GET' &amp;&amp; url.pathname === '/books') {
      return Response.json({
        ok: true
      })
    }
    return Response.json(
      {
        ok: false
      },
      {
        status: 404
      }
    )
  }
}
</code></pre>
            <p>If you use Hono, you can write it like the following:</p>
            <pre><code>import { Hono } from 'hono'

const app = new Hono()

app.get('/books', (c) =&gt; {
  return c.json({
    ok: true
  })
})

export default app</code></pre>
            <p>It is short. And you can understand that “it handles <code>GET</code> accesses to <code>/books</code>” intuitively.</p><p>If you want to handle <code>GET</code> requests to <code>/authors/yusuke</code> and get "yusuke" from the path —  "yusuke" is variable, you have to add something more complicated. The below is "vanilla" JavaScript example:</p>
            <pre><code>if (req.method === 'GET') {
  const match = url.pathname.match(/^\/authors\/([^\/]+)/)
  if (match) {
    const author = match[1]
    return Response.json({
      Author: author
    })
  }
}
</code></pre>
            <p>If you use Hono, you don't need <code>if</code> statements. Just add the endpoint definition to the <code>app</code>. Also, you don't need to write a regular expression to get "yusuke". You can get it with the function <code>c.req.param()</code>:</p>
            <pre><code>app.get('/authors/:name', (c) =&gt; {
  const author = c.req.param('name')
  return c.json({
    Author: author
  })
})
</code></pre>
            <p>One or two routes may be fine, but any more than that and maintenance becomes tricky. Code becomes more complex and bugs are harder to find. Using Hono, the code is very neat.</p><p>It is also easy to handle bindings to Cloudflare products, such as <a href="https://developers.cloudflare.com/kv/"><u>KV</u></a>, <a href="https://developers.cloudflare.com/r2/"><u>R2</u></a>, <a href="https://developers.cloudflare.com/d1/"><u>D1</u></a>, etc. as Hono uses a "context model". A context is a container that holds the application's state until a request is received, and a response is returned. You can use a context to retrieve a request object, set response headers, and create custom variables. It also holds Cloudflare bindings. For example, if you set up a Cloudflare KV namespace with the name <code>MY_KV</code>, you can access it as follows, with TypeScript type completion.</p>
            <pre><code>import { Hono } from 'hono'

type Env = {
  Bindings: {
    MY_KV: KVNamespace
  }
}

const app = new Hono&lt;Env&gt;()

app.post('/message', async (c) =&gt; {
  const message = c.req.query('message') ?? 'Hi'
  await c.env.MY_KV.put('message', message)
  return c.text(`message is set`, 201)
})</code></pre>
            <p>Hono lets you write code in a simple and intuitive way, but that doesn't mean there are limitations. You can do everything possible with Cloudflare Workers using Hono.</p>
    <div>
      <h2>Add it when you want to use it</h2>
      <a href="#add-it-when-you-want-to-use-it">
        
      </a>
    </div>
    <p>Hono is tiny. With the smallest preset, <code>hono/tiny</code>, you can write a "Hello World" application in just 12 KB. This is because it uses only the Web Standards API built into the runtime and has minimal functions. In comparison, the bundle size of Express is <a href="https://bundlephobia.com/package/express@4.21.0"><u>579 KB</u></a>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Av7EyqZPKagcswI0xgwd1/713fccc1608b7eeb97d2e63b18765737/image1.png" />
          </figure><p>However, there is much that you can do.</p><p>You can easily add functions using middleware. For example, it is a bit tedious to implement Basic Authentication from scratch, but with the built-in <a href="https://hono.dev/docs/middleware/builtin/basic-auth"><u>Basic Auth middleware</u></a>, you can apply Basic Authentication to the path <code>/auth/page</code> with just this:</p>
            <pre><code>import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'

const app = new Hono()

app.use(
  '/auth/*',
  basicAuth({
    username: 'hono',
    password: 'acoolproject',
  })
)

app.get('/auth/page', (c) =&gt; {
  return c.text('You are authorized')
})
</code></pre>
            <p>Hono's package also includes built-in middleware that allows Bearer and JWT authentication, and easy configuration of CORS, etc. These built-in middleware components do not depend on external libraries, but there is also many 3rd-party middleware that allow the use of external libraries, such as authentication middleware using <a href="https://clerk.com/"><u>Clerk</u></a> and <a href="https://authjs.dev/"><u>Auth.js</u></a>, and validators using <a href="https://zod.dev/"><u>Zod</u></a> and <a href="https://valibot.dev/"><u>Valibot</u></a>.</p><p>There are also a number of built-in helpers, including the <a href="https://hono.dev/docs/helpers/streaming"><u>Streaming helper</u></a>, which is useful for implementing AI. These can be added when you want to use them, and the file size increases only when they are added.</p><p>In Cloudflare Workers, <a href="https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits"><u>there is a limit to a file size of a Worker</u></a>. Keeping the core small and extending functions with middleware and helpers makes a lot of sense.</p>
    <div>
      <h2>Onion structure</h2>
      <a href="#onion-structure">
        
      </a>
    </div>
    <p>The important concepts of Hono are ”handler” and "middleware”.</p><p>A handler is a place to write a function that receives a request and returns a response, as specified by the user. For example, you can write a handler that gets a value of a query parameter, retrieves data from a database, and returns the result in JSON. Middleware can handle the requests that come to the handler and the responses that the handler returns. You can combine middleware with other middleware to build more large and complex applications. It is structured like an onion.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6GniuL8WPyoXpvbULJbqDc/c8308c5eb6255ddaacda4b197ed424be/image2.png" />
          </figure><p>In a remarkably simple way, you can create middleware. For example, a custom logger that logs the request can be written as follows:</p>
            <pre><code>app.use(async (c, next) =&gt; {
  console.log(`[${c.req.method}] ${c.req.path}`)
  await next()
})
</code></pre>
            <p>If you want to add a custom header to the response, write the following:</p>
            <pre><code>app.use(async (c, next) =&gt; {
  await next()
  c.header('X-Message', 'Hi, this is Hono!')
})
</code></pre>
            <p>It would be interesting to combine this with <a href="https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/"><u>HTMLRewriter</u></a>. If an endpoint returns HTML, the middleware that modifies the HTML tags in it can be written as follows:</p>
            <pre><code>app.get('/pages/*', async (c, next) =&gt; {
  await next()

  class AttributeRewriter {
    constructor(attributeName) {
      this.attributeName = attributeName
    }
    element(element) {
      const attribute = element.getAttribute(this.attributeName)
      if (attribute) {
        element.setAttribute(this.attributeName, attribute.replace('oldhost', 'newhost'))
      }
    }
  }
  const rewriter = new HTMLRewriter().on('a', new AttributeRewriter('href'))

  const contentType = c.res.headers.get('Content-Type')

  if (contentType!.startsWith('text/html')) {
    c.res = rewriter.transform(c.res)
  }
})
</code></pre>
            <p>There is very little to remember to create middleware. All you have to do is to work with the context, which you should already know.</p>
    <div>
      <h2>The RPC is like magic</h2>
      <a href="#the-rpc-is-like-magic">
        
      </a>
    </div>
    <p>Hono has a strong type system. One feature that uses this is RPC (Remote Procedure Call). With RPC, you can express server-side API specifications as TypeScript types. When these types are loaded as generics in a client, the paths, arguments, and return types of each API endpoint are inferred. It's like magic.</p><p>For example, imagine an endpoint for creating a blog post. This endpoint takes a <code>number</code> type <code>id</code> and a <code>string</code> type <code>title</code>. Using <a href="https://zod.dev/"><u>Zod</u></a>, one of the validator libraries that support TypeScript inference, you can define the schema like this:</p>
            <pre><code>import { z } from 'zod'

const schema = z.object({
  id: z.number(),
  title: z.string()
})
</code></pre>
            <p>You create a handler that receives this object in JSON format via a POST request to the path <code>/posts</code>. Using <a href="https://github.com/honojs/middleware/tree/main/packages/zod-validator"><u>Zod Validator</u></a>, you check if it matches the schema. The response will have a property called message of type string.</p>
            <pre><code>import { zValidator } from '@hono/zod-validator'

const app = new Hono().basePath('/v1')

// ...

const routes = app.post('/posts', zValidator('json', schema), (c) =&gt; {
  const data = c.req.valid('json')
  return c.json({
    message: `${data.id.toString()} is ${data.title}`
  })
})
</code></pre>
            <p>This is a “typical” Hono handler. However, the TypeScript type you can get from the <code>typeof</code> for the <code>routes</code> will contain the information about its Web API specification. In this case, it includes the endpoint for creating blog posts — sending a POST request to the path <code>/posts</code> returns a JSON object.</p>
            <pre><code>export type AppType = typeof routes</code></pre>
            <p>Now, let's create a client. You pass the earlier <code>AppType</code> as generics to a Hono client object.</p>
            <pre><code>import { hc } from 'hono/client'
import { AppType } from '.'

const client = hc&lt;AppType&gt;('http://localhost:8787')</code></pre>
            <p>With this setup, you're ready. It's magic time.</p><div>
  
</div><p>Code completion works perfectly. When you write client-side code, you no longer need to know the API specifications completely, which also helps eliminate mistakes.</p>
    <div>
      <h2>Server-side JSX is fun</h2>
      <a href="#server-side-jsx-is-fun">
        
      </a>
    </div>
    <p>Hono provides built-in JSX, a syntax that allows you to write code in JavaScript that looks like HTML tags. When you hear the term JSX, you may think of React, a front-end UI library. However, Hono's JSX was initially developed to run only on the server side. When we first started developing Hono, we were looking for template engines to render HTML. Most template engines, such as <a href="https://handlebarsjs.com/"><u>Handlebars</u></a> and <a href="https://ejs.co/"><u>EJS</u></a>, use <code>eval</code> internally and are incompatible with Cloudflare Workers, which does not support it. Then we came up with the idea of using JSX.</p><p>Hono's JSX is unique in that it treats the tags as a string. So the following strange code actually works.</p>
            <pre><code>console.log((&lt;h1&gt;Hello!&lt;/h1&gt;).toString())</code></pre>
            <p>There is no need to do <code>renderToString()</code> as in React. If you want to render HTML, just return this as is.</p>
            <pre><code>app.get('/', (c) =&gt; c.html(&lt;h1&gt;Hello&lt;/h1&gt;))</code></pre>
            <p>Very interesting is the creation of <code>Suspense</code> — a feature in React that allows you to display a fallback UI while waiting for an asynchronous component to load — without any client implementation. The asynchronous components are running in a server-only implementation.</p><div>
  
</div><p></p><p>Server-side JSX is a better developer experience than you might imagine. You can use the toolchains for React's JSX in the same way for Hono's JSX, including the ability to complete tags in the editor. They bring mature front-end technology to the server side.</p>
    <div>
      <h2>Testing is important</h2>
      <a href="#testing-is-important">
        
      </a>
    </div>
    <p>Testing is important. Fortunately, you can write tests easily when using Hono.</p><p>For example, let's write a test for an endpoint. To test for a 200 response status of a request coming to <code>/</code> with the GET method, you can write the following:</p>
            <pre><code>it('should return 200 response', async () =&gt; {
  const res = await app.request('/')
  expect(res.status).toBe(200)
})
</code></pre>
            <p>Simple, right? The beauty of this test is that you don't have to bring up the server. The Web Standard API black boxes the server layer. The internal tests of Hono have 20,000 lines of code, but most of them are written in the same style as above, without the server up and running. </p>
    <div>
      <h2>Going to full-stack</h2>
      <a href="#going-to-full-stack">
        
      </a>
    </div>
    <p>We released a new major <a href="https://github.com/honojs/hono/releases/tag/v4.0.0"><u>version 4</u></a> in February 2024. There are three main features that stand out:</p><ol><li><p>Static site generation</p></li><li><p>Client components</p></li><li><p>File-based routing</p></li></ol><p>With these features, you can create full-stack applications with a user interface in Hono.</p><p>The introduction of client components allows JSX to work in the client. Now you can add interactions to your pages. Static site generation allows you to create blogs, etc. without having to bundle them into a single JavaScript file. We have also started an experimental project called <a href="https://github.com/honojs/honox"><u>HonoX</u></a>. This is a meta-framework using Hono and Vite that provides file-based routing and a mechanism to hydrate client-side components to server-side generated HTML. It is easier to create larger applications that are a great match for Cloudflare Pages or Workers.</p><p>In addition to that, plans are underway to run it as a base server for existing full-stack frameworks such as <a href="https://remix.run/"><u>Remix</u></a> and <a href="https://qwik.dev/"><u>Qwik</u></a>.</p><p>In contrast to the <a href="https://nextjs.org/"><u>Next.js</u></a> framework, which started from the client-side with React, Hono is trying to become a full-stack framework starting from the server-side.</p>
    <div>
      <h2>Hono Conference</h2>
      <a href="#hono-conference">
        
      </a>
    </div>
    <p>On June 22, 2024, I held the <a href="https://blog.yusu.ke/hono-conference-2024/"><u>"Hono Conference" in Tokyo</u></a>, the first event to consist entirely of Hono-focused talks. One hundred people attended, and the event was a great success.</p><p>It was my dream to do this event. Now, there are 200 contributors to the <a href="https://github.com/honojs/hono"><code><u>honojs/hono</u></code></a> repository on GitHub. If you include other Hono related repositories, there are many more. Creating "the most invincible framework we could think of" is a lot of fun for contributors and users.</p><p>Below is a group photo taken at the end of the event. This is my treasure. I want to make the 2nd event a global event.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2EKltkDZeAvWzxVtnewOH6/995bceacd2ad37c59885e732e51bae87/image3.jpg" />
          </figure>
    <div>
      <h2>Hono is 炎</h2>
      <a href="#hono-is-yan">
        
      </a>
    </div>
    <p>I haven't mentioned the origin of the name Hono yet. The name Hono is from the Japanese word for "<i>炎</i>". It is similar to the word "<i>flare</i>". Hono now runs on a variety of runtimes, but I said that it was first created to create Cloud"<i>flare</i>" Workers applications. It is an honor for Cloudflare that it has remained in its name.</p><p>That is all that the creator of Hono has to say about Hono.</p>
    <div>
      <h2>Just try it</h2>
      <a href="#just-try-it">
        
      </a>
    </div>
    <p>Everyone who has experienced application development with Hono and Cloudflare Workers says "<i>the developer experience is a great experience</i>". If you haven't experienced it yet, just try it.</p><p>See <a href="https://hono.dev/"><u>the Hono website</u></a> for how to get started. If you are interested in reporting issues or contributing, please see <a href="https://github.com/honojs"><u>the GitHub project</u></a>. Plus, you can watch <a href="https://www.youtube.com/watch?v=yoqtk85HITM"><u>my interview about Hono</u></a> on the YouTube <a href="https://www.youtube.com/@CloudflareDevelopers"><u>Cloudflare Developers channel</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <guid isPermaLink="false">2P9zNPNEmMlhOCUA8XM37a</guid>
            <dc:creator>Yusuke Wada</dc:creator>
        </item>
        <item>
            <title><![CDATA[Developer Week 2024 wrap-up]]></title>
            <link>https://blog.cloudflare.com/developer-week-2024-wrap-up/</link>
            <pubDate>Mon, 08 Apr 2024 13:00:02 GMT</pubDate>
            <description><![CDATA[ Developer Week 2024 has officially come to a close. Here’s a quick recap of the announcements and in-depth technical explorations that went out last week ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7fwPu75tSubJgSS8nJ5gOt/6e2fd9b7cc6f9dcd7b86d73988a6e5fb/Dev-week-wrap-up-1.jpg" />
            
            </figure><p>Developer Week 2024 has officially come to a close. Each day last week, we shipped new products and functionality geared towards giving developers the components they need to build full-stack applications on Cloudflare.</p><p>Even though Developer Week is now over, we are continuing to innovate with the over two million developers who build on our platform. Building a platform is only as exciting as seeing what developers build on it. Before we dive into a recap of the announcements, to send off the week, we wanted to share how a couple of companies are using Cloudflare to power their applications:</p><blockquote><p><i>We have been using Workers for image delivery using R2 and have been able to maintain stable operations for a year after implementation. The speed of deployment and the flexibility of detailed configurations have greatly reduced the time and effort required for traditional server management. In particular, we have seen a noticeable cost savings and are deeply appreciative of the support we have received from Cloudflare Workers.</i>- <a href="http://www.fancs.com/">FAN Communications</a></p></blockquote><blockquote><p><i>Milkshake helps creators, influencers, and business owners create engaging web pages directly from their phone, to simply and creatively promote their projects and passions. Cloudflare has helped us migrate data quickly and affordably with R2. We use Workers as a routing layer between our users' websites and their images and assets, and to build a personalized analytics offering affordably. Cloudflare’s innovations have consistently allowed us to run infrastructure at a fraction of the cost of other developer platforms and we have been eagerly awaiting updates to D1 and Queues to sustainably scale Milkshake as the product continues to grow.</i>- <a href="https://milkshake.app/">Milkshake</a></p></blockquote><p>In case you missed anything, here’s a quick recap of the announcements and in-depth technical explorations that went out last week:</p>
    <div>
      <h2>Summary of announcements</h2>
      <a href="#summary-of-announcements">
        
      </a>
    </div>
    
    <div>
      <h3>Monday</h3>
      <a href="#monday">
        
      </a>
    </div>
    
<table>
<thead>
  <tr>
    <th><span>Announcement</span></th>
    <th><span>Summary</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/making-full-stack-easier-d1-ga-hyperdrive-queues"><span>Making state easy with D1 GA, Hyperdrive, Queues and Workers Analytics Engine updates</span></a></td>
    <td><span>A core part of any full-stack application is storing and persisting data! We kicked off the week with announcements that help developers build stateful applications on top of Cloudflare, including making D1, Cloudflare’s SQL database and Hyperdrive, our database accelerating service, generally available.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/building-d1-a-global-database"><span>Building D1: a Global Database</span></a></td>
    <td><span>D1, Cloudflare’s SQL database, is now generally available. With new support for 10GB databases, data export, and enhanced query debugging, we empower developers to build production-ready applications with D1 to meet all their relational SQL needs. To support Workers in global applications, we’re sharing a sneak peek of our design and API for D1 global read replication to demonstrate how developers scale their workloads with D1.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/workers-environment-live-object-bindings"><span>Why Workers environment variables contain live objects</span></a></td>
    <td><span>Bindings don't just reduce boilerplate. They are a core design feature of the Workers platform which simultaneously improve developer experience and application security in several ways. Usually these two goals are in opposition to each other, but bindings elegantly solve for both at the same time.</span></td>
  </tr>
</tbody>
</table>
    <div>
      <h3>Tuesday</h3>
      <a href="#tuesday">
        
      </a>
    </div>
    
<table>
<thead>
  <tr>
    <th><span>Announcement</span></th>
    <th><span>Summary</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/workers-ai-ga-huggingface-loras-python-support"><span>Leveling up Workers AI: General Availability and more new capabilities</span></a></td>
    <td><span>We made a series of AI-related announcements, including Workers AI, Cloudflare’s inference platform becoming GA, support for fine-tuned models with LoRAs, one-click deploys from HuggingFace, Python support for Cloudflare Workers, and more.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/fine-tuned-inference-with-loras"><span>Running fine-tuned models on Workers AI with LoRAs</span></a></td>
    <td><span>Workers AI now supports fine-tuned models using LoRAs. But what is a LoRA and how does it work? In this post, we dive into fine-tuning, LoRAs and even some math to share the details of how it all works under the hood.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/python-workers"><span>Bringing Python to Workers using Pyodide and WebAssembly</span></a></td>
    <td><span>We introduced Python support for Cloudflare Workers, now in open beta. We've revamped our systems to support Python, from the Workers runtime itself to the way Workers are deployed to Cloudflare’s network. Learn about a Python Worker's lifecycle, Pyodide, dynamic linking, and memory snapshots in this post.</span></td>
  </tr>
</tbody>
</table>
    <div>
      <h3>Wednesday</h3>
      <a href="#wednesday">
        
      </a>
    </div>
    
<table>
<thead>
  <tr>
    <th><span>Announcement</span></th>
    <th><span>Summary</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/r2-events-gcs-migration-infrequent-access"><span>R2 adds event notifications, support for migrations from Google Cloud Storage, and an infrequent access storage tier</span></a></td>
    <td><span>We announced three new features for Cloudflare R2: event notifications, support for migrations from Google Cloud Storage, and an infrequent access storage tier.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/data-anywhere-events-pipelines-durable-execution-workflows"><span>Data Anywhere with Pipelines, Event Notifications, and Workflows</span></a></td>
    <td><span>We’re making it easier to build scalable, reliable, data-driven applications on top of our global network, and so we announced a new Event Notifications framework; our take on durable execution, Workflows; and an upcoming streaming ingestion service, Pipelines.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/prisma-orm-and-d1"><span>Improving Cloudflare Workers and D1 developer experience with Prisma ORM</span></a></td>
    <td><span>Together, Cloudflare and Prisma make it easier than ever to deploy globally available apps with a focus on developer experience. To further that goal, Prisma ORM now natively supports Cloudflare Workers and D1 in Preview. With version 5.12.0 of Prisma ORM you can now interact with your data stored in D1 from your Cloudflare Workers with the convenience of the Prisma Client API. Learn more and try it out now.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/picsart-move-to-workers-huge-performance-gains"><span>How Picsart leverages Cloudflare's Developer Platform to build globally performant services</span></a></td>
    <td><span>Picsart, one of the world’s largest digital creation platforms, encountered performance challenges in catering to its global audience. Adopting Cloudflare's global-by-default Developer Platform emerged as the optimal solution, empowering Picsart to enhance performance and scalability substantially.</span></td>
  </tr>
</tbody>
</table>
    <div>
      <h3>Thursday</h3>
      <a href="#thursday">
        
      </a>
    </div>
    
<table>
<thead>
  <tr>
    <th><span>Announcement</span></th>
    <th><span>Summary</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/pages-workers-integrations-monorepos-nextjs-wrangler"><span>Announcing Pages support for monorepos, wrangler.toml, database integrations and more!</span></a></td>
    <td><span>We launched four improvements to Pages that bring functionality previously restricted to Workers, with the goal of unifying the development experience between the two. Support for monorepos, wrangler.toml, new additions to Next.js support and database integrations!</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/workers-production-safety"><span>New tools for production safety — Gradual Deployments, Stack Traces, Rate Limiting, and API SDKs</span></a></td>
    <td><span>Production readiness isn’t just about scale and reliability of the services you build with. We announced five updates that put more power in your hands – Gradual Deployments, Source mapped stack traces in Tail Workers, a new Rate Limiting API, brand-new API SDKs, and updates to Durable Objects – each built with mission-critical production services in mind.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/whats-next-for-cloudflare-media"><span>What’s new with Cloudflare Media: updates for Calls, Stream, and Images</span></a></td>
    <td><span>With Cloudflare Calls in open beta, you can build real-time, serverless video and audio applications. Cloudflare Stream lets your viewers instantly clip from ongoing streams. Finally, Cloudflare Images now supports automatic face cropping and has an upload widget that lets you easily integrate into your application.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/cloudflare-calls-anycast-webrtc"><span>Cloudflare Calls: millions of cascading trees all the way down</span></a></td>
    <td><span>Cloudflare Calls is a serverless SFU and TURN service running at Cloudflare’s edge. It’s now in open beta and costs $0.05/ real-time GB. It’s 100% anycast WebRTC.</span></td>
  </tr>
</tbody>
</table>
    <div>
      <h3>Friday</h3>
      <a href="#friday">
        
      </a>
    </div>
    
<table>
<thead>
  <tr>
    <th><span>Announcement</span></th>
    <th><span>Summary</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/browser-rendering-api-ga-rolling-out-cloudflare-snippets-swr-and-bringing-workers-for-platforms-to-our-paygo-plans"><span>Browser Rendering API GA, rolling out Cloudflare Snippets, SWR, and bringing Workers for Platforms to all users</span></a></td>
    <td><span>Browser Rendering API is now available to all paid Workers customers with improved session management.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/cloudflare-acquires-baselime-expands-observability-capabilities"><span>Cloudflare acquires Baselime to expand serverless application observability capabilities</span></a></td>
    <td><span>We announced that Cloudflare has acquired Baselime, a serverless observability company.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/cloudflare-acquires-partykit"><span>Cloudflare acquires PartyKit to allow developers to build real-time multi-user applications</span></a></td>
    <td><span>We announced that PartyKit, a trailblazer in enabling developers to craft ambitious real-time, collaborative, multiplayer applications, is now a part of Cloudflare. This acquisition marks a significant milestone in our journey to redefine the boundaries of serverless computing, making it more dynamic, interactive, and, importantly, stateful.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/blazing-fast-development-with-full-stack-frameworks-and-cloudflare"><span>Blazing fast development with full-stack frameworks and Cloudflare</span></a></td>
    <td><span>Full-stack web development with Cloudflare is now faster and easier! 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. Let’s dev together!</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/javascript-native-rpc"><span>We've added JavaScript-native RPC to Cloudflare Workers</span></a></td>
    <td><span>Cloudflare Workers now features a built-in RPC (Remote Procedure Call) system for use in Worker-to-Worker and Worker-to-Durable Object communication, with absolutely minimal boilerplate. We've designed an RPC system so expressive that calling a remote service can feel like using a library.</span></td>
  </tr>
  <tr>
    <td><a href="http://staging.blog.mrk.cfdata.org/2024-community-update"><span>Community Update: empowering startups building on Cloudflare and creating an inclusive community</span></a></td>
    <td><span>We closed out Developer Week by sharing updates on our Workers Launchpad program, our latest Developer Challenge, and the work we’re doing to ensure our community spaces – like our Discord and Community forums – are safe and inclusive for all developers.</span></td>
  </tr>
</tbody>
</table><p>Here's a video summary, by Craig Dennis, Developer Educator, AI:</p><blockquote><p>🏃<a href="https://twitter.com/CloudflareDev?ref_src=twsrc%5Etfw">@CloudflareDev</a> Developer Week 2024 🧡 ICYMI 🧡 Speed run <a href="https://t.co/0uzPJshC93">pic.twitter.com/0uzPJshC93</a></p>— Craig Dennis (@craigsdennis) <a href="https://twitter.com/craigsdennis/status/1778875721575989734?ref_src=twsrc%5Etfw">April 12, 2024</a></blockquote> 
    <div>
      <h3>Continue the conversation</h3>
      <a href="#continue-the-conversation">
        
      </a>
    </div>
    <p>Thank you for being a part of Developer Week! Want to continue the conversation and share what you’re building? Join us on <a href="https://discord.com/invite/cloudflaredev">Discord</a>. To get started building on Workers, check out our <a href="https://developers.cloudflare.com/workers/">developer documentation</a>.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Rate Limiting]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[D1]]></category>
            <category><![CDATA[Connectivity Cloud]]></category>
            <guid isPermaLink="false">VNnYecAmN7CpST4nBbas0</guid>
            <dc:creator>Phillip Jones</dc:creator>
        </item>
        <item>
            <title><![CDATA[Announcing Pages support for monorepos, wrangler.toml, database integrations and more!]]></title>
            <link>https://blog.cloudflare.com/pages-workers-integrations-monorepos-nextjs-wrangler/</link>
            <pubDate>Thu, 04 Apr 2024 13:00:16 GMT</pubDate>
            <description><![CDATA[ Today, we’re launching four improvements to Pages that bring functionality previously restricted to Workers, with the goal of unifying the development experience between the two.  Support for monorepos, wrangler.toml, new additions to Next.js support and database integrations ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Pages <a href="/cloudflare-pages-ga">launched</a> in 2021 with the goal of empowering developers to go seamlessly from idea to production. With <a href="https://developers.cloudflare.com/pages/get-started/git-integration/#configure-your-deployment">built-in CI/CD</a>, <a href="https://developers.cloudflare.com/pages/configuration/preview-deployments/">Preview Deployments</a>, <a href="https://developers.cloudflare.com/pages/configuration/git-integration/">integration with GitHub and GitLab</a>, and support for all the most popular <a href="https://developers.cloudflare.com/pages/framework-guides/">JavaScript frameworks</a>, Pages lets you build and deploy both static and full-stack apps globally to our network in seconds.</p><p>Pages has superpowers like these that Workers does not have, and vice versa. Today you have to choose upfront whether to build a Worker or a Pages project, even though the two products largely overlap. That’s why during 2023’s <a href="/pages-and-workers-are-converging-into-one-experience">Developer Week</a>, we started bringing both products together to give developers the benefit of the best of both worlds. And it’s why we announced that like Workers, Pages projects can now directly access <a href="https://developers.cloudflare.com/workers/configuration/bindings/">bindings</a> to Cloudflare services — using <a href="https://github.com/cloudflare/workerd">workerd</a> under-the-hood — even when using the local development server provided by a full-stack framework like <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-an-astro-site/">Astro,</a> <a href="https://developers.cloudflare.com/pages/framework-guides/nextjs/deploy-a-nextjs-site/">Next.js,</a> <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-nuxt-site/">Nuxt,</a> <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-qwik-site/">Qwik,</a> <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-remix-site/">Remix,</a> <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-solid-site/">SolidStart, or</a> <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site/">SvelteKit</a>. Today, we’re thrilled to be launching some new improvements to Pages that bring functionality previously restricted to Workers. Welcome to the stage: monorepos, wrangler.toml, new additions to Next.js support, and database integrations!</p>
    <div>
      <h3>Pages now supports monorepos</h3>
      <a href="#pages-now-supports-monorepos">
        
      </a>
    </div>
    <p>Many development teams use monorepos – repositories that contain multiple apps, with each residing in its own subdirectory. This approach is extremely helpful when these apps share code.</p><p>Previously, the Pages CI/CD set-up limited users to one repo per project. To use a monorepo with Pages, you had to <a href="https://developers.cloudflare.com/pages/get-started/direct-upload/">directly upload it</a> on your own, using the Wrangler CLI. If you did this, you couldn’t use Pages’ integration with GitHub or Gitlab, or have Pages CI/CD handle builds and deployments. With Pages support for monorepos, development teams can trigger builds to their various projects with each push.</p><p><b>Manage builds and move fast</b>You can now include and exclude specific paths to watch for in each of your projects to avoid unnecessary builds from commits to your repo.</p><p>Let’s say a monorepo contains 4 subdirectories – a marketing app, an ecommerce app, a design library, and a package. The marketing app depends on the design library, while the ecommerce app depends on the design library and the package.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/435MhqmGH7CJ4dXIvMEbN2/2ec159aecf92ea18f5b686b75024484d/image3-7.png" />
            
            </figure><p>Updates to the design library should rebuild and redeploy both applications, but an update to the marketing app shouldn’t rebuild and deploy the ecommerce app. However, by default, any push you make to my-monorepo triggers a build for both projects regardless of which apps were changed. Using the include/exclude build controls, you can specify paths to build and ignore for your project to help you track dependencies and build more efficiently.</p><p><b>Bring your own tools</b>Already using tools like <a href="https://turbo.build/">Turborepo</a>, <a href="https://nx.dev/">NX</a>, and <a href="https://lerna.js.org/">Lerna</a>? No problem! You can also bring your favorite <a href="https://developers.cloudflare.com/pages/configuration/monorepos/#monorepo-management-tools">monorepo management tooling</a> to Pages to help manage your dependencies quickly and efficiently.</p><p>Whatever your tooling and however you’re set up, check out our <a href="https://developers.cloudflare.com/pages/configuration/monorepos/">documentation</a> to get started with your monorepo right out of the box.</p>
    <div>
      <h3>Configure Pages projects with wrangler.toml</h3>
      <a href="#configure-pages-projects-with-wrangler-toml">
        
      </a>
    </div>
    <p>Today, we’re excited to announce that you can now configure Pages projects using wrangler.toml — the same configuration file format that is already used for configuring Workers.</p><p>Previously, Pages projects had to be configured exclusively in the dashboard. This forced you to context switch from your development environment any time you made a configuration change, like adding an environment variable or <a href="https://developers.cloudflare.com/workers/configuration/bindings/">binding</a>. It also separated configuration from code, making it harder to know things like what bindings are being used in your project. If you were developing as a team, all the users on your team had to have access to your account to make changes – even if they had access to make changes to the source code via your repo.</p><p>With wrangler.toml, you can:</p><ul><li><p><b>Store your configuration file in source control.</b> Keep your configuration in your repo alongside the rest of your code.</p></li><li><p><b>Edit your configuration via your code editor.</b> Remove the need to switch back and forth between interfaces.</p></li><li><p><b>Write configuration that is shared across environments.</b> Define bindings and environment variables for local, preview, and production in one file.</p></li><li><p><b>Ensure better access control.</b> By using a configuration file in your repo, you can control who has access to make changes without giving access to your Cloudflare dashboard.</p></li></ul><p><b>Migrate existing projects</b>If you have an existing Pages project, we’ve added a new Wrangler CLI command that downloads your existing configuration and provides you with a valid <code>wrangler.toml</code> file.</p>
            <pre><code>$ npx wrangler@latest pages download config &lt;PROJECT_NAME&gt;</code></pre>
            <p>Run this command, add the wrangler.toml file that it generates to your project’s root directory, and then when you deploy, your project will be configured based on this configuration file.</p><p>If you are already using wrangler.toml to define your local development configuration, you can continue doing so. By default, your existing wrangler.toml file will continue to only apply to local development. When you run <code>wrangler pages deploy</code>, Wrangler will show you the additional fields that you must add in order for your configuration to apply to production and preview environments. Add these fields to your wrangler.toml, and then when you deploy your changes, the configuration you’ve defined in wrangler.toml will be used by your Pages project.</p><p>Refer to the <a href="https://developers.cloudflare.com/pages/functions/wrangler-configuration/">documentation</a> for more information on exactly what’s supported and how to leverage wrangler.toml in your development workflows.</p>
    <div>
      <h3>Integrate Pages projects with your favorite database</h3>
      <a href="#integrate-pages-projects-with-your-favorite-database">
        
      </a>
    </div>
    <p>You can already connect to <a href="https://developers.cloudflare.com/d1/">D1</a>, Cloudflare’s <a href="https://www.cloudflare.com/developer-platform/products/d1/">serverless SQL database</a>, directly from Pages projects. And you can connect directly to your existing PostgreSQL database using <a href="https://developers.cloudflare.com/hyperdrive/">Hyperdrive</a>. Today, we’re making it even easier for you to connect 3rd party databases to Pages with just a couple of clicks. Pages now integrates directly with <a href="https://developers.cloudflare.com/workers/databases/native-integrations/neon/">Neon</a>, <a href="https://developers.cloudflare.com/workers/databases/native-integrations/planetscale/">PlanetScale</a>, <a href="https://developers.cloudflare.com/workers/databases/native-integrations/supabase/">Supabase</a>, <a href="https://developers.cloudflare.com/workers/databases/native-integrations/turso/">Turso</a>, <a href="https://developers.cloudflare.com/workers/databases/native-integrations/upstash/">Upstash</a>, and <a href="https://developers.cloudflare.com/workers/databases/native-integrations/xata/">Xata</a>!</p><p>Simply navigate to your Pages project’s settings, select your database provider, and we’ll add <a href="https://developers.cloudflare.com/pages/functions/bindings/#environment-variableshttps://developers.cloudflare.com/pages/functions/bindings/#environment-variables">environment variables</a> with credentials needed to connect as well a <a href="https://developers.cloudflare.com/pages/functions/bindings/#secrets">secret</a> with the API key from the provider for you automatically.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2seOXZ1bXjWnBbHTV7zs4U/6e180084ddfcbcc3a661afdacb8b1dc9/image1-4.png" />
            
            </figure><p>Not ready to ship to production yet? You can deploy your changes to Pages’ preview environment alongside your staging database and test your deployment with its unique preview URL.</p><p><b>What’s coming up for integrations?</b>We’re just getting started with database integrations, with many more providers to come. In the future, we’re also looking to expand our integrations platform to include seamless set up when building other components of your app – think authentication and observability!</p><p>Want to bring your favorite tools to Cloudflare but don’t see the integration option? Want to build out your own integration?</p><p>Not only are we looking for <a href="https://docs.google.com/forms/d/e/1FAIpQLScUzm1bpWzR0SlJLGI80HchcAz9emPWG2lIXO107KNZTcfo-w/viewform">user input on new integrations</a> to add, but we’re also opening up the integrations platform to builders who want to submit their own products! We’ll be releasing step-by-step documentation and tooling to easily build and publish your own integration. If you’re interested in submitting your own integration, please fill out our <a href="https://docs.google.com/forms/d/e/1FAIpQLScUzm1bpWzR0SlJLGI80HchcAz9emPWG2lIXO107KNZTcfo-w/viewform">integration intake form</a> and we’ll be in touch!</p>
    <div>
      <h3>Improved Next.js Support for Pages</h3>
      <a href="#improved-next-js-support-for-pages">
        
      </a>
    </div>
    <p>With <a href="https://github.com/cloudflare/next-on-pages/releases">30 minor and patch releases</a> since the 1.0 launch of <a href="https://github.com/cloudflare/next-on-pages">next-on-pages</a> during Dev Week 2023, our <a href="https://nextjs.org/">Next.js</a> integration has been continuously maturing and keeping up with the evolution of Next.js. In addition to performance improvements, and compatibility and bug fixes, we released three significant improvements.</p><p>First, the <a href="https://eslint.org/">ESLint</a> plugin <a href="https://www.npmjs.com/package/eslint-plugin-next-on-pages">eslint-plugin-next-on-pages</a> is a great way to catch and fix compatibility issues as you are writing your code before you build and deploy applications. The plugin contains <a href="https://github.com/cloudflare/next-on-pages/tree/main/packages/eslint-plugin-next-on-pages/docs/rules">several rules</a> for the most common coding mistakes we see developers make, with more being added as we identify problematic scenarios.</p><p>Another noteworthy change is the addition of <a href="https://github.com/cloudflare/next-on-pages/blob/3846730c4a0d12/packages/next-on-pages/README.md#cloudflare-platform-integration">getRequestContext()</a> APIs, which provides you with access to Cloudflare-specific resources and metadata about the request currently being processed by your application, allowing for example you to take client’s location or browser preferences into account when generating a response.</p><p>Last but not least, we have completely <a href="https://developers.cloudflare.com/pages/framework-guides/nextjs/deploy-a-nextjs-site/">overhauled the local development workflow for Next.js</a> as well as other full-stack frameworks. Thanks to the new <a href="https://github.com/cloudflare/next-on-pages/tree/main/internal-packages/next-dev">setupDevPlatform()</a> API, you can now use the default development server <code>next dev</code>, with support for instant edit &amp; refresh experience, while also using D1, <a href="https://www.cloudflare.com/developer-platform/r2/">R2</a>, KV and other resources provided by the Cloudflare development platform. Want to take it for a quick spin? Use <a href="https://developers.cloudflare.com/pages/get-started/c3/">C3</a> to scaffold a new Next.js application with just one command.</p><p>To learn more about our Next.js integration, check out our <a href="https://developers.cloudflare.com/pages/framework-guides/nextjs/deploy-a-nextjs-site/">Next.js framework guide</a>.</p>
    <div>
      <h3>What’s next for the convergence of Workers and Pages?</h3>
      <a href="#whats-next-for-the-convergence-of-workers-and-pages">
        
      </a>
    </div>
    <p>While today’s launch represents just a few of the many upcoming additions to converge Pages and Workers, we also wanted to share a few milestones that are on the horizon, planned later in 2024</p><p><b>Pages features coming soon to Workers</b></p><ul><li><p><b>Workers CI/CD.</b> Later this year, we plan to bring the <a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-ci-cd/">CI/CD system</a> from Cloudflare Pages to Cloudflare Workers. Connect your repositories to Cloudflare and trigger builds for your Workers with every commit.</p></li><li><p><b>Serve static assets from Workers.</b> You will be able to deploy and serve static assets as part of Workers – just like you can with Pages today – and build Workers using full-stack frameworks! This will also extend to Workers for Platforms, allowing you to build platforms that let your customers deploy complete, full-stack applications that serve both dynamic and static assets.</p></li><li><p><b>Workers</b> <a href="https://developers.cloudflare.com/pages/configuration/preview-deployments"><b>preview URLs</b></a><b>.</b> Preview versions of your Workers with every change and share a unique URL with your team for testing.</p></li></ul><p><b>Workers features coming soon to Pages</b></p><ul><li><p><b>Add</b> <a href="https://developers.cloudflare.com/workers/observability/logging/tail-workers/"><b>Tail Workers</b></a> <b>to Pages projects.</b> Get observability into your Pages Functions by capturing <code>console.log()</code> messages, unhandled exceptions, and request metadata, and then forward the information to external destinations.</p></li><li><p><a href="https://developers.cloudflare.com/workers/observability/logging/logpush/"><b>Workers Trace Events Logpush</b></a><b>.</b> Push your Pages Functions logs to supported destinations like <a href="https://developers.cloudflare.com/r2/">R2</a>, Datadog, or any HTTP destination for long term storage, auditing, and compliance.</p></li><li><p><a href="https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/"><b>Gradual Deployments</b></a><b>.</b> Gradually deploy new versions of your Pages Function to reduce risk when making changes to critical applications.</p></li></ul><p>You might also notice that the Pages and Workers interfaces in the Cloudflare Dash will begin to look more similar through the rest of this year. These changes aren’t just superficial, or us porting over functionality from one product to another. Under-the-hood, we are unifying the way that Workers and Pages projects are composed and then deployed to our network, ensuring that as we add new products and features, they can work with both Pages and Workers on day one.</p><p>In the meantime, bring your monorepo, a wrangler.toml, and your favorite databases to Pages and let’s rock! Be sure to show off what you’ve built in the <a href="https://discord.cloudflare.com/">Cloudflare Developer Discord</a> or by giving us a shout at <a href="https://twitter.com/CloudflareDev">@CloudflareDev</a>.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Wrangler]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <guid isPermaLink="false">4P2K139AXugqOLE4sR5wIu</guid>
            <dc:creator>Nevi Shah</dc:creator>
            <dc:creator>Adam Murray</dc:creator>
            <dc:creator>Igor Minar</dc:creator>
        </item>
        <item>
            <title><![CDATA[Building secure websites: a guide to Cloudflare Pages and Turnstile Plugin]]></title>
            <link>https://blog.cloudflare.com/guide-to-cloudflare-pages-and-turnstile-plugin/</link>
            <pubDate>Thu, 07 Mar 2024 14:00:52 GMT</pubDate>
            <description><![CDATA[ Learn how to use Cloudflare Pages and Turnstile to deploy your website quickly and easily while protecting it from bots, without compromising user experience. Follow our tutorial here for a seamless integration ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2K7WFOjmj3tTCZ2C90XOzp/1fd9487aaaf44a7b6f853f82f9e2be5f/image1-26.png" />
            
            </figure><p>Balancing developer velocity and security against bots is a constant challenge. Deploying your changes as quickly and easily as possible is essential to stay ahead of your (or your customers’) needs and wants. Ensuring your website is safe from malicious bots — without degrading user experience with alien hieroglyphics to decipher just to prove that you are a human — is no small feat. With <a href="https://pages.cloudflare.com/">Pages</a> and <a href="https://developers.cloudflare.com/turnstile/">Turnstile</a>, we'll walk you through just how easy it is to have the best of both worlds!</p><p><a href="https://pages.cloudflare.com/">Cloudflare Pages</a> offer a seamless platform for deploying and scaling your websites with ease. You can get started right away with configuring your websites with a quick integration using your git provider, and get set up with unlimited requests, bandwidth, collaborators, and projects.</p><p>Cloudflare Turnstile is Cloudflare’s <a href="https://www.cloudflare.com/products/turnstile/">CAPTCHA alternative solution</a> where your users don’t ever have to solve another puzzle to get to your website, no more stop lights and fire hydrants. You can protect your site without having to put your users through an annoying user experience. If you are already using another CAPTCHA service, we have made it easy for you to <a href="https://developers.cloudflare.com/turnstile/migration/">migrate over to Turnstile</a> with minimal effort needed. Check out the <a href="https://developers.cloudflare.com/turnstile/">Turnstile documentation</a> to get started.</p>
    <div>
      <h3>Alright, what are we building?</h3>
      <a href="#alright-what-are-we-building">
        
      </a>
    </div>
    <p>In this tutorial, we'll walk you through integrating Cloudflare Pages with Turnstile to secure your website against bots. You'll learn how to deploy Pages, embed the Turnstile widget, validate the token on the server side, and monitor Turnstile analytics. Let’s build upon <a href="https://developers.cloudflare.com/pages/tutorials/forms/">this tutorial</a> from Cloudflare’s developer docs, which outlines how to create an HTML form with Pages and Functions. We’ll also show you how to secure it by integrating with Turnstile, complete with client-side rendering and server-side validation, using the <a href="https://developers.cloudflare.com/pages/functions/plugins/turnstile/">Turnstile Pages Plugin</a>!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4DaVCMwbVOKW17aOgthwZh/f756f3466f64bc4f889a5b9ea497b910/Screenshot-2024-03-07-at-11.52.43.png" />
            
            </figure>
    <div>
      <h3>Step 1: Deploy your Pages</h3>
      <a href="#step-1-deploy-your-pages">
        
      </a>
    </div>
    <p>On the Cloudflare Dashboard, select your account and go to Workers &amp; Pages to create a new Pages application with your git provider. Choose the repository where you cloned the tutorial project or any other repository that you want to use for this walkthrough.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5kZP90IyFiaZzl7fX608Dl/355af9d8c998c19e0c032df65d14c324/image2-23.png" />
            
            </figure><p>The Build settings for this project is simple:</p><ul><li><p><b>Framework preset</b>: None</p></li><li><p><b>Build command</b>: npm install @cloudflare/pages-plugin-turnstile</p></li><li><p><b>Build output directory</b>: public</p></li></ul><p>Once you select “Save and Deploy”, all the magic happens under the hood and voilà! The form is already deployed.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6I301dY7rRKreWxUwEuHs/7ea859c666d3c04b39c139c47d3b1c7f/image8-3.png" />
            
            </figure>
    <div>
      <h3>Step 2: Embed Turnstile widget</h3>
      <a href="#step-2-embed-turnstile-widget">
        
      </a>
    </div>
    <p>Now, let’s navigate to Turnstile and add the newly created Pages site.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5BadX97G4W7s5FUuMx82MV/891f196d7f6840d7773684c13d9173ab/image5-14.png" />
            
            </figure><p>Here are the widget configuration options:</p><ul><li><p><b>Domain</b>: All you need to do is add the domain for the Pages application. In this example, it’s “pages-turnstile-demo.pages.dev”. For each deployment, Pages generates a <a href="https://developers.cloudflare.com/pages/configuration/preview-deployments/">deployment specific preview</a> subdomain. Turnstile covers all subdomains automatically, so your Turnstile widget will work as expected even in your previews. This is covered more extensively in our <a href="https://developers.cloudflare.com/turnstile/reference/domain-management/">Turnstile domain management documentation</a>.</p></li><li><p><b>Widget Mode</b>: There are three types of <a href="https://developers.cloudflare.com/turnstile/reference/widget-types/">widget modes</a> you can choose from.</p></li><li><p><b>Managed</b>: This is the recommended option where Cloudflare will decide when further validation through the checkbox interaction is required to confirm whether the user is a human or a bot. This is the mode we will be using in this tutorial.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78AnsttFltsSDTWMXxFjnH/d1ee443938d095a4d44874607c046760/Screenshot-2024-03-07-at-11.54.30.png" />
            
            </figure><ul><li><p><b>Non-interactive</b>: This mode does not require the user to interact and check the box of the widget. It is a non-intrusive mode where the widget is still visible to users but requires no added step in the user experience.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2EAIzU3P7JhQKFteYXb4Hq/1b39212a5393124a081bef94c7d9a07d/Screenshot-2024-03-07-at-11.55.00.png" />
            
            </figure><ul><li><p><b>Invisible</b>: Invisible mode is where the widget is not visible at all to users and runs in the background of your website.</p></li><li><p><b>Pre-Clearance setting</b>: With a clearance cookie issued by the Turnstile widget, you can configure your website to verify every single request or once within a session. To learn more about <a href="/integrating-turnstile-with-the-cloudflare-waf-to-challenge-fetch-requests">implementing pre-clearance with Turnstile</a>, check out this blog post.</p></li></ul><p>Once you create your widget, you will be given a sitekey and a secret key. The sitekey is public and used to invoke the Turnstile widget on your site. The secret key should be stored safely for security purposes.</p><p>Let’s embed the widget above the Submit button. Your index.html should look like this:</p>
            <pre><code>&lt;!doctype html&gt;
&lt;html lang="en"&gt;
	&lt;head&gt;
		&lt;meta charset="utf8"&gt;
		&lt;title&gt;Cloudflare Pages | Form Demo&lt;/title&gt;
		&lt;meta name="theme-color" content="#d86300"&gt;
		&lt;meta name="mobile-web-app-capable" content="yes"&gt;
		&lt;meta name="apple-mobile-web-app-capable" content="yes"&gt;
		&lt;meta name="viewport" content="width=device-width,initial-scale=1"&gt;
		&lt;link rel="icon" type="image/png" href="https://www.cloudflare.com/favicon-128.png"&gt;
		&lt;link rel="stylesheet" href="/index.css"&gt;
		&lt;script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=_turnstileCb" defer&gt;&lt;/script&gt;
	&lt;/head&gt;
	&lt;body&gt;

		&lt;main&gt;
			&lt;h1&gt;Demo: Form Submission&lt;/h1&gt;

			&lt;blockquote&gt;
				&lt;p&gt;This is a demonstration of Cloudflare Pages with Turnstile.&lt;/p&gt;
				&lt;p&gt;Pages deployed a &lt;code&gt;/public&lt;/code&gt; directory, containing a HTML document (this webpage) and a &lt;code&gt;/functions&lt;/code&gt; directory, which contains the Cloudflare Workers code for the API endpoint this &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; references.&lt;/p&gt;
				&lt;p&gt;&lt;b&gt;NOTE:&lt;/b&gt; On form submission, the API endpoint responds with a JSON representation of the data. There is no JavaScript running in this example.&lt;/p&gt;
			&lt;/blockquote&gt;

			&lt;form method="POST" action="/api/submit"&gt;
				&lt;div class="input"&gt;
					&lt;label for="name"&gt;Full Name&lt;/label&gt;
					&lt;input id="name" name="name" type="text" /&gt;
				&lt;/div&gt;

				&lt;div class="input"&gt;
					&lt;label for="email"&gt;Email Address&lt;/label&gt;
					&lt;input id="email" name="email" type="email" /&gt;
				&lt;/div&gt;

				&lt;div class="input"&gt;
					&lt;label for="referers"&gt;How did you hear about us?&lt;/label&gt;
					&lt;select id="referers" name="referers"&gt;
						&lt;option hidden disabled selected value&gt;&lt;/option&gt;
						&lt;option value="Facebook"&gt;Facebook&lt;/option&gt;
						&lt;option value="Twitter"&gt;Twitter&lt;/option&gt;
						&lt;option value="Google"&gt;Google&lt;/option&gt;
						&lt;option value="Bing"&gt;Bing&lt;/option&gt;
						&lt;option value="Friends"&gt;Friends&lt;/option&gt;
					&lt;/select&gt;
				&lt;/div&gt;

				&lt;div class="checklist"&gt;
					&lt;label&gt;What are your favorite movies?&lt;/label&gt;
					&lt;ul&gt;
						&lt;li&gt;
							&lt;input id="m1" type="checkbox" name="movies" value="Space Jam" /&gt;
							&lt;label for="m1"&gt;Space Jam&lt;/label&gt;
						&lt;/li&gt;
						&lt;li&gt;
							&lt;input id="m2" type="checkbox" name="movies" value="Little Rascals" /&gt;
							&lt;label for="m2"&gt;Little Rascals&lt;/label&gt;
						&lt;/li&gt;
						&lt;li&gt;
							&lt;input id="m3" type="checkbox" name="movies" value="Frozen" /&gt;
							&lt;label for="m3"&gt;Frozen&lt;/label&gt;
						&lt;/li&gt;
						&lt;li&gt;
							&lt;input id="m4" type="checkbox" name="movies" value="Home Alone" /&gt;
							&lt;label for="m4"&gt;Home Alone&lt;/label&gt;
						&lt;/li&gt;
					&lt;/ul&gt;
				&lt;/div&gt;
				&lt;div id="turnstile-widget" style="padding-top: 20px;"&gt;&lt;/div&gt;
				&lt;button type="submit"&gt;Submit&lt;/button&gt;
			&lt;/form&gt;
		&lt;/main&gt;
	&lt;script&gt;
	// This function is called when the Turnstile script is loaded and ready to be used.
	// The function name matches the "onload=..." parameter.
	function _turnstileCb() {
	    console.debug('_turnstileCb called');

	    turnstile.render('#turnstile-widget', {
	      sitekey: '0xAAAAAAAAAXAAAAAAAAAAAA',
	      theme: 'light',
	    });
	}
	&lt;/script&gt;
	&lt;/body&gt;
&lt;/html&gt;</code></pre>
            <p>You can embed the Turnstile widget implicitly or explicitly. In this tutorial, we will explicitly embed the widget by injecting the JavaScript tag and related code, then specifying the placement of the widget.</p>
            <pre><code>&lt;script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=_turnstileCb" defer&gt;&lt;/script&gt;</code></pre>
            
            <pre><code>&lt;script&gt;
	function _turnstileCb() {
	    console.debug('_turnstileCb called');

	    turnstile.render('#turnstile-widget', {
	      sitekey: '0xAAAAAAAAAXAAAAAAAAAAAA',
	      theme: 'light',
	    });
	}
&lt;/script&gt;</code></pre>
            <p>Make sure that the <code>div id</code> you assign is the same as the <code>id</code> you specify in <code>turnstile.render</code> call. In this case, let’s use <code>“turnstile-widget”</code>. Once that’s done, you should see the widget show up on your site!</p>
            <pre><code>&lt;div id="turnstile-widget" style="padding-top: 20px;"&gt;&lt;/div&gt;</code></pre>
            
    <div>
      <h3>Step 3: Validate the token</h3>
      <a href="#step-3-validate-the-token">
        
      </a>
    </div>
    <p>Now that the Turnstile widget is rendered on the front end, let’s validate it on the server side and check out the Turnstile outcome. We need to make a call to the <code>/siteverify</code> API with the token in the submit function under <code>./functions/api/submit.js</code>.</p><p>First, grab the token issued from Turnstile under cf-turnstile-response. Then, call the <code>/siteverify</code> API to ensure that the token is valid. In this tutorial, we’ll attach the Turnstile outcome to the response to verify everything is working well. You can decide on the expected behavior and where to direct the user based on the <code>/siteverify</code> response.</p>
            <pre><code>/**
 * POST /api/submit
 */

import turnstilePlugin from "@cloudflare/pages-plugin-turnstile";

// This is a demo secret key. In prod, we recommend you store
// your secret key(s) safely. 
const SECRET_KEY = '0x4AAAAAAASh4E5cwHGsTTePnwcPbnFru6Y';

export const onRequestPost = [
    turnstilePlugin({
    	secret: SECRET_KEY,
    }),
    (async (context) =&gt; {
    	// Request has been validated as coming from a human
    	const formData = await context.request.formData()

    	var tmp, outcome = {};
	for (let [key, value] of formData) {
		tmp = outcome[key];
		if (tmp === undefined) {
			outcome[key] = value;
		} else {
			outcome[key] = [].concat(tmp, value);
		}
	}

	// Attach Turnstile outcome to the response
	outcome["turnstile_outcome"] = context.data.turnstile;

	let pretty = JSON.stringify(outcome, null, 2);

      	return new Response(pretty, {
      		headers: {
      			'Content-Type': 'application/json;charset=utf-8'
      		}
      	});
    })
];</code></pre>
            <p>Since Turnstile accurately decided that the visitor was not a bot, the response for “success” is “true” and “interactive” is “false”. The “interactive” being “false” means that the checkbox was automatically checked by Cloudflare as the visitor was determined to be human. The user was seamlessly allowed access to the website without having to perform any additional actions. If the visitor looks suspicious, Turnstile will become interactive, requiring the visitor to actually click the checkbox to verify that they are not a bot. We used the managed mode in this tutorial but depending on your application logic, you can choose the widget mode that works best for you.</p>
            <pre><code>{
  "name": "Sally Lee",
  "email": "sallylee@cloudflare.com",
  "referers": "Facebook",
  "movies": "Space Jam",
  "cf-turnstile-response": "0._OHpi7JVN7Xz4abJHo9xnK9JNlxKljOp51vKTjoOi6NR4ru_4MLWgmxt1rf75VxRO4_aesvBvYj8bgGxPyEttR1K2qbUdOiONJUd5HzgYEaD_x8fPYVU6uZPUCdWpM4FTFcxPAnqhTGBVdYshMEycXCVBqqLVdwSvY7Me-VJoge7QOStLOtGgQ9FaY4NVQK782mpPfgVujriDAEl4s5HSuVXmoladQlhQEK21KkWtA1B6603wQjlLkog9WqQc0_3QMiBZzZVnFsvh_NLDtOXykOFK2cba1mLLcADIZyhAho0mtmVD6YJFPd-q9iQFRCMmT2Sz00IToXz8cXBGYluKtxjJrq7uXsRrI5pUUThKgGKoHCGTd_ufuLDjDCUE367h5DhJkeMD9UsvQgr1MhH3TPUKP9coLVQxFY89X9t8RAhnzCLNeCRvj2g-GNVs4-MUYPomd9NOcEmSpklYwCgLQ.jyBeKkV_MS2YkK0ZRjUkMg.6845886eb30b58f15de056eeca6afab8110e3123aeb1c0d1abef21c4dd4a54a1",
  "turnstile_outcome": {
    "success": true,
    "error-codes": [],
    "challenge_ts": "2024-02-28T22:52:30.009Z",
    "hostname": "pages-turnstile-demo.pages.dev",
    "action": "",
    "cdata": "",
    "metadata": {
      "interactive": false
    }
  }
}</code></pre>
            
    <div>
      <h3>Wrapping up</h3>
      <a href="#wrapping-up">
        
      </a>
    </div>
    <p>Now that we've set up Turnstile, we can head to Turnstile analytics in the <a href="https://dash.cloudflare.com/?to=/:account/turnstile">Cloudflare Dashboard</a> to monitor the <a href="https://developers.cloudflare.com/turnstile/reference/tooltips/">solve rate</a> and widget traffic. Visitor Solve Rate indicates the percentage of visitors who successfully completed the Turnstile widget. A sudden drop in the Visitor Solve Rate could indicate an increase in bot traffic, as bots may fail to complete the challenge presented by the widget. API Solve Rate measures the percentage of visitors who successfully validated their token against the <code>/siteverify</code> API. Similar to the Visitor Solve Rate, a significant drop in the API Solve Rate may indicate an increase in bot activity, as bots may fail to validate their tokens. Widget Traffic provides insights into the nature of the traffic hitting your website. A high number of challenges requiring interaction may suggest that bots are attempting to access your site, while a high number of unsolved challenges could indicate that the Turnstile widget is effectively blocking suspicious traffic.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6LS7efg5RG5VVEE9VVh41n/0c4f7feaeb2c693ea47df2229efa2091/image6-4.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4rZfKrfY91tTRuCvJgFFHU/3a689f0b7a066d2d85fb09725859bd20/image9-1.png" />
            
            </figure><p>And that’s it! We’ve walked you through how to easily secure your Pages with Turnstile. Pages and Turnstile are currently available for free for every Cloudflare user to get started right away. If you are looking for a seamless and speedy developer experience to get a secure website up and running, protected by Turnstile, head over to the <a href="https://dash.cloudflare.com/?to=/:account/turnstile">Cloudflare Dashboard</a> today!</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[Turnstile]]></category>
            <category><![CDATA[CAPTCHA]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Bots]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">1XOUeKfqYNFTbbeERuAxAg</guid>
            <dc:creator>Sally Lee</dc:creator>
        </item>
        <item>
            <title><![CDATA[Race ahead with Cloudflare Pages build caching]]></title>
            <link>https://blog.cloudflare.com/race-ahead-with-build-caching/</link>
            <pubDate>Thu, 28 Sep 2023 13:00:57 GMT</pubDate>
            <description><![CDATA[ Unleash the fast & furious in your builds with Cloudflare Pages' build caching. Reduce build times by caching previously computed project components. Now in Beta for select frameworks and package managers. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we are thrilled to release a beta of Cloudflare Pages support for build caching! With build caching, we are offering a supercharged Pages experience by helping you cache parts of your project to save time on subsequent builds.</p><p>For developers, time is not just money – it’s innovation and progress. When every second counts in crunch time before a new launch, the “need for speed” becomes <i>critical</i>. With Cloudflare Pages’ built-in <a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-ci-cd/">continuous integration and continuous deployment (CI/CD)</a>, developers count on us to drive fast. We’ve already taken great strides in making sure we’re enabling quick development iterations for our users by <a href="/cloudflare-pages-build-improvements/">making solid improvements on the stability and efficiency</a> of our build infrastructure. But we always knew there was more to our build story.</p>
    <div>
      <h3>Quick pit stops</h3>
      <a href="#quick-pit-stops">
        
      </a>
    </div>
    <p>Build times can feel like a developer's equivalent of a time-out, a forced pause in the creative process—the inevitable pit stop in a high-speed formula race.</p><p>Long build times not only breaks the flow of individual developers, but it can also create a ripple effect across the team. It can slow down iterations and push back deployments. In the fast-paced world of CI/CD, these delays can drastically impact productivity and the delivery of products.</p><p>We want to empower developers to <b>win the race</b>, miles ahead of competition.</p>
    <div>
      <h3>Mechanics of build caching</h3>
      <a href="#mechanics-of-build-caching">
        
      </a>
    </div>
    <p>At its core, build caching is a mechanism that stores artifacts of a build, allowing subsequent builds to reuse these artifacts rather than recomputing them from scratch. By leveraging the cached results, build times can be significantly reduced, leading to a more efficient build process.</p><p>Previously, when you initiated a build, the Pages CI system would generate every step of the build process, even if most parts of the codebase remain unchanged between builds. This is the equivalent to changing out every single part of the car during a pit stop, irrespective of if anything needs replacing.</p><p>Build caching refines this process. Now, the Pages build system will detect if cached artifacts can be leveraged, restore the artifacts, then focus on only computing the modified sections of the code. In essence, build caching acts like an experienced pit crew, smartly skipping unnecessary steps and focusing only on what's essential to get you back in the race faster.</p>
    <div>
      <h3>What are we caching?</h3>
      <a href="#what-are-we-caching">
        
      </a>
    </div>
    <p>It boils down to two components: dependencies and build output.</p><p>The Pages build system supports dependency caching for select package managers and build output caching for select frameworks. Check out our <a href="https://developers.cloudflare.com/pages/platform/build-caching">documentation</a> for more information on what’s currently supported and what’s coming up.</p><p>Let’s take a closer look at what exactly we are caching.</p><p><b>Dependencies:</b> upon initiating a build, the Pages CI system checks for cached artifacts from previous builds. If it identifies a cache hit for dependencies, it restores from cache to speed up dependency installation.</p><p><b>Build output:</b> if a cache hit for build output is identified, Pages will only build the changed assets. This approach enables the long awaited <i>incremental builds</i> for supported JavaScript frameworks.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4kqmUJuLrUGc7vtXbDc4X6/3f1440dbf1ad3acef20a2b99c18d6d28/image2-26.png" />
            
            </figure>
    <div>
      <h3>Ready, set … go!</h3>
      <a href="#ready-set-go">
        
      </a>
    </div>
    <p>Build caching is now in beta, and ready for you to test drive!</p><p>In this release, the feature will support the node-based package managers <a href="https://www.npmjs.com/">npm</a>, <a href="https://yarnpkg.com/">yarn</a>, <a href="https://pnpm.io/">pnpm</a>, as well as <a href="https://bun.sh/">Bun</a>. We’ve also ensured compatibility with the most popular frameworks that provide native incremental building support: <a href="https://www.gatsbyjs.com/">Gatsby.js</a>, <a href="https://nextjs.org/">Next.js</a> and <a href="https://astro.build/">Astro</a> – and more to come!</p><p>For you as a Pages user, interacting with build caching will be seamless. If you are working with an existing project, simply navigate to your project’s settings to toggle on Build Cache.</p><p>When you push a code change and initiate a build using Pages CI, build caching will kick-start and do its magic in the background.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4hWz7Sh9wtk64c01cSnNjG/6967b9783a75f3fdfaaa10bd26884e0b/image4-17.png" />
            
            </figure>
    <div>
      <h3>“Cache” us on Discord</h3>
      <a href="#cache-us-on-discord">
        
      </a>
    </div>
    <p>Have questions? Join us on our <a href="https://discord.com/invite/cloudflaredev?event=1152163002502615050">Discord Server</a>. We will be hosting an “Ask Us Anything” <a href="https://discord.com/invite/cloudflaredev?event=1152163002502615050">session</a> on October 2nd where you can chat live with members of our team! Your feedback on this beta is invaluable to us, so after testing out build caching, don't hesitate to share your experiences! Happy building!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6lavvh2PfpjlEbNV0YEuGB/8104fcccf6bf1243dfa113e940317f82/image3-32.png" />
            
            </figure><p></p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Beta]]></category>
            <category><![CDATA[Speed]]></category>
            <guid isPermaLink="false">5NhsEJJxtKlKawPWJmHWJm</guid>
            <dc:creator>Anni Wang</dc:creator>
            <dc:creator>Jacob Hands</dc:creator>
            <dc:creator>John Fawcett</dc:creator>
        </item>
        <item>
            <title><![CDATA[How we scaled and protected Eurovision 2023 voting with Pages and Turnstile]]></title>
            <link>https://blog.cloudflare.com/how-cloudflare-scaled-and-protected-eurovision-2023-voting/</link>
            <pubDate>Fri, 23 Jun 2023 13:00:55 GMT</pubDate>
            <description><![CDATA[ More than 162 million fans tuned in to the 2023 Eurovision Song Contest, the first year that non-participating countries could also vote. Cloudflare helped scale and protect the voting application based.io, built by once.net using our rapid DNS infrastructure, CDN, Cloudflare Pages and Turnstile ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3EL1K1PkflEKz4BN5RvvVl/9caa639fcc20faba71edc840a70a6ad6/image3-27.png" />
            
            </figure><p>2023 was the first year that non-participating countries could vote for their favorites during the Eurovision Song Contest, adding millions of additional viewers and voters to an already impressive 162 million tuning in from the participating countries. It became a truly global event with a potential for disruption from multiple sources. To prepare for anything, Cloudflare helped scale and protect the voting application, used by millions of dedicated fans around the world to choose the winner.</p><p>In this blog we will cover how <a href="https://once.net">once.net</a> built their platform <a href="https://www.based.io/">based.io</a> to monitor, manage and scale the Eurovision voting application to handle all traffic using many Cloudflare services. The speed with which DNS changes made through the Cloudflare API propagate globally allowed them to scale their backend within seconds. At the same time, Cloudflare Pages was ready to serve any amount of traffic to the voting landing page so fans didn’t miss a beat. And to cap it off, by combining Cloudflare CDN, <a href="https://www.cloudflare.com/ddos/">DDoS protection</a>, WAF, and Turnstile, they made sure that attackers didn’t steal any of the limelight.</p>
    <div>
      <h3>The unsung heroes</h3>
      <a href="#the-unsung-heroes">
        
      </a>
    </div>
    <p>Based.io is a resilient live data platform built by the <a href="https://once.net">once.net</a> team, with the capability to scale up to 400 million concurrent connected users. It’s built from the ground up for speed and performance, consisting of an observable real time graph database, <a href="https://www.cloudflare.com/learning/network-layer/what-is-the-network-layer/">networking layer</a>, cloud functions, analytics and infrastructure orchestration. Since all system information, traffic analysis and disruptions are monitored in real time, it makes the platform instantly responsive to variable demand, which enables real time scaling of your infrastructure during spikes, outages and attacks.</p><p>Although the based.io platform on its own is currently in closed beta, it is already serving a few flagship customers in production assisted by the software and services of the once.net team. One such customer is Tally, a platform used by multiple broadcasters in Europe to add live interaction to traditional television. Over 100 live shows have been performed using the platform. Another is Airhub, a startup that handles and logs automatic drone flights. And of course the star of this blog post, the Eurovision Song Contest.</p>
    <div>
      <h3>Setting the stage</h3>
      <a href="#setting-the-stage">
        
      </a>
    </div>
    <p>The Eurovision Song Contest is one of the world’s most popular broadcasted contests, and this year it reached 162 million people across 38 broadcasting countries. In addition, on TikTok the three live shows were viewed 4.8 million times, while 7.6 million people watched the Grand Final live on YouTube. With such an audience, it is no surprise that Cloudflare sees the impact of it on the Internet. Last year, we wrote <a href="/eurovision-2022-internet-trends/">a blog post</a> where we showed lower than average traffic during, and higher than average traffic after the grand final. This year, the traffic from participating countries showed an even more remarkable surge:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3gO2U4Z3Qg4GOcNKZ6MIwe/d4fbd14cabdefb4e3764d7f4bc70c893/image1-39.png" />
            
            </figure><p>HTTP Requests per Second from Norway, with a similar pattern visible in countries such as the UK, Sweden and France. Internet traffic spiked at 21:20 UTC, when voting started.</p><p>Such large amounts of traffic are nothing new to the Eurovision Song Contest. Eurovision has relied on Cloudflare’s services for over a decade now and Cloudflare has helped to protect Eurovision.tv and improve its performance through noticeable faster load time to visitors from all corners of the world. Year after year, the team of Eurovision continued to use our services more, discovering additional features to improve performance and reliability further, with increasingly fine-grained control over their traffic flows. Eurovision.tv uses Page Rules to cache additional content on Cloudflare’s edge, speeding up delivery without sacrificing up-to-the-minute updates during the global event. Finally, to protect their backend and content management system, the team has placed their admin portals behind Cloudflare Zero Trust to delegate responsibilities down to individual levels.</p><p>Since then the contest itself has also evolved – sometimes by choice, sometimes by force. During the COVID-19 pandemic in-person cheering became impossible for many people due to a reduced live audience, resulting in the Eurovision Song Contest asking once.net to build a new iOS and Android application in which fans could cheer virtually. The feature was an instant hit, and it was clear that it would become part of this year’s contest as well.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3r33qBRnXFsgoKlqwH8Hly/5a087ff5295ac331344e548f2f7bd0ee/Screenshot-2023-06-23-at-12.05.08.png" />
            
            </figure><p>A screenshot of the official Eurovision Song Contest application showing the real-time number of connected fans (1) and allowing them to cheer (2) for their favorites.</p><p>In 2023, once.net was also asked to handle the paid voting from the regions where phone and SMS voting was not possible. It was the first time that Eurovision allowed voting online. The challenge that had to be overcome was the extreme peak demand on the platform when the show was live, and especially when the voting window started.</p><p>Complicating it further, was the fact that during last year’s show, there had been a large number of targeted and coordinated attacks.</p><p>To prepare for these spikes in demand and determined adversaries, once.net needed a platform that isn’t only resilient and highly scalable, but could also act as a mitigation layer in front of it. once.net selected Cloudflare for this functionality and integrated Cloudflare deeply with its <a href="https://www.cloudflare.com/application-services/solutions/app-performance-monitoring/">real-time monitoring</a> and management platform. To understand how and why, it’s essential to understand based.io underlying architecture.</p>
    <div>
      <h3>The based.io platform</h3>
      <a href="#the-based-io-platform">
        
      </a>
    </div>
    <p>Instead of relying on network or HTTP load balancers, based.io uses a client-side service discovery pattern, selecting the most suitable server to connect to and leveraging Cloudflare's fast cache propagation infrastructure to handle spikes in traffic (both malicious and benign).</p><p>First, each server continuously registers a unique access key that has an expiration of 15 seconds, which must be used when a client connects to the server. In addition, the backend servers register their health (such as active connections, CPU, memory usage, requests per second, etc.) to the service registry every 300 milliseconds. Clients then request the optimal server URL and associated access key from a central discovery registry and proceed to establish a long lived connection with that server. When a server gets overloaded it will disconnect a certain amount of clients and those clients will go through the discovery process again.</p><p>The central discovery registry would normally be a huge bottleneck and attack target. based.io resolves this by putting the registry behind Cloudflare's global network with a cache time of three seconds. Since the system relies on real-time stats to distribute load and uses short lived access keys, it is crucial that the cache updates fast and reliably. This is where Cloudflare’s infrastructure proved its worth, both due to the fast updating cache and reducing load with <a href="/introducing-regional-tiered-cache/">Tiered Caching</a>.</p><p>Not using <a href="https://www.cloudflare.com/learning/performance/what-is-load-balancing/">load balancers</a> means the based.io system allows clients to connect to the backend servers through Cloudflare, resulting in  better performance and a more resilient infrastructure by eliminating the load balancers as potential attack surface. It also results in a better distribution of connections, using the real-time information of server health, amount of active connections, active subscriptions.</p><p>Scaling up the platform happens automatically under load by deploying additional machines that can each handle 40,000 connected users. These are spun up in batches of a couple of hundred and as each machine spins up, it reaches out directly to the Cloudflare API to configure its own <a href="https://www.cloudflare.com/learning/dns/dns-records/">DNS record</a> and proxy status. Thanks to <a href="/dns-build-improvement/">Cloudflare’s high speed DNS system</a>, these changes are then propagated globally within seconds, resulting in a total machine turn-up time of around three seconds. This means faster discovery of new servers and faster dynamic rebalancing from the clients. And since the voting window of the Eurovision Song Contest is only 45 minutes, with the main peak within minutes after the window opens, every second counts!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Q8phs7FFGD11xvymotWjf/d911f35b6d8521dbad6f0e5fb27b6adb/image4-22.png" />
            
            </figure><p>High level architecture of the based.io platform used for the 2023 Eurovision Song Contest‌ ‌</p><p>To vote, users of the mobile app and viewers globally were pointed to the voting landing page, <a href="https://www.esc.vote">esc.vote</a>. Building a frontend web application able to handle this kind of an audience is a challenge in itself. Although hosting it yourself and putting a <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDN</a> in front seems straightforward, this still requires you to own, configure and manage your origin infrastructure. once.net decided to leverage Cloudflare’s infrastructure directly by hosting the voting landing page on Cloudflare Pages. Deploying was as quick as a commit to their Git repository, and they never had to worry about reachability or scaling of the webpage.</p><p>once.net also used <a href="/turnstile-private-captcha-alternative/">Cloudflare Turnstile</a> to protect their payment <a href="https://www.cloudflare.com/learning/security/api/what-is-api-endpoint/">API endpoints</a> that were used to validate online votes. They used the invisible Turnstile widget to make sure the request was not coming from emulated browsers (e.g. Selenium). And best of all, using the invisible Turnstile widget the user did not have to go through extra steps, which allowed for a better user experience and better conversion.</p>
    <div>
      <h3>Cloudflare Pages stealing the show!</h3>
      <a href="#cloudflare-pages-stealing-the-show">
        
      </a>
    </div>
    <p>After the two semi-finals went according to plan with approximately 200,000 concurrent users during each,May 13 brought the Grand Final. The once.net team made sure that there were enough machines ready to take the initial load, jumped on a call with Cloudflare to monitor and started looking at the number of concurrent users slowly increasing. During the event, there were a few attempts to <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS</a> the site, which were automatically and instantaneously mitigated without any noticeable impact to any visitors.</p><p>The based.io discovery registry server also got some attention. Since the cache TTL was set quite low at five seconds, a high rate of distributed traffic to it could still result in a significant load. Luckily, on its own, the highly optimized based.io server can already handle around 300,000 requests per second. Still, it was great to see that during the event the cache hit ratio for normal traffic was 20%, and during one significant attack the <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cache-hit-ratio/">cache hit ratio</a> peaked towards 80%. This showed how easy it is to leverage a combination of Cloudflare CDN and DDoS protection to mitigate such attacks, while still being able to serve dynamic and real time content.</p><p>When the curtains finally closed, 1.3 million concurrent users connected to the based.io platform at peak. The based.io platform handled a total of 350 million events and served seven million unique users in three hours. The voting landing page hosted by Cloudflare Pages served 2.3 million requests per second at peak, and made sure that the voting payments were by real human fans using Turnstile. Although the Cloudflare platform didn’t blink for such a flood of traffic, it is no surprise that it shows up as a short crescendo in our traffic statistics:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5huk3IzNOj2l5bBB9gyPJK/adbabc5972b3d2e713de82b42ab26803/image5-15.png" />
            
            </figure>
    <div>
      <h3>Get in touch with us</h3>
      <a href="#get-in-touch-with-us">
        
      </a>
    </div>
    <p>If you’re also working on or with an application that would benefit from Cloudflare’s speed and security, but don’t know where to start, reach <a href="https://www.cloudflare.com/plans/enterprise/contact/">out</a> and we’ll work together.</p> ]]></content:encoded>
            <category><![CDATA[Speed Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Turnstile]]></category>
            <category><![CDATA[Customers]]></category>
            <category><![CDATA[Customer Success]]></category>
            <category><![CDATA[DNS]]></category>
            <category><![CDATA[Speed]]></category>
            <category><![CDATA[Reliability]]></category>
            <guid isPermaLink="false">7jlSeSTqS7MOjIXIa5Bwy6</guid>
            <dc:creator>Dirk-Jan van Helmond</dc:creator>
            <dc:creator>Michiel Appelman</dc:creator>
            <dc:creator>Jim de Beer (Guest Author)</dc:creator>
        </item>
        <item>
            <title><![CDATA[Making Cloudflare Pages the fastest way to serve your sites]]></title>
            <link>https://blog.cloudflare.com/how-we-decreased-pages-latency/</link>
            <pubDate>Fri, 23 Jun 2023 13:00:37 GMT</pubDate>
            <description><![CDATA[ Pages is now the fastest way to serve your sites across Netlify, Vercel and many others and we’re so proud ]]></description>
            <content:encoded><![CDATA[ <p></p><p>In an era where visitors expect instant gratification and content on-demand, every millisecond counts. If you’re a web application developer, it’s an excellent time to be in this line of business, but with great power comes great responsibility. You’re tasked with creating an experience that is not only intuitive and delightful but also quick, reactive and responsive – sometimes with the two sides being at odds with each other. To add to this, if your business completely runs on the internet (say ecommerce), then your site’s Core Web Vitals could make or break your bottom line.</p><p>You don’t just need fast – you need <i>magic</i> fast. For the past two years, Cloudflare Pages has been serving up performant applications for users across the globe, but this week, we’re showing off our brand new, lightning fast architecture, decreasing the TTFB by up to 10X when serving assets.</p><p>And while a magician never reveals their secrets, this trick is too good to keep to ourselves. For all our application builders, we’re thrilled to share the juicy technical details on how we adopted Workers for Platforms — our extension of Workers to build SaaS businesses on top of — to make Pages one of the fastest ways to serve your sites.</p>
    <div>
      <h3>The problem</h3>
      <a href="#the-problem">
        
      </a>
    </div>
    <p>When we <a href="/cloudflare-pages-ga/">launched Pages in 2021</a>, we didn’t anticipate the exponential growth we would experience for our platform in the months and years to come. As our users began to adopt Pages into their development workflows, usage of our platform began to skyrocket. However, while riding the high of Pages’ success, we began to notice a problem – a rather large one. As projects grew in size, with every deployment came a pinch more latency, slowly affecting the end users visiting the Pages site. Customers with tens of thousands of deployments were at risk of introducing latency to their site – a problem that needed to be solved.</p><p>Before we dive into our technical solution, let’s first explore further the setup of Pages and the relationship between number of deployments and the observed latency.</p>
    <div>
      <h3>How could this be?</h3>
      <a href="#how-could-this-be">
        
      </a>
    </div>
    <p>Built on top of Cloudflare Workers, Pages serves static assets through a highly optimised Worker. We refer to this as the Asset Server Worker.</p><p>Users can also add dynamic content through Pages Functions which eventually get compiled into a separate Worker. Every single Pages deployment corresponds to unique instances of these Workers composed in a pipeline.</p><p>When a request hits Cloudflare we need to look up which pipeline to execute. As you’d expect, this is a function of the hostname in the URL.</p><p>If a user requested <a href="https://2b469e16.example.pages.dev/index.html">https://2b469e16.example.pages.dev/index.html</a>, the hostname is <a href="https://2b469e16.example.pages.dev/index.html">2b469e16.example.pages.dev</a> which is unique across all deployments on Pages — 2b469e16 is typically the commit hash and example in this case refers to the name of the project.</p><p>Every Pages project has its own routing table which is used to look up the pipeline to execute. The routing table happens to be a JSON object with a list of regexes for possible paths in that project (in our case, one for every deployment) and their corresponding pipelines.</p><p>The script_hash in the example below refers to the pipeline identifier. Naming is hard indeed.</p>
            <pre><code>{
 "filters": [
   {
     "pattern": "^(?:2b469e16.example.pages.dev(?:[:][0-9]+)?\\/(?&lt;p1&gt;.*))$",
     "script_hash": "..."
   },
   {
     "pattern": "^(?:example.pages.dev(?:[:][0-9]+)?\\/(?&lt;p1&gt;.*))$",
     "script_hash": "..."
   },
   {
     "pattern": "^(?:test.example.com(?:[:][0-9]+)?\\/(?&lt;p1&gt;.*))$",
     "script_hash": "..."
   }
 ],
 "v": 1
}</code></pre>
            <p>So to look up the pipeline in question, we would: download this JSON object from Quicksilver, parse it, and then iterate through this until it finds a regex that matches the current request.</p><p>Unsurprisingly, this is expensive. Let’s take a look at a quick real world example to see <i>how</i> expensive.</p><p>In one realistic case, it took us 107ms just to <i>parse</i> the JSON. The larger the JSON object gets, the more compute it takes to parse it — with tens of thousands of deployments (not unusual for very active projects that deploy immutable preview deployments for every git commit), this JSON could be several megabytes in size!</p><p>It doesn’t end there though. After parsing this, it took 29ms to then iterate and test several regexes to find the one that matched the current request.</p><p>To summarise, every single request to this project would take 136ms to just pick the right pipeline to execute. While this was the median case for projects with 10,000 deployments on average, we’ve seen projects with <b>seconds</b> in added latency making them unusable after 50,000 deployments, punishing users for using our platform.</p><p>Given most web sites load more than one asset for a page, this leads to timeouts and breakage leading to an unstable and unacceptable user experience.</p>
    <div>
      <h3>The secret sauce is Workers for Platforms</h3>
      <a href="#the-secret-sauce-is-workers-for-platforms">
        
      </a>
    </div>
    <p>We launched <a href="/workers-for-platforms/">Workers for Platforms</a> last year as a way to build ambitious platforms on top of Workers. Workers for Platforms lets one build complex pipelines where a request may be served by a Worker built and maintained by you but could then dispatch to a Worker written by a user of your platform. This allows your platform’s users to write their own Worker like they’ve been used to but while you control how and when they are executed.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/27P8dholN414dEiSnVKtUb/87156e682888a0a329799a756e0578ef/image4-23.png" />
            
            </figure><p>This isn’t very different from what we do with Pages. Users write their Pages functions which compile into a Worker. Users also upload their own static assets which are then bound to our special Asset Server Worker in unique pipelines for each of their deployments. And we control how and when which Worker gets executed based on a hostname in their URL.</p><p>Runtime lookups shouldn’t be O(n) though but O(1). Because Workers for Platforms was designed to build entire platforms on top of, lookups when trying to dispatch to a user’s Worker were designed as O(1) ensuring latency wasn’t a function of number of Workers in an account.</p>
    <div>
      <h3>The solution</h3>
      <a href="#the-solution">
        
      </a>
    </div>
    <p>By default, Workers for Platforms hashes the name of the Worker with a secret and uses that for lookups at runtime. However, because we need to dispatch by hostname, we had a different idea. At deployment time, we could hash the pipeline for the deployment by its hostname — <a href="https://2b469e16.example.pages.dev/index.html">2b469e16.example.pages.dev</a>, for example.</p><p>When a request comes in, we hash the hostname from the URL with our predefined secret and then use that value to look up the pipeline to execute. This entirely removes the necessity to fetch, parse and traverse the routing table JSON from before, now making our lookup O(1).</p><p>Once we were happy with our new setup from internal testing we wanted to onboard a real user. Our <a href="https://developers.cloudflare.com">Developer Docs</a> have been <a href="/new-dev-docs/">running on Pages</a> since the start of 2022 and during that time, we’ve dogfooded many different features and experiments. Due to the great relationship between our two teams and them being a sizable customer of ours we wanted to bring them onto our new Workers for Platform routing.</p><p>Before opting them in, TTFB was averaging at about 600ms.</p><p>After opting them in, TTFB is now 60ms. <a href="https://developers.cloudflare.com/analytics/web-analytics">Web Analytics</a> shows a noticeable drop in entire page load time as a result.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2hOpJu5iOcgDj5g7A9MR6W/690f8c5c96c4cc9a07f17d0a5d784472/Untitled.png" />
            
            </figure><p>This improvement was also visible through <a href="https://developer.chrome.com/en/docs/lighthouse/performance/performance-scoring/">Lighthouse scores</a> now approaching a perfect score of 100 instead of 78 which was the average we saw previously.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1DOMSoGft9tLCzg68wtXSN/bb3440d4f5e0c82a906dfb9793d71cfd/Untitled--1-.png" />
            
            </figure><p>The team was ecstatic about this especially given all of this happened under the hood with no downtime or required engineering team on their end. Not only is <a href="https://developers.cloudflare.com/">https://developers.cloudflare.com/</a> faster, we’re using less compute to serve it to all of you.</p>
    <div>
      <h3>The big migration</h3>
      <a href="#the-big-migration">
        
      </a>
    </div>
    <p>Migrating <a href="https://developers.cloudflare.com/">developers.cloudflare.com</a> was a big milestone for us and meant our new infrastructure was capable of handling traffic at scale. But a goal we were very certain of was migrating every Pages deployment ever created. We didn’t want to leave any users behind.</p><p>Turns out, that wasn’t a small number. There’d been over 14 million deployments so far over the years. This was about to be one of the biggest migrations we’d done to runtime assets and the risk was that we’d take down someone’s site.</p><p>We approached this migration with some key goals:</p><ul><li><p>Customer impact in terms of downtime was a no go, all of this needed to happen under the hood without anyone’s site being affected;</p></li><li><p>We needed the ability to A/B test the old and new setup so we could revert on a per site basis if something went wrong or was incompatible;</p></li><li><p>Migrations at this scale have the ability to cause incidents because they exceed the typical request capacity of our APIs in a short window so they need to run slowly;</p></li><li><p>Because this was likely to be a long running migration, we needed the ability to look at metrics and retry failures.</p></li></ul><p>The first step to all of this was to add the ability to A/B test between the legacy setup and the new one. To ensure we could A/B between the legacy setup and new one at any time, we needed to deploy both a regular pipeline (and updated routing table) and new Workers for Platforms hashed one for every deployment.</p><p>We also added a feature flag that allowed us to route to either the legacy setup or the new one per site or per data centre with the ability to explicitly opt out a site when an edgecase didn’t work.</p><p>With this setup, we started running our long running migration behind the scenes that duplicated every single deployment to the new Workers for Platforms enabled pipelines.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6cH3RPk39q50jgPD42nDej/d1ef38c399ffe3a8bb9a2e889863f267/unnamed--1-.png" />
            
            </figure><p>Duplicating them instead of replacing them meant that risk was low and A/B would be possible with the tradeoff of more cleanup after we finished but we picked that with reliability for users in mind.</p><p>A few days in after all 14 million deployments had finished migrating over, we started rollout to the new infrastructure with a percentage based rollout. This was a great way for us to find issues and ensure we were ready to serve all runtime traffic for Pages without the risk of an incident.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3UKjjt2upheD8aALjmDMT4/9634075b6f2d3d823ff7e257ed1e1f98/image--2-.png" />
            
            </figure>
    <div>
      <h3>Feeding three birds with one scone</h3>
      <a href="#feeding-three-birds-with-one-scone">
        
      </a>
    </div>
    <p>Alongside the significant latency improvements for Pages projects, this change also gave improvements in other areas:</p><ul><li><p><b><b><b>Lower CPU usage</b></b></b> - Since we no longer need to parse a huge JSON blob and do potentially thousands of regex matches, we saved a nice amount of CPU time across thousands of machines across our data centres.</p></li><li><p><b><b><b>Higher LRU hit rate</b></b></b> - We have LRU caches for things we fetch from <a href="/introducing-quicksilver-configuration-distribution-at-internet-scale/">Quicksilver</a> this is to reduce load on Quicksilver and improve performance. However, with the large routing tables we had previously, we could easily fill up this cache with one or just a few routing tables. Now that we have turned this into tiny single entry JSONs, we have improved the cache hit rate for <i>all Workers</i>.</p></li><li><p><b><b><b>Quicksilver storage reduction</b></b></b> - We also reduced the storage we take up with our routing tables by <b>92%</b>. This is a reduction of approximately 12 GiB on each of our hundreds of data centres.</p></li></ul>
    <div>
      <h3>We’re just getting started</h3>
      <a href="#were-just-getting-started">
        
      </a>
    </div>
    <p>Pages is now the <a href="https://www.webpagetest.org/video/compare.php?tests=230323_BiDcFV_ABE,230323_BiDcMX_ABF">fastest way</a> to serve your sites across Netlify, <a href="https://www.webpagetest.org/video/compare.php?tests=230323_AiDc65_AFF,230323_BiDcGD_ABX">Vercel</a> and many others and we’re so proud.</p><p>But it’s going to get even faster. With projects like <a href="/building-cloudflare-on-cloudflare/">Flame</a>, we can’t wait to shave off many more milliseconds to every request a user makes to your site.</p><p>To a faster web for all of us.</p> ]]></content:encoded>
            <category><![CDATA[Speed Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Performance]]></category>
            <guid isPermaLink="false">5LCX7XNiq3YobLf0Mqolzp</guid>
            <dc:creator>Sid Chatterjee</dc:creator>
            <dc:creator>Daniel Walsh</dc:creator>
            <dc:creator>Nevi Shah</dc:creator>
        </item>
        <item>
            <title><![CDATA[Making Cloudflare the best place for your web applications]]></title>
            <link>https://blog.cloudflare.com/making-cloudflare-for-web/</link>
            <pubDate>Wed, 17 May 2023 13:05:00 GMT</pubDate>
            <description><![CDATA[ Angular, Astro, Next, Nuxt, Qwik, Remix, Solid, Svelte, Vue on Cloudflare? Deployed globally, even “beyond the edge”, with a single command? And all for $0 or a fraction of the usual cost? Try: npm create cloudflare ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/48P7Dxc74iSHHOSu9OT0Dd/6838b800ce6874801e5d9c0f110ea77d/image1-38.png" />
            
            </figure><p>Hey web developers! We are about to shake things up a bit here at Cloudflare and wanted to give you a heads-up, so that you know what we are doing and where we are going. You might know Cloudflare as one of the best places to come to when you need to protect, speed up, or scale your web application, but increasingly Cloudflare is also becoming the best place to <i>deploy and run</i> your application!</p><p><b>Why deploy your application to Cloudflare?</b> Two simple reasons. First, it removes lots of hassle of managing many separate systems and allows you to develop, deploy, monitor, and tune your application all in one place. Second, by deploying to Cloudflare directly, there is so much more we can do to optimize your application and get it to the hands, ears, or eyes of your users more quickly and smoothly.</p><p><b>So what’s changing?</b> Quite a bit, actually. I’m not going to bore you with rehashing all the details as my most-awesome colleagues have written separate blog posts with all the details, but here is a high level rundown.</p>
    <div>
      <h3>Cloudflare Workers + Pages = awesome development platform</h3>
      <a href="#cloudflare-workers-pages-awesome-development-platform">
        
      </a>
    </div>
    <p>Cloudflare Pages and Workers are merging into a single unified development and <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">application hosting platform</a> that offers:</p><ul><li><p>Super low latency globally: your static assets and compute are less than <a href="https://www.cloudflare.com/network/">50ms away from 95% of the world’s Internet-connected population</a>.</p></li><li><p>Free egress including free static asset hosting.</p></li><li><p>Standards-based JavaScript and WASM runtime that already serves over 10 million requests per second at peak globally.</p></li><li><p>Access to powerful features like R2 (<a href="https://www.cloudflare.com/learning/cloud/what-is-object-storage/">object storage</a> with an <a href="https://www.cloudflare.com/developer-platform/solutions/s3-compatible-object-storage/">S3-compatible</a> API), low-latency globally replicated <a href="https://www.cloudflare.com/products/workers-kv/">KV storage</a>, <a href="https://developers.cloudflare.com/queues/">Queues</a>, <a href="https://developers.cloudflare.com/d1/">D1 database</a>, and many more.</p></li><li><p>Support for GitOps and <a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-ci-cd/">CI/CD workflows</a> and preview environments to boost development velocity.</p></li><li><p>… and so much more.</p></li></ul><p>While mathematically proven to be wrong, we stubbornly believe that 1+1=3, and in this case this translates to Cloudflare Pages + Workers = way more than the sum of the parts. In fact, it’s an awesome foundation for one of a kind development platform that we are thrilled to be building for you.</p><p>We started this product convergence journey a few quarters ago, and early on agreed upon not leaving any of the existing applications behind. Instead, we’ll be bringing them over to this new world. Today we are ready to start sharing the incremental results, with so much more to come over the upcoming quarters. Want to know more? My colleague Nevi posted lots of spicy details in <a href="/pages-and-workers-are-converging-into-one-experience">her blog post</a>.</p>
    <div>
      <h3>Smart Placement for Workers takes us beyond the edge!</h3>
      <a href="#smart-placement-for-workers-takes-us-beyond-the-edge">
        
      </a>
    </div>
    <p>Smart placement is, to put it simply, revolutionary for Cloudflare. It enables a new compute paradigm on our platform, unmatched by any other application hosting providers today. Do you have a typical full-stack application built with one of the many popular web frameworks? This feature is for you! And it works with both Workers and Pages!</p><p>While previously we always executed all applications at the “edge” of our global network — meaning, as close to the user as possible. With smart placement, we intelligently determine the best location within our network where the compute (your application) should run. We do this by observing your application’s behavior and what other network resources or endpoints the application interacts with. We then transparently spawn your application at an optimal location, usually close to where your data is stored, and route the incoming requests via our network to this location.</p><p>Smart placement enables applications to run near to the data these applications need to get stuff done. This is especially powerful for applications that interact with databases, <a href="https://www.cloudflare.com/learning/cloud/what-is-object-storage/">object stores</a>, or other backend endpoints, especially if these are centralized and not globally distributed.</p><p>Your user or clients requests still enter our lightning fast network in one of our 285+ datacenters in the world, close to their current location, but instead of spawning the application right there, we route the request to the most optimal datacenter, the one that is near the data or backend system the application talks to.</p><p>This doesn’t mean that compute at the edge is not cool anymore! It is! There are still many use-cases where running your application at the edge makes sense, and smart placement will determine this scenario and keep the application at the edge if that’s the right place for it to be. A/B testing, localization, asset serving, and others are use-cases that should almost always happen at the edge.</p><p>Sounds interesting? Check out this <a href="https://smart-placement-demo.pages.dev/">visual demo</a> and read up on <a href="/announcing-workers-smart-placement/">Smart Placement in a blog post from my colleague Tanushree</a> to get started.</p>
    <div>
      <h3>Develop locally or in the browser!</h3>
      <a href="#develop-locally-or-in-the-browser">
        
      </a>
    </div>
    <p>We continue to deliver on our goal to build the best development environment integrated directly into our lightning fast and globally distributed application platform. We’re launching <a href="https://developers.cloudflare.com/workers/get-started/guide/#1-start-a-new-project-with-wrangler-the-workers-cli">Wrangler</a> v3, with complete support for local-by-default development workflow. Powered by the open-source Cloudflare Workers JavaScript runtime — <a href="https://github.com/cloudflare/workerd#readme">workerd</a>, this change reduces development server startup time by 10x and script reload times by 60x — boosting your productivity and keeping you in the flow longer.</p><p>In the dashboard, we're introducing an upgraded and far more powerful online editor powered by <a href="https://code.visualstudio.com/">VSCode</a> – you can now finally edit multiple JavaScript modules in your browser, get an accurate edge preview of your code, friendly error pages, and type checking!</p><p>Finally, in both our dashboard editor and Wrangler, we've updated our workerd-customized <a href="https://developer.chrome.com/docs/devtools/">Chrome DevTools</a> to the latest version, providing even greater debugging and profiling capabilities, wherever you choose to work.</p><p>This is just the first wave of improvements to our development tooling space, you’ll see us iterating in this space over the next few quarters, but in the meantime, check out in-depth posts from Adam, Brendan, and Samuel with <a href="/wrangler3">all the Wrangler v3 details</a> and <a href="/improved-quick-edit">VSCode and dash editor improvements</a>.</p>
    <div>
      <h3>Increased memory, CPU, and application size limits and simplified pricing!</h3>
      <a href="#increased-memory-cpu-and-application-size-limits-and-simplified-pricing">
        
      </a>
    </div>
    <p>In the age of AI, WASM, and powerful full-stack applications, we’ve noticed that developers are hitting our current resource limits with increased frequency. We want to be a place where these applications thrive and developers are empowered to build bigger and more sophisticated applications. Therefore, within the next week we’ll be increasing application size limits (JavaScript/WASM bundle size) to 10MB (after gzip) and startup latency limit (script compile time) is being increased from 200ms to 400ms.</p><p>To further empower developers, we’re thinking about how to unify and simplify our billing model to make our pricing more straightforward, and increase limits such as memory limits by introducing tiers. Stay tuned for more information on these!</p><p>With these changes developers can build cooler apps and operate them for less! Cool, right?!?</p>
    <div>
      <h3>Pages CI now with a modern build image!</h3>
      <a href="#pages-ci-now-with-a-modern-build-image">
        
      </a>
    </div>
    <p>The wait is finally over! Pages now use a modern build image to power the CI and integrated build system. With this improvement you can finally use recent versions of Node.js, pnpm, and many other tools used by developers today.</p><p>While delivering this improvement, we made it much easier for us to keep things up to date in the future, but also unlocked new features like build caching!</p><p>The updates are available to all new projects by default, while existing projects can opt in to newer defaults. Sounds like your cup of coffee? Read on in <a href="/moderizing-cloudflare-pages-builds-toolbox">this blog post by Greg</a>.</p>
    <div>
      <h3>Enough already, let’s get started! …with your framework of choice and C3!</h3>
      <a href="#enough-already-lets-get-started-with-your-framework-of-choice-and-c3">
        
      </a>
    </div>
    <p>In addition to being a CDN, and place to deploy your Worker applications, Cloudflare is now also becoming the best place to run your full-stack web applications. This includes all full-stack web frameworks like Angular, Astro, Next, Nuxt, Qwik, Remix, Solid, Svelte, Vue, and others.</p><p>Our overall mission is to help build a better Internet, and my team’s contribution to this mission is to enable developers, but really just about anyone, to go from an idea to a deployed application in no time.</p><p>To enable developers to turn their ideas into deployed applications quickly and without any hassle we’ve built two things.</p><p>First, we partnered with many web framework authors to build new or improve existing adapters for all the popular JavaScript web frameworks. These adapters ensure that your application runs on our platform in the most efficient way, while having access to all the capabilities and features of our platform.</p><p>These adapters include the highly <a href="https://github.com/cloudflare/next-on-pages/">requested Next.js adapter</a>, that we’ve just overhauled to be production ready and are launching 1.0.0 today! In partnership with the respective teams, we’ve built brand-new adapters for <a href="https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare/src/frameworks/angular">Angular</a>, and <a href="https://github.com/dario-piotrowicz/qwik/tree/main/starters/adaptors/cloudflare-pages">Qwik</a>, while improving Astro, Nuxt, Solid, and a few others.</p><p>Second, we developed a brand new sassy CLI we call C3 — short for create-cloudflare CLI, a sibling to our existing Wrangler CLI. If you are a developer who lives your life in terminal or local editors like VSCode, then this CLI is your single entry-point to the Cloudflare universe.</p><p>Run the C3 command, and we’ll get you started. You pick your framework of choice, we hand the control over to the CLI of the chosen framework as we don’t want to stand in between you and the hard-working framework authors that craft the experience for their framework. A minute or so later once all npm dependencies are installed, you get a URL from us with your application deployed. That’s it. From an idea to a URL that you can share with friends almost instantly! Boom.</p><div></div>
<p></p>
    <div>
      <h3>The best place for your web applications</h3>
      <a href="#the-best-place-for-your-web-applications">
        
      </a>
    </div>
    <p>So to recap, our first class support for full-stack web frameworks, combined with the low latency and cost-effectiveness of our platform, as well as smart placement that allows the backend of the full-stack web application to run in the optimal location automagically, and all the remaining significant improvements in our developer tooling, makes Cloudflare THE best place to build and host web applications. This is our contribution to our mission to build a better Internet and push the Web forward.</p><p>We aspire to be the place people turn to when they want to get business done, or when they just want to be creative, explore ideas and have fun. It’s a long journey, and we’ve got a lot of interesting challenges ahead of us. <a href="https://forms.gle/X7P6BWs529eJRs6LA">Your input will be critical</a> in guiding us. We are all thrilled to have the opportunity to be part of it and give it our best shot. You can join this journey too, and get started today:</p>
            <pre><code>npm create cloudflare my-first-app</code></pre>
            
    <div>
      <h3>Watch on Cloudflare TV</h3>
      <a href="#watch-on-cloudflare-tv">
        
      </a>
    </div>
    <div></div><p></p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">2StHmpNu5XVZ3I0fn5ji54</guid>
            <dc:creator>Igor Minar</dc:creator>
        </item>
        <item>
            <title><![CDATA[Modernizing the toolbox for Cloudflare Pages builds]]></title>
            <link>https://blog.cloudflare.com/moderizing-cloudflare-pages-builds-toolbox/</link>
            <pubDate>Wed, 17 May 2023 13:00:37 GMT</pubDate>
            <description><![CDATA[ A new beta build system is now available for Cloudflare Pages. We've updated our default languages and tools, and made some exciting underlying architecture changes. You can enable it in your project settings in the dashboard today. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2lME1CWawsCQtIuzvvfLPq/b037a9c82151e6976cb0ed220573ad80/image2-22.png" />
            
            </figure><p>Cloudflare Pages <a href="/cloudflare-pages/">launched</a> over two years ago in December 2020, and since then, we have grown Pages to build millions of deployments for developers. In May 2022, to support developers with more complex requirements, we opened up Pages to empower developers to <a href="/cloudflare-pages-direct-uploads/">create deployments using their own build environments</a> — but that wasn't the end of our journey. Ultimately, we want to be able to allow anyone to use our build platform and take advantage of the git integration we offer. You should be able to connect your repository and have it <i>just work</i> on Cloudflare Pages.</p><p>Today, we're introducing a new beta version of our build system (a.k.a. "build image") which brings the default set of tools and languages up-to-date, and sets the stage for future improvements to builds on Cloudflare Pages. We now support the latest versions of Node.js, Python, Hugo and many more, putting you on the best path for any new projects that you undertake. Existing projects will continue to use the current build system, but this upgrade will be available to opt-in for everyone.</p>
    <div>
      <h2>New defaults, new possibilities</h2>
      <a href="#new-defaults-new-possibilities">
        
      </a>
    </div>
    <p>The Cloudflare Pages build system has been updated to not only support new versions of your favorite languages and tools, but to also include new versions by default. The versions of 2020 are no longer relevant for the majority of today's projects, and as such, we're bumping these to their more modern equivalents:</p><ul><li><p><b>Node.js</b>' default is being increased from 12.18.0 to 18.16.0,</p></li><li><p><b>Python</b> 2.7.18 and 3.10.5 are both now available by default,</p></li><li><p><b>Ruby</b>'s default is being increased from 2.7.1 to 3.2.2,</p></li><li><p><b>Yarn</b>'s default is being increased from 1.22.4 to 3.5.1,</p></li><li><p>And we're adding <b>pnpm</b> with a default version of 8.2.0.</p></li></ul><p>These are just some of the headlines — check out <a href="https://developers.cloudflare.com/pages/platform/language-support-and-tools/">our documentation</a> for the full list of changes.</p><p>We're aware that these new defaults constitute a breaking change for anyone using a project without pinning their versions with an environment variable or version file. That's why we're making this new build system opt-in for existing projects. You'll be able to stay on the existing system without breaking your builds. If you do decide to adventure with us, we make it easy to test out the new system in your preview environments before rolling out to production.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/265Kkauu3hg7i7PgW6l9IC/ac785539cffcfd46c52ca0e01b70b306/image5-5.png" />
            
            </figure><p>Additionally, we're now making your builds more reproducible by taking advantage of lockfiles with many package managers. <code>npm ci</code> and <code>yarn --pure-lockfile</code> are now used ahead of your build command in this new version of the build system.</p><p>For new projects, these updated defaults and added support for pnpm and Yarn 3 mean that more projects will just work immediately without any undue setup, tweaking, or configuration. Today, we're launching this update as a beta, but we will be quickly promoting it to general availability once we're satisfied with its stability. Once it does graduate, new projects will use this updated build system by default.</p><p>We know that this update has been a long-standing request from our users (we thank you for your patience!) but part of this rollout is ensuring that we are now in a better position to make regular updates to Cloudflare Pages' build system. You can expect these default languages and tools to now keep pace with the rapid rate of change seen in the world of web development.</p><p>We very much welcome your continued feedback as we know that new tools can quickly appear on the scene, and old ones can just as quickly drop off. As ever, our <a href="https://discord.com/invite/cloudflaredev">Discord server</a> is the best place to engage with the community and Pages team. We’re excited to hear your thoughts and suggestions.</p>
    <div>
      <h2>Our modular and scalable architecture</h2>
      <a href="#our-modular-and-scalable-architecture">
        
      </a>
    </div>
    <p>Powering this updated build system is a new architecture that we've been working on behind-the-scenes. <a href="/cloudflare-pages-build-improvements/">We're no strangers to sweeping changes of our build infrastructure</a>: we've done a lot of work to grow and scale our infrastructure. Moving beyond purely <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">static site hosting</a> with <a href="/cloudflare-pages-goes-full-stack/">Pages Functions</a> brought a new wave of users, and as we explore <a href="/pages-and-workers-are-converging-into-one-experience">convergence</a> with Workers, we expect even more developers to rely on our git integrations and CI builds. Our new architecture is being rolled out without any changes affecting users, so unless you're interested in the technical nitty-gritty, feel free to stop reading!</p><p>The biggest change we're making with our architecture is its modularity. Previously, we were using Kubernetes to run a monolithic container which was responsible for everything for the build. Within the same image, we'd stream our build logs, clone the git repository, install any custom versions of languages and tools, install a project's dependencies, run the user's build command, and upload all the assets of the build. This was a lot of work for one container! It meant that our system tooling had to be compatible with versions in the user's space and therefore new default versions were a massive change to make. This is a big part of why it took us so long to be able to update the build system for our users.</p><p>In the new architecture, we've broken these steps down into multiple separate containers. We make use of <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/">Kubernetes' init containers feature</a> and instead of one monolithic container, we have three that execute sequentially:</p><ol><li><p>clone a user's git repository,</p></li><li><p>install any custom versions of languages and tools, install a project's dependencies, run the user's build command, and</p></li><li><p>upload all the assets of a build.</p></li></ol><p>We use a <a href="https://kubernetes.io/docs/tasks/access-application-cluster/communicate-containers-same-pod-shared-volume/">shared volume</a> to give the build a persistent workspace to use between containers, but now there is clear isolation between <b>system</b> stages (cloning a repository and uploading assets) and <b>user</b> stages (running code that the user is responsible for). We no longer need to worry about conflicting versions, and we've created an additional layer of security by isolating a user's control to a separate environment.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/11McSSl7sZWok1UdR8EuRV/35bb07d37152065d5870c24813ac8d73/1-6.png" />
            
            </figure><p>We're also aligning the final stage, the one responsible for uploading static assets, with the same APIs that Wrangler uses for Direct Upload projects. This reduces our maintenance burden going forward since we'll only need to consider one way of uploading assets and creating deployments. As we consolidate, we're exploring ways to make these APIs even faster and more reliable.</p>
    <div>
      <h3>Logging out</h3>
      <a href="#logging-out">
        
      </a>
    </div>
    <p>You might have noticed that we haven't yet talked about how we're continuing to stream build logs. Arguably, this was one of the most challenging pieces to work out. When everything ran in a single container, we were able to simply latch directly into the <code>stdout</code> of our various stages and pipe them through to a Durable Object which could communicate with the Cloudflare dashboard.</p><p>By introducing this new isolation between containers, we had to get a bit more inventive. After prototyping a number of approaches, we've found one that we like. We run a separate, global log collector container inside Kubernetes which is responsible for collating logs from a build, and passing them through to that same Durable Object infrastructure. The one caveat is that the logs now need to be annotated with which build they are coming from, since one global log collector container accepts logs from multiple builds. A Worker in front of the Durable Object is responsible for reading the annotation and delegating to the relevant build's Durable Object instance.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6liD6t03jOlyaUug44A7gQ/9e6fec6d6b8864b187353872d9744cd4/image1-40.png" />
            
            </figure>
    <div>
      <h3>Caching in</h3>
      <a href="#caching-in">
        
      </a>
    </div>
    <p>With this new modular architecture, we plan to integrate a feature we've been teasing for a while: build caching. Today, when you run a build in Cloudflare Pages, we start fresh every time. This works, but it's inefficient.</p><p>Very often, only small changes are actually made to your website between deployments: you might tweak some text on your homepage, or add a new blog post; but rarely does the core foundation of your site actually change between deployments. With build caching, we can reuse some of the work from earlier builds to speed up subsequent builds. We'll offer a best-effort storage mechanism that allows you to persist and restore files between builds. You'll soon be able to cache dependencies, as well as the build output itself if your framework supports it, resulting in considerably faster builds and a tighter feedback loop from push to deploy.</p><p>This is possible because our new modular design has clear divides between the stages where we'd want to restore and cache files.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/YcQdgswXh6aueVEkJkBGK/020e5a74f78b9c8a961cda57c623c69c/2-3.png" />
            
            </figure>
    <div>
      <h2>Start building</h2>
      <a href="#start-building">
        
      </a>
    </div>
    <p>We're excited about the improvements that this new modular architecture will afford the Pages team, but we're even more excited for how this will result in faster and more scalable builds for our users. This architecture transition is rolling out behind-the-scenes, but <b>the updated beta build system with new languages and tools is available to try today</b>. Navigate to your Pages project settings in the Cloudflare Dashboard to opt-in.</p><p>Let us know if you have any feedback on the <a href="https://discord.com/invite/cloudflaredev">Discord server</a>, and stay tuned for more information about build caching in upcoming posts on this blog. Later today (Wednesday 17th, 2023), the Pages team will be hosting a <a href="https://discord.com/invite/cloudflaredev?event=1103324690149298196">Q&amp;A session</a> to talk about this announcement on Discord at 17:30 UTC.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">3CewNJiFa5g7gsagtQab5V</guid>
            <dc:creator>Greg Brimble</dc:creator>
        </item>
        <item>
            <title><![CDATA[Use the language of your choice with Pages Functions via WebAssembly]]></title>
            <link>https://blog.cloudflare.com/pages-functions-with-webassembly/</link>
            <pubDate>Fri, 24 Mar 2023 13:00:00 GMT</pubDate>
            <description><![CDATA[ Today Pages is excited to offer support for WebAssembly when writing a Pages Function ]]></description>
            <content:encoded><![CDATA[ <p></p><p>On the Cloudflare Developer Platform, we understand that building any application is a unique experience for every developer. We know that in the developer ecosystem there are a plethora of tools to choose from and as a developer you have preferences and needs. We don’t believe there are “right” or “wrong” tools to use in development and want to ensure a good developer experience no matter your choices. We believe in meeting you where you are.</p><p>When Pages Functions moved to <a href="/pages-function-goes-ga/">Generally Available in November of last year</a>, we knew it was the key that unlocks a variety of use cases – namely full-stack applications! However, we still felt we could do more to provide the flexibility for you to build what you want and how you want.</p><p>That’s why today we’re opening the doors to developers who want to build their server side applications with something other than JavaScript. We’re excited to announce WebAssembly support for Pages Functions projects!</p><p><a href="https://webassembly.org/"><b>WebAssembly</b></a> <b>(or Wasm)</b> is a low-level assembly-like language that can run with near-native performance. It provides programming languages such as C/C++, C# or Rust with a compilation target, enabling them to run alongside JavaScript. Primarily designed to run on the <a href="https://webassembly.org/docs/web/">web</a> (though <a href="https://webassembly.org/docs/non-web/">not exclusively</a>), WebAssembly opens up exciting opportunities for applications to run on the web platform, both on the client and the server, that up until now couldn't have done so.</p><p>With Pages Functions being Workers “under the hood” and Workers having <a href="/workers-javascript-modules/">Wasm module support</a> for quite <a href="/webassembly-on-cloudflare-workers/">some time</a>, it is only natural that Pages provides a similar experience for our users as well. While not all use cases are a good fit for Wasm, there are <a href="https://webassembly.org/docs/use-cases/">many</a> that are. Our goal with adding Wasm support is enabling those use cases and expanding the boundaries of what Functions can build.</p>
    <div>
      <h3>Using WebAssembly in Pages Functions</h3>
      <a href="#using-webassembly-in-pages-functions">
        
      </a>
    </div>
    <p>WebAssembly in Pages Functions works very similar to how it does today in Workers — we read <code>wasm</code> files as WebAssembly modules, ready for you to import and use directly from within your Functions. In short, like this:</p>
            <pre><code>// functions/api/distance-between.js

import wasmModule from "../../pkg/distance.wasm";

export async function onRequest({ request }) {
  const moduleInstance = await WebAssembly.instantiate(wasmModule);
  const distance = await moduleInstance.exports.distance_between();

  return new Response(distance);
}</code></pre>
            <p>Let’s briefly unpack the code snippet above to highlight some things that are important to understand.</p>
            <pre><code>import wasmModule from "../../pkg/distance.wasm";</code></pre>
            <p>Pages makes no assumptions as to how the binary <code>.wasm</code> files you want to import were compiled. In our example above, <code>distance.wasm</code> can be a file you compiled yourself out of code you wrote, or equally, a file provided in a third-party library’s distribution. The only thing Pages cares about is that <code>distance.wasm</code> is a compiled <a href="https://webassembly.github.io/spec/core/binary/conventions.html">binary</a> Wasm <a href="https://webassembly.github.io/spec/core/binary/modules.html">module</a> file.</p><p>The result of that import is a <a href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Module"><code>WebAssembly.Module</code></a> object, which you can then <a href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiate">instantiate</a>:</p>
            <pre><code>const moduleInstance = await WebAssembly.instantiate(wasmModule);</code></pre>
            <p>Once the <a href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Instance"><code>WebAssembly.Instance</code></a> object is created, you can start using whatever features your Wasm module <a href="https://webassembly.github.io/spec/core/syntax/modules.html#syntax-export"><code>exports</code></a>, inside your Functions code:</p>
            <pre><code>const distance = await moduleInstance.exports.distance_between();</code></pre>
            
    <div>
      <h3>More modules, more fun!</h3>
      <a href="#more-modules-more-fun">
        
      </a>
    </div>
    <p>Apart from Wasm modules, this work unlocks support for two other module types that you can import within your Functions code: <b>text</b> and <b>binary</b>. These are not standardized modules, but can be very handy if you need to import raw text blobs (such as HTML files) as a <code>string</code>:</p>
            <pre><code>// functions/my-function.js
import html from "404.html";

export async function onRequest() {
  return new Response(html,{
    headers: { "Content-Type": "text/html" }
  });
}</code></pre>
            <p>or raw data blobs (such as images) as an <code>ArrayBuffer</code>.</p>
            <pre><code>// functions/my-function.js
import image from "../hearts.png.bin";

export async function onRequest() {
  return new Response(image,{
    headers: { "Content-Type": "image/png" }
  });
}</code></pre>
            
    <div>
      <h3>The distance between us on the surface of Earth</h3>
      <a href="#the-distance-between-us-on-the-surface-of-earth">
        
      </a>
    </div>
    <p>Let’s take a look at a live example to see it all in action! We’ve built a small <a href="https://pages-with-wasm-demo.pages.dev/">demo app</a> that walks you through an example of Functions with WebAssembly end-to-end. You can check out the code of our demo application available on <a href="https://github.com/cloudflare/pages-fns-with-wasm-demo">GitHub</a>.</p><p>The application computes the distance in kilometers on the surface of Earth between your current location (based on the geo coordinates of the incoming <a href="https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties">request</a>) and any other point on the globe, each time you click on the globe's surface.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ZkZSyM2pJghHYzcwgfw3T/3c3609ec59bb6589d7a575177cbf602e/image3-31.png" />
            
            </figure><p>The code that performs the actual high-performance distance calculation is written in Rust, and is a slight adaptation of the <a href="https://rust-lang-nursery.github.io/rust-cookbook/science/mathematics/trigonometry.html#distance-between-two-points-on-the-earth">example</a> provided in the <a href="https://rust-lang-nursery.github.io/rust-cookbook/">Rust cookbook</a>:</p>
            <pre><code>fn distance_between(from_latitude_degrees: f64, from_longitude_degrees: f64, to_latitude_degrees: f64, to_longitude_degrees: f64) -&gt; f64 {
    let earth_radius_kilometer = 6371.0_f64;

    let from_latitude = from_latitude_degrees.to_radians();
    let to_latitude = to_latitude_degrees.to_radians();

    let delta_latitude = (from_latitude_degrees - to_latitude_degrees).to_radians();
    let delta_longitude = (from_longitude_degrees - to_longitude_degrees).to_radians();

    let central_angle_inner = (delta_latitude / 2.0).sin().powi(2)
        + from_latitude.cos() * to_latitude.cos() * (delta_longitude / 2.0).sin().powi(2);
    let central_angle = 2.0 * central_angle_inner.sqrt().asin();

    let distance = earth_radius_kilometer * central_angle;
    
    return distance;
}</code></pre>
            <p>We have a Rust playground experiment available <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=b60cdd8c60bed969c03bf5b87914c196">here</a>, in case you want to play around with this code snippet in particular.</p><p>To use the <code>distance_between()</code> Rust function in Pages Functions, we first compile the code to WebAssembly using <a href="https://github.com/rustwasm/wasm-pack"><code>wasm-pack</code></a>:</p>
            <pre><code>##
# generate the `pkg` folder which will contain the wasm binary
##
wasm-pack build</code></pre>
            <p>Then, we import the generated <code>.wasm</code> artifact from inside our <code>distance-between.js</code> Pages Function. Now, each time you click on the globe surface, a request to <code>/api/distance-between</code> is made, which will trigger the <code>distance_between()</code> function to execute. Once computed, the distance value is returned by our Function, back to the client, which proceeds to display the value to the user.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1nWcWHd4MNnPVEZQ8A3MwE/868b091b3b4849d2c6d9848a737eb476/image2-23.png" />
            
            </figure><p>We want to point out that this application could have been built entirely in JavaScript, however, we equally wanted to show just how simple it is to build it with Rust. The decision to use Rust was motivated by a few factors. First, the tooling ecosystem for building and working with Rust-generated WebAssembly is quite mature, well documented, and easy to get started with. Second, the Rust <a href="https://www.rust-lang.org/learn">docs</a> are a fantastic resource if you are new to Rust or to Rust with WebAssembly! If you are looking for a step-by-step tutorial on how to generate and set up a Rust and WebAssembly project, we highly recommend checking out Rust’s official <a href="https://rustwasm.github.io/docs/book/introduction.html">WebAssembly Book</a>.</p><p>We hope it gives you a solid starting point in exploring what is possible with Wasm on Pages Functions, and inspires you to create some powerful applications of your own. Head over to our <a href="https://developers.cloudflare.com/pages/platform/functions/module-support/#module-support">docs</a> to get started today!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[WASM]]></category>
            <category><![CDATA[WebAssembly]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">55qq7fR9xOWSZtN9TZgrik</guid>
            <dc:creator>Carmen Popoviciu</dc:creator>
        </item>
        <item>
            <title><![CDATA[Welcome to Wildebeest: the Fediverse on Cloudflare]]></title>
            <link>https://blog.cloudflare.com/welcome-to-wildebeest-the-fediverse-on-cloudflare/</link>
            <pubDate>Wed, 08 Feb 2023 19:00:00 GMT</pubDate>
            <description><![CDATA[ Today we're announcing Wildebeest, an open-source, easy-to-deploy ActivityPub and Mastodon-compatible server built entirely on top of Cloudflare's Supercloud. ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5BbRRixkMxcIiNYgdA9go/f2d1e27e932958951271d36ccffa2c16/Wildebeest.png" />
            
            </figure><p><a href="https://en.wikipedia.org/wiki/Fediverse">The Fediverse</a> has been a hot topic of discussion lately, with thousands, if not <a href="https://bitcoinhackers.org/@mastodonusercount">millions</a>, of new users creating accounts on platforms like <a href="https://joinmastodon.org/">Mastodon</a> to either move entirely to "the other side" or experiment and learn about this new social network.</p><p>Today we're introducing <a href="https://github.com/cloudflare/wildebeest">Wildebeest</a>, an open-source, easy-to-deploy ActivityPub and Mastodon-compatible server built entirely on top of Cloudflare's Supercloud. If you want to run your own spot in the Fediverse you can now do it entirely on Cloudflare.</p>
    <div>
      <h2>The Fediverse, built on Cloudflare</h2>
      <a href="#the-fediverse-built-on-cloudflare">
        
      </a>
    </div>
    <p>Today you're left with two options if you want to join the Mastodon federated network: either you join one of the <a href="https://joinmastodon.org/servers">existing servers</a> (servers are also called communities, and each one has its own infrastructure and rules), or you can run your self-hosted server.</p><p>There are a few reasons why you'd want to run your own server:</p><ul><li><p>You want to create a new community and attract other users over a common theme and usage rules.</p></li><li><p>You don't want to have to trust third-party servers or abide by their policies and want your server, under your domain, for your personal account.</p></li><li><p>You want complete control over your data, personal information, and content and visibility over what happens with your instance.</p></li></ul><p>The Mastodon gGmbH non-profit organization provides a server implementation using Ruby, Node.js, PostgreSQL and Redis. Running the <a href="https://github.com/mastodon/mastodon">official server</a> can be challenging, though. You need to own or rent a server or VPS somewhere; you have to install and configure the software, set up the database and public-facing web server, and configure and protect your network against attacks or abuse. And then you have to maintain all of that and deal with constant updates. It's a lot of scripting and technical work before you can get it up and running; definitely not something for the less technical enthusiasts.</p><p>Wildebeest serves two purposes: you can quickly deploy your Mastodon-compatible server on top of Cloudflare and connect it to the Fediverse in minutes, and you don't need to worry about maintaining or protecting it from abuse or attacks; Cloudflare will do it for you automatically.</p><p>Wildebeest is not a managed service. It's your instance, data, and code running in our cloud under your Cloudflare account. Furthermore, it's <a href="https://github.com/cloudflare/wildebeest">open-sourced</a>, which means it keeps evolving with more features, and anyone can <a href="https://github.com/cloudflare/wildebeest/pulls">extend</a> and improve it.</p><p>Here's what we support today:</p><ul><li><p><a href="https://www.w3.org/TR/activitypub/">ActivityPub</a>, <a href="https://www.rfc-editor.org/rfc/rfc7033">WebFinger</a>, <a href="https://github.com/cloudflare/wildebeest/tree/main/functions/nodeinfo">NodeInfo</a>, <a href="https://datatracker.ietf.org/doc/html/rfc8030">WebPush</a> and <a href="https://docs.joinmastodon.org/api/">Mastodon-compatible</a> APIs. Wildebeest can connect to or receive connections from other Fediverse servers.</p></li><li><p>Compatible with the most popular Mastodon <a href="https://github.com/nolanlawson/pinafore">web</a> (like <a href="https://github.com/nolanlawson/pinafore">Pinafore</a>), desktop, and <a href="https://joinmastodon.org/apps">mobile clients</a>. We also provide a simple read-only web interface to explore the timelines and user profiles.</p></li><li><p>You can publish, edit, boost, or delete posts, sorry, toots. We support text, images, and (soon) video.</p></li><li><p>Anyone can follow you; you can follow anyone.</p></li><li><p>You can search for content.</p></li><li><p>You can register one or multiple accounts under your instance. Authentication can be email-based on or using any Cloudflare Access compatible IdP, like GitHub or Google.</p></li><li><p>You can edit your profile information, avatar, and header image.</p></li></ul>
    <div>
      <h2>How we built it</h2>
      <a href="#how-we-built-it">
        
      </a>
    </div>
    <p>Our implementation is built entirely on top of our <a href="https://www.cloudflare.com/cloudflare-product-portfolio/">products</a> and <a href="https://developers.cloudflare.com/">APIs</a>. Building Wildebeest was another excellent opportunity to showcase our technology stack's power and versatility and prove how anyone can also use Cloudflare to build larger applications that involve multiple systems and complex requirements.</p><p>Here's a birds-eye diagram of Wildebeest's architecture:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/33R5UHXYSBDBUsoFLMkoC8/0304880c93af0a41d168616da4c73b90/Screenshot-2023-02-08-at-10.58.01-AM.png" />
            
            </figure><p>Let's get into the details and get technical now.</p>
    <div>
      <h3>Cloudflare Pages</h3>
      <a href="#cloudflare-pages">
        
      </a>
    </div>
    <p>At the core, Wildebeest is a <a href="https://pages.cloudflare.com/">Cloudflare Pages</a> project running its code using <a href="https://developers.cloudflare.com/pages/platform/functions/">Pages Functions</a>. Cloudflare Pages provides an excellent foundation for building and deploying your application and serving your bundled assets, Functions gives you full access to the Workers ecosystem, where you can run any code.</p><p>Functions has a built-in <a href="https://developers.cloudflare.com/pages/platform/functions/routing/">file-based router</a>. The <a href="https://github.com/cloudflare/wildebeest/tree/main/functions">/functions</a> directory structure, which is uploaded by Wildebeest’s continuous deployment builds, defines your application routes and what files and code will process each HTTP endpoint request. This routing technique is similar to what other frameworks like Next.js <a href="https://nextjs.org/docs/routing/introduction">use</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5NsNlFYtyqKVzhFFBuGeRW/767c9b102b9d97ad067c343df387c5db/2b.png" />
            
            </figure><p>For example, Mastodon’s <a href="https://docs.joinmastodon.org/methods/timelines/#public">/api/v1/timelines/public</a> API endpoint is handled by <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/api/v1/timelines/public.ts">/functions/api/v1/timelines/public.ts</a> with the onRequest method.</p>
            <pre><code>export onRequest = async ({ request, env }) =&gt; {
	const { searchParams } = new URL(request.url)
	const domain = new URL(request.url).hostname
...
	return handleRequest(domain, env.DATABASE, {})
}

export async function handleRequest(
    …
): Promise&lt;Response&gt; {
    …
}
</code></pre>
            <p>Unit testing these endpoints becomes easier too, since we only have to call the handleRequest() function from the testing framework. Check one of our <a href="https://jestjs.io/">Jest</a> tests, <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/test/mastodon.spec.ts">mastodon.spec.ts</a>:</p>
            <pre><code>import * as v1_instance from 'wildebeest/functions/api/v1/instance'

describe('Mastodon APIs', () =&gt; {
	describe('instance', () =&gt; {
		test('return the instance infos v1', async () =&gt; {
			const res = await v1_instance.handleRequest(domain, env)
			assert.equal(res.status, 200)
			assertCORS(res)

			const data = await res.json&lt;Data&gt;()
			assert.equal(data.rules.length, 0)
			assert(data.version.includes('Wildebeest'))
		})
       })
})
</code></pre>
            <p>As with any other regular Worker, Functions also lets you set up <a href="https://developers.cloudflare.com/pages/platform/functions/bindings/">bindings</a> to interact with other Cloudflare products and features like <a href="https://developers.cloudflare.com/workers/runtime-apis/kv/">KV</a>, <a href="https://developers.cloudflare.com/r2/data-access/workers-api/workers-api-reference/">R2</a>, <a href="https://developers.cloudflare.com/d1/">D1</a>, <a href="https://developers.cloudflare.com/workers/runtime-apis/durable-objects/">Durable Objects</a>, and more. The list keeps growing.</p><p>We use Functions to implement a large portion of the official <a href="https://docs.joinmastodon.org/api/">Mastodon API</a> specification, making Wildebeest compatible with the existing ecosystem of other servers and client applications, and also to run our own read-only web frontend under the same project codebase.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/Wz8EZKQyMvyEfDvH7cOV9/02183c976fe7c619c2fc4f8e99795463/3b.png" />
            
            </figure><p>Wildebeest’s web frontend uses <a href="https://qwik.builder.io/">Qwik</a>, a general-purpose web framework that is optimized for speed, uses modern concepts like the JSX JavaScript syntax extension and supports server-side-rendering (SSR) and static site generation (SSG).</p><p>Qwik provides a <a href="https://qwik.builder.io/integrations/deployments/cloudflare-pages/">Cloudflare Pages Adaptor</a> out of the box, so we use that (check our <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-qwik-site/">framework guide</a> to know more about how to deploy a Qwik site on Cloudflare Pages). For styling we use the <a href="https://tailwindcss.com/">Tailwind CSS</a> framework, which Qwik supports natively.</p><p>Our frontend website code and static assets can be found under the <a href="https://github.com/cloudflare/wildebeest/tree/main/frontend">/frontend</a> directory. The application is handled by the <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/%5B%5Bpath%5D%5D.ts">/functions/[[path]].js</a> dynamic route, which basically catches all the non-API requests, and then <a href="https://github.com/cloudflare/wildebeest/blob/main/frontend/src/entry.cloudflare-pages.tsx">invokes</a> Qwik’s own internal router, <a href="https://qwik.builder.io/qwikcity/routing/overview/">Qwik City</a>, which takes over everything else after that.</p><p>The power and versatility of Pages and Functions routes make it possible to run both the backend APIs and a server-side-rendered dynamic client, effectively a full-stack app, under the same project.</p><p>Let's dig even deeper now, and understand how the server interacts with the other components in our architecture.</p>
    <div>
      <h3>D1</h3>
      <a href="#d1">
        
      </a>
    </div>
    <p>Wildebeest uses <a href="https://developers.cloudflare.com/d1/">D1</a>, <a href="https://www.cloudflare.com/developer-platform/products/d1/">Cloudflare’s first SQL database</a> for the Workers platform built on top of SQLite, now open to everyone in <a href="/d1-open-alpha/">alpha</a>, to store and query data. Here’s our schema:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/67Xq9kbn6qh2XgRveGSdHt/3a020d6c71a89f0020b8fb7e87433601/4b.png" />
            
            </figure><p>The schema will probably change in the future, as we add more features. That’s fine, D1 supports <a href="https://developers.cloudflare.com/d1/platform/migrations/">migrations</a> which are great when you need to update your database schema without losing your data. With each new Wildebeest version, we can create a <a href="https://github.com/cloudflare/wildebeest/blob/main/migrations/0001_add-unique-following.sql">new migration</a> file if it requires database schema changes.</p>
            <pre><code>-- Migration number: 0001 	 2023-01-16T13:09:04.033Z

CREATE UNIQUE INDEX unique_actor_following ON actor_following (actor_id, target_actor_id);
</code></pre>
            <p>D1 exposes a powerful <a href="https://developers.cloudflare.com/d1/platform/client-api/">client API</a> that developers can use to manipulate and query data from Worker scripts, or in our case, Pages Functions.</p><p>Here’s a simplified example of how we interact with D1 when you start following someone on the Fediverse:</p>
            <pre><code>export async function addFollowing(db, actor, target, targetAcct): Promise&lt;UUID&gt; {
	const query = `INSERT OR IGNORE INTO actor_following (id, actor_id, target_actor_id, state, target_actor_acct) VALUES (?, ?, ?, ?, ?)`
	const out = await db
		.prepare(query)
		.bind(id, actor.id.toString(), target.id.toString(), STATE_PENDING, targetAcct)
		.run()
	return id
}
</code></pre>
            <p>Cloudflare’s culture of dogfooding and building on top of our own products means that we sometimes experience their shortcomings before our users. We did face a few challenges using D1, which is built on SQLite, to store our data. Here are two examples.</p><p><a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> uses <a href="https://www.rfc-editor.org/rfc/rfc4122.txt">UUIDs</a> to identify objects and reference them in URIs extensively. These objects need to be stored in the database. Other databases like PostgreSQL provide built-in functions to <a href="https://www.postgresql.org/docs/current/functions-uuid.html">generate unique identifiers</a>. SQLite and D1 don't have that, yet, it’s in our roadmap.</p><p>Worry not though, the Workers runtime supports <a href="https://developers.cloudflare.com/workers/runtime-apis/web-crypto/">Web Crypto</a>, so we use crypto.randomUUID() to get our unique identifiers. Check the <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/activitypub/actors/inbox.ts">/backend/src/activitypub/actors/inbox.ts</a>:</p>
            <pre><code>export async function addObjectInInbox(db, actor, obj) {
	const id = crypto.randomUUID()
	const out = await db
		.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
		.bind(id, actor.id.toString(), obj.id.toString())
		.run()
}</code></pre>
            <p>Problem solved.</p><p>The other example is that we need to store dates with sub-second resolution. Again, databases like PostgreSQL have that:</p>
            <pre><code>psql&gt; select now();
2023-02-01 11:45:17.425563+00</code></pre>
            <p>However SQLite falls short with:</p>
            <pre><code>sqlite&gt; select datetime();
2023-02-01 11:44:02</code></pre>
            <p>We worked around this problem with a small hack using <a href="https://www.sqlite.org/lang_datefunc.html">strftime()</a>:</p>
            <pre><code>sqlite&gt; select strftime('%Y-%m-%d %H:%M:%f', 'NOW');
2023-02-01 11:49:35.624</code></pre>
            <p>See our <a href="https://github.com/cloudflare/wildebeest/blob/main/migrations/0000_initial.sql">initial SQL schema</a>, look for the <i>cdate</i> defaults.</p>
    <div>
      <h3>Images</h3>
      <a href="#images">
        
      </a>
    </div>
    <p>Mastodon content has a lot of rich media. We don't need to reinvent the wheel and build an image pipeline; Cloudflare Images <a href="https://developers.cloudflare.com/images/">provides APIs</a> to upload, transform, and serve optimized images from our global CDN, so it's the perfect fit for Wildebeest's requirements.</p><p>Things like posting content images, the profile avatar, or headers, all use the Images APIs. See <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/media/image.ts">/backend/src/media/image.ts</a> to understand how we interface with Images.</p>
            <pre><code>async function upload(file: File, config: Config): Promise&lt;UploadResult&gt; {
	const formData = new FormData()
	const url = `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/images/v1`

	formData.set('file', file)

	const res = await fetch(url, {
		method: 'POST',
		body: formData,
		headers: {
			authorization: 'Bearer ' + config.apiToken,
		},
	})

      const data = await res.json()
	return data.result
}</code></pre>
            <p>If you're curious about Images for your next project, here's a tutorial on <a href="https://developers.cloudflare.com/images/cloudflare-images/tutorials/integrate-cloudflare-images/">how to integrate Cloudflare Images</a> on your website.</p><p>Cloudflare Images is also available from the dashboard. You can use it to browse or manage your catalog quickly.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1A4gwBFdbSGDvS4DAJRyhR/95849178c4b10c82d5f619ffc1153ba0/5b.png" />
            
            </figure>
    <div>
      <h3>Queues</h3>
      <a href="#queues">
        
      </a>
    </div>
    <p>The <a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> protocol is chatty by design. Depending on the size of your social graph, there might be a lot of back-and-forth HTTP traffic. We can’t have the clients blocked waiting for hundreds of Fediverse message deliveries every time someone posts something.</p><p>We needed a way to work asynchronously and launch background jobs to offload data processing away from the main app and keep the clients snappy. The official Mastodon server has a similar strategy using <a href="https://docs.joinmastodon.org/admin/scaling/#sidekiq">Sidekiq</a> to do background processing.</p><p>Fortunately, we don't need to worry about any of this complexity either. <a href="https://developers.cloudflare.com/queues/">Cloudflare Queues</a> allows developers to send and receive messages with guaranteed delivery, and offload work from your Workers' requests, effectively providing you with asynchronous batch job capabilities.</p><p>To put it simply, you have a queue topic identifier, which is basically a buffered list that scales automatically, then you have one or more producers that, well, produce structured messages, JSON objects in our case, and put them in the queue (you define their schema), and finally you have one or more consumers that subscribes that queue, receive its messages and process them, at their own speed.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5m1TSSTZesMX1jt7K7YpHS/c192aa543426e12c03b2c753f4e4b8c4/6b.png" />
            
            </figure><p>Here’s the <a href="https://developers.cloudflare.com/queues/learning/how-queues-works/">How Queues works</a> page for more information.</p><p>In our case, the main application produces queue jobs whenever any incoming API call requires long, expensive operations. For example, when someone posts, sorry, <i>toots</i> something, we need to broadcast that to their followers' inboxes, potentially triggering many requests to remote servers. <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/activitypub/deliver.ts">Here we are</a> queueing a job for that, thus freeing the APIs to keep responding:</p>
            <pre><code>export async function deliverFollowers(
	db: D1Database,
	from: Actor,
	activity: Activity,
	queue: Queue
) {
	const followers = await getFollowers(db, from)

	const messages = followers.map((id) =&gt; {
		const body = {
			activity: JSON.parse(JSON.stringify(activity)),
			actorId: from.id.toString(),
			toActorId: id,
		}
		return { body }
	})

	await queue.sendBatch(messages)
}</code></pre>
            <p>Similarly, we don't want to stop the main APIs when remote servers deliver messages to our instance inboxes. Here's Wildebeest creating asynchronous jobs when it <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/ap/users/%5Bid%5D/inbox.ts">receives messages</a> in the inbox:</p>
            <pre><code>export async function handleRequest(
	domain: string,
	db: D1Database,
	id: string,
	activity: Activity,
	queue: Queue,
): Promise&lt;Response&gt; {
	const handle = parseHandle(id)

	const actorId = actorURL(domain, handle.localPart)
const actor = await actors.getPersonById(db, actorId)

      // creates job
	await queue.send({
		type: MessageType.Inbox,
		actorId: actor.id.toString(),
		activity,
	})

	// frees the API
	return new Response('', { status: 200 })
}</code></pre>
            <p>And the final piece of the puzzle, our <a href="https://github.com/cloudflare/wildebeest/tree/main/consumer">queue consumer</a> runs in a separate Worker, independently from the Pages project. The consumer listens for new messages and processes them sequentially, at its rhythm, freeing everyone else from blocking. When things get busy, the queue grows its buffer. Still, things keep running, and the jobs will eventually get dispatched, freeing the main APIs for the critical stuff: responding to remote servers and clients as quickly as possible.</p>
            <pre><code>export default {
	async queue(batch, env, ctx) {
		for (const message of batch.messages) {
			…

			switch (message.body.type) {
				case MessageType.Inbox: {
					await handleInboxMessage(...)
					break
				}
				case MessageType.Deliver: {
					await handleDeliverMessage(...)
					break
				}
			}
		}
	},
}</code></pre>
            <p>If you want to get your hands dirty with Queues, here’s a simple example on <a href="https://developers.cloudflare.com/queues/examples/send-errors-to-r2/">Using Queues to store data in R2</a>.</p>
    <div>
      <h3>Caching and Durable Objects</h3>
      <a href="#caching-and-durable-objects">
        
      </a>
    </div>
    <p>Caching repetitive operations is yet another strategy for improving performance in complex applications that require data processing. A famous Netscape developer, Phil Karlton, once said: "There are only two hard things in Computer Science: <b>cache invalidation</b> and naming things."</p><p>Cloudflare obviously knows a lot about caching since <a href="https://developers.cloudflare.com/cache/">it's a core feature</a> of our global CDN. We also provide <a href="https://developers.cloudflare.com/workers/learning/how-kv-works/">Workers KV</a> to our customers, a global, low-latency, key-value data store that anyone can use to cache data objects in our data centers and build fast websites and applications.</p><p>However, KV achieves its performance by being eventually consistent. While this is fine for many applications and use cases, it's not ideal for others.</p><p>The ActivityPub protocol is highly transactional and can't afford eventual consistency. Here's an example: generating complete timelines is expensive, so we cache that operation. However, when you post something, we need to invalidate that cache before we reply to the client. Otherwise, the new post won't be in the timeline and the client can fail with an error because it doesn’t see it. This actually happened to us with one of the most popular clients.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7mBZfs5UZumkHzh9ITpUSn/1f9d5e53e7d61417d962a9fd566df9e6/7b.png" />
            
            </figure><p>We needed to get clever. The team discussed a few options. Fortunately, our API catalog has plenty of options. Meet <a href="https://developers.cloudflare.com/workers/learning/using-durable-objects/">Durable Objects</a>.</p><p>Durable Objects are single-instance Workers that provide a transactional storage API. They're ideal when you need central coordination, strong consistency, and state persistence. You can use Durable Objects in cases like handling the state of <a href="https://developers.cloudflare.com/workers/learning/using-websockets/#durable-objects-and-websocket-state">multiple WebSocket</a> connections, coordinating and routing messages in a <a href="https://github.com/cloudflare/workers-chat-demo">chatroom</a>, or even <a href="/doom-multiplayer-workers/">running a multiplayer game like Doom</a>.</p><p>You know where this is going now. Yes, we implemented our key-value caching subsystem for Wildebeest <a href="https://github.com/cloudflare/wildebeest/tree/main/do">on top of a Durable Object</a>. By taking advantage of the DO's native transactional storage API, we can have strong guarantees that whenever we create or change a key, the next read will always return the latest version.</p><p>The idea is so simple and effective that it took us literally a <a href="https://github.com/cloudflare/wildebeest/blob/main/do/src/index.ts">few lines of code</a> to implement a key-value cache with two primitives: HTTP PUT and GET.</p>
            <pre><code>export class WildebeestCache {
	async fetch(request: Request) {
		if (request.method === 'GET') {
			const { pathname } = new URL(request.url)
			const key = pathname.slice(1)
			const value = await this.storage.get(key)
			return new Response(JSON.stringify(value))
		}

		if (request.method === 'PUT') {
			const { key, value } = await request.json()
			await this.storage.put(key, value)
			return new Response('', { status: 201 })
		}
	}
}</code></pre>
            <p>Strong consistency it is. Let's move to user registration and authentication now.</p>
    <div>
      <h3>Zero Trust Access</h3>
      <a href="#zero-trust-access">
        
      </a>
    </div>
    <p>The official Mastodon server <a href="https://docs.joinmastodon.org/user/signup/">handles user registrations</a>, typically using email, before you can choose your local username and start using the service. Handling user registration and authentication can be daunting and time-consuming if we were to build it from scratch though.</p><p>Furthermore, people don't want to create new credentials for every new service they want to use and instead want more convenient OAuth-like authorization and authentication methods so that they can reuse their existing Apple, Google, or GitHub accounts.</p><p>We wanted to simplify things using Cloudflare’s built-in features. Needless to say, we have a product that handles user onboarding, authentication, and <a href="https://developers.cloudflare.com/cloudflare-one/policies/access/policy-management/">access policies</a> to any application behind Cloudflare; it's called <a href="https://developers.cloudflare.com/cloudflare-one/">Zero Trust</a>. So we put Wildebeest behind it.</p><p>Zero Trust Access can either do one-time PIN (<a href="https://en.wikipedia.org/wiki/One-time_password">OTP</a>) authentication using email or single-sign-on (SSO) with many identity providers (examples: Google, Facebook, GitHub, LinkedIn), including any generic one supporting <a href="https://developers.cloudflare.com/cloudflare-one/identity/idp-integration/generic-saml/">SAML 2.0</a>.</p><p>When you start using Wildebeest with a client, you don't need to register at all. Instead, you go straight to log in, which will redirect you to the Access page and handle the authentication according to the policy that you, the owner of your instance, configured.</p><p>The policy defines who can authenticate, and how.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1zDpfgueYrKRmhmNvHCBGX/68b6b579fcb33110566b07ea6e5a3d3e/8b.png" />
            
            </figure><p>When authenticated, Access will redirect you back to Wildebeest. The first time this happens, we will detect that we don't have information about the user and ask for your Username and Display Name. This will be asked only once and is what will be to create your public Mastodon profile.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/76J7DmTtShD7slpawYXNAE/ccc908ed0dffb75a7ce6afc7b0b55510/9b.png" />
            
            </figure><p>Technically, Wildebeest implements the <a href="https://docs.joinmastodon.org/spec/oauth/#implementation">OAuth 2 specification</a>. <a href="https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/">Zero Trust</a> protects the <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/oauth/authorize.ts">/oauth/authorize</a> endpoint and issues a valid <a href="https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/validating-json/">JWT token</a> in the request headers when the user is authenticated. Wildebeest then reads and verifies the JWT and returns an authorization code in the URL redirect.</p><p>Once the client has an authorization code, it can use the <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/oauth/token.ts">/oauth/token</a> endpoint to obtain an API access token. Subsequent API calls inject a bearer token in the Authorization header:</p><p><code>Authorization: Bearer access_token</code></p>
    <div>
      <h3>Deployment and Continuous Integration</h3>
      <a href="#deployment-and-continuous-integration">
        
      </a>
    </div>
    <p>We didn't want to run a managed service for Mastodon as it would somewhat diminish the concepts of federation and data ownership. Also, we recognize that ActivityPub and Mastodon are emerging, fast-paced technologies that will evolve quickly and in ways that are difficult to predict just yet.</p><p>For these reasons, we thought the best way to help the ecosystem right now would be to provide an open-source software package that anyone could use, customize, improve, and deploy on top of our cloud. Cloudflare will obviously keep improving Wildebeest and support the community, but we want to give our Fediverse maintainers complete control and ownership of their instances and data.</p><p>The remaining question was, how do we distribute the Wildebeest bundle and make it easy to deploy into someone's account when it requires configuring so many Cloudflare features, and how do we facilitate updating the software over time?</p><p>The solution ended up being a clever mix of using GitHub with <a href="https://github.com/features/actions">GitHub Actions</a>, <a href="https://developers.cloudflare.com/workers/platform/deploy-button/">Deploy with Workers</a>, and <a href="https://github.com/cloudflare/terraform-provider-cloudflare">Terraform</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5V8fRfu3U03n2ZVNtsh01L/404637763a8476b425a562ff5bbf8739/Screenshot-2023-02-08-at-11.13.05-AM-1.png" />
            
            </figure><p>The Deploy with Workers button is a specially crafted link that auto-generates a workflow page where the user gets asked some questions, and Cloudflare handles authorizing GitHub to deploy to Workers, automatically forks the Wildebeest repository into the user's account, and then configures and deploys the project using a <a href="https://github.com/marketplace/actions/deploy-to-cloudflare-workers-with-wrangler">GitHub Actions</a> workflow.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3MhyoOAbQEjlNnEhwl70Jm/5000c8c1dc1dfea549ee6ca62f8460b4/10b.png" />
            
            </figure><p>A GitHub Actions <a href="https://docs.github.com/en/actions/using-workflows/about-workflows">workflow</a> is a YAML file that declares what to do in every step. Here’s the <a href="https://github.com/cloudflare/wildebeest/blob/main/.github/workflows/deploy.yml">Wildebeest workflow</a> (simplified):</p>
            <pre><code>name: Deploy
on:
  push:
    branches:
      - main
  repository_dispatch:
jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - name: Ensure CF_DEPLOY_DOMAIN and CF_ZONE_ID are defined
        ...
      - name: Create D1 database
        uses: cloudflare/wrangler-action@2.0.0
        with:
          command: d1 create wildebeest-${{ env.OWNER_LOWER }}
        ...
      - name: retrieve Zero Trust organization
        ...
      - name: retrieve Terraform state KV namespace
        ...
      - name: download VAPID keys
        ...
      - name: Publish DO
      - name: Configure
        run: terraform plan &amp;&amp; terraform apply -auto-approve
      - name: Create Queue
        ...
      - name: Publish consumer
        ...
      - name: Publish
        uses: cloudflare/wrangler-action@2.0.0
        with:
          command: pages publish --project-name=wildebeest-${{ env.OWNER_LOWER }} .</code></pre>
            
    <div>
      <h4>Updating Wildebeest</h4>
      <a href="#updating-wildebeest">
        
      </a>
    </div>
    <p>This workflow runs automatically every time the main branch changes, so updating the Wildebeest is as easy as synchronizing the upstream official repository with the fork. You don't even need to use git commands for that; GitHub provides a convenient Sync button in the UI that you can simply click.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6vkcs7XzLMZdihq7z5n2L5/b83e8499970012ebcf0b47e686b6518a/11b.png" />
            
            </figure><p>What's more? Updates are incremental and non-destructive. When the GitHub Actions workflow redeploys Wildebeest, we only make the necessary changes to your configuration and nothing else. You don't lose your data; we don't need to delete your existing configurations. Here’s how we achieved this:</p><p>We use <a href="https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs">Terraform</a>, a declarative configuration language and tool that interacts with our APIs and can query and configure your Cloudflare features. Here's the trick, whenever we apply a new configuration, we keep a copy of the Terraform state for Wildebeest in a <a href="https://developers.cloudflare.com/workers/learning/how-kv-works/">Cloudflare KV</a> key. When a new deployment is triggered, we get that state from the KV copy, calculate the differences, then change only what's necessary.</p><p>Data loss is not a problem either because, as you read above, D1 supports <a href="https://developers.cloudflare.com/d1/platform/migrations/">migrations</a>. If we need to add a new column to a table or a new table, we don't need to destroy the database and create it again; we just apply the necessary SQL to that change.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3UW6Qm1KE662wiFVgrWFPZ/ba80730c09161abc81c85bb89fd758b3/12b.png" />
            
            </figure>
    <div>
      <h3>Protection, optimization and observability, naturally</h3>
      <a href="#protection-optimization-and-observability-naturally">
        
      </a>
    </div>
    <p>Once Wildebeest is up and running, you can protect it from bad traffic and malicious actors. Cloudflare offers you <a href="https://www.cloudflare.com/ddos/">DDoS</a>, <a href="https://www.cloudflare.com/waf/">WAF</a>, and <a href="https://www.cloudflare.com/products/bot-management/">Bot Management</a> protection out of the box at a click's distance.</p><p>Likewise, you'll get instant network and content delivery optimizations from our products and <a href="https://www.cloudflare.com/analytics/">analytics</a> on how your Wildebeest instance is performing and being used.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4EYUh9pE5NNPnpj9mwVSfz/7d97cf99ad29cd9436b8e48c7918cd24/13b.png" />
            
            </figure>
    <div>
      <h3>ActivityPub, WebFinger, NodeInfo and Mastodon APIs</h3>
      <a href="#activitypub-webfinger-nodeinfo-and-mastodon-apis">
        
      </a>
    </div>
    <p>Mastodon popularized the Fediverse concept, but many of the underlying technologies used have been around for quite a while. This is one of those rare moments when everything finally comes together to create a working platform that answers an actual use case for Internet users. Let's quickly go through the protocols that Wildebeest had to implement:</p>
    <div>
      <h4>ActivityPub</h4>
      <a href="#activitypub">
        
      </a>
    </div>
    <p><a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> is a decentralized social networking protocol and has been around as a W3C recommendation since at least 2018. It defines client APIs for creating and manipulating content and server-to-server APIs for content exchange and notifications, also known as federation. ActivityPub uses <a href="https://www.w3.org/TR/activitystreams-core/">ActivityStreams</a>, an even older W3C protocol, for its vocabulary.</p><p>The concepts of <a href="https://www.w3.org/TR/activitypub/#actors">Actors</a> (profiles), messages or <a href="https://www.w3.org/TR/activitypub/#obj">Objects</a> (the toots), <a href="https://www.w3.org/TR/activitypub/#inbox">inbox</a> (where you receive toots from people you follow), and <a href="https://www.w3.org/TR/activitypub/#outbox">outbox</a> (where you send your toots to the people you follow), to name a few of many other actions and activities, are all defined on the ActivityPub specification.</p><p>Here’s our folder with the <a href="https://github.com/cloudflare/wildebeest/tree/main/backend/src/activitypub">ActivityPub implementation</a>.</p>
            <pre><code>import type { APObject } from 'wildebeest/backend/src/activitypub/objects'
import type { Actor } from 'wildebeest/backend/src/activitypub/actors'

export async function addObjectInInbox(db, actor, obj) {
	const id = crypto.randomUUID()
	const out = await db
		.prepare('INSERT INTO inbox_objects(id, actor_id, object_id) VALUES(?, ?, ?)')
		.bind(id, actor.id.toString(), obj.id.toString())
		.run()
}
</code></pre>
            
    <div>
      <h4>WebFinger</h4>
      <a href="#webfinger">
        
      </a>
    </div>
    <p>WebFinger is a simple HTTP protocol used to discover information about any entity, like a profile, a server, or a specific feature. It resolves URIs to resource objects.</p><p>Mastodon uses <a href="https://www.rfc-editor.org/rfc/rfc7033">WebFinger</a> lookups to discover information about remote users. For example, say you want to interact with @<a href="#">user@example.com</a>. Your local server would <a href="https://github.com/cloudflare/wildebeest/blob/main/backend/src/webfinger/index.ts">request</a> <a href="https://example.com/.well-known/webfinger?resource=acct:user@example.com">https://example.com/.well-known/webfinger?resource=acct:user@example.com</a> (using the <a href="https://www.rfc-editor.org/rfc/rfc7565">acct scheme</a>) and get something like this:</p>
            <pre><code>{
    "subject": "acct:user@example.com",
    "aliases": [
        "https://example.com/ap/users/user"
    ],
    "links": [
        {
            "rel": "self",
            "type": "application/activity+json",
            "href": "https://example.com/ap/users/user"
        }
    ]
}
</code></pre>
            <p>Now we know how to interact with <code>@user@example.com</code>, using the <code>https://example.com/ap/users/user endpoint</code>.</p><p>Here’s our WebFinger <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/.well-known/webfinger.ts">response</a>:</p>
            <pre><code>export async function handleRequest(request, db): Promise&lt;Response&gt; {
	…
	const jsonLink = /* … link to actor */

	const res: WebFingerResponse = {
		subject: `acct:...`,
		aliases: [jsonLink],
		links: [
			{
				rel: 'self',
				type: 'application/activity+json',
				href: jsonLink,
			},
		],
	}
	return new Response(JSON.stringify(res), { headers })
}</code></pre>
            
    <div>
      <h4>Mastodon API</h4>
      <a href="#mastodon-api">
        
      </a>
    </div>
    <p>Finally, things like setting your server information, profile information, generating timelines, notifications, and searches, are all Mastodon-specific APIs. The Mastodon open-source project defines a catalog of REST APIs, and you can find all the documentation for them on <a href="https://docs.joinmastodon.org/api/">their website</a>.</p><p>Our Mastodon API implementation can be found <a href="https://github.com/cloudflare/wildebeest/tree/main/functions/api">here</a> (REST endpoints) and <a href="https://github.com/cloudflare/wildebeest/tree/main/backend/src/mastodon">here</a> (backend primitives). Here’s an example of Mastodon’s server information <a href="https://docs.joinmastodon.org/methods/instance/#v2">/api/v2/instance</a> implemented by <a href="https://github.com/cloudflare/wildebeest/blob/main/functions/api/v2/instance.ts">Wildebeest</a>:</p>
            <pre><code>export async function handleRequest(domain, db, env) {

	const res: InstanceConfigV2 = {
		domain,
		title: env.INSTANCE_TITLE,
		version: getVersion(),
		source_url: 'https://github.com/cloudflare/wildebeest',
		description: env.INSTANCE_DESCR,
		thumbnail: {
			url: DEFAULT_THUMBNAIL,
		},
		languages: ['en'],
		registrations: {
			enabled: false,
		},
		contact: {
			email: env.ADMIN_EMAIL,
		},
		rules: [],
	}

	return new Response(JSON.stringify(res), { headers })
}</code></pre>
            <p>Wildebeest also implements <a href="https://github.com/cloudflare/wildebeest/tree/main/backend/src/webpush">WebPush</a> for client notifications and <a href="https://github.com/cloudflare/wildebeest/tree/main/functions/nodeinfo">NodeInfo</a> for server information.</p><p>Other Mastodon-compatible servers had to implement all these protocols <a href="https://pleroma.social/">too</a>; Wildebeest is one of them. The community is very active in discussing future enhancements; we will keep improving our compatibility and adding support to more features over time, ensuring that Wildebeest plays well with the Fediverse ecosystem of servers and clients emerging.</p>
    <div>
      <h3>Get started now</h3>
      <a href="#get-started-now">
        
      </a>
    </div>
    <p>Enough about technology; let's get you into the Fediverse. We tried to detail all the steps to deploy your server. To start using Wildebeest, head to the public GitHub repository and check our <a href="https://github.com/cloudflare/wildebeest/blob/main/README.md">Get Started tutorial</a>.</p><p>Most of Wildebeest's dependencies offer a generous free plan that allows you to try them for personal or hobby projects that aren't business-critical, however you will need to subscribe an <a href="https://www.cloudflare.com/products/cloudflare-images/">Images</a> plan (the lowest tier should be enough for most needs) and, depending on your server load, <a href="https://developers.cloudflare.com/workers/platform/limits/#unbound-usage-model">Workers Unbound</a> (again, the minimum cost should be plenty for most use cases).</p><p>Following our dogfooding mantra, Cloudflare is also officially joining the Fediverse today. You can start following our Mastodon accounts and get the same experience of having regular updates from Cloudflare as you get from us on other social platforms, using your favorite Mastodon apps. These accounts are entirely running on top of a Wildebeest server:</p><ul><li><p><a href="https://cloudflare.social/@cloudflare">@cloudflare@cloudflare.social</a> - Our main account</p></li><li><p><a href="https://cloudflare.social/@radar">@radar@cloudflare.social</a> - Cloudflare Radar</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2QJsY2PkGqLfVQCDJc1AlH/d52823cfd8d2d9e7de9845686790a3bf/14b.png" />
            
            </figure><p>Wildebeest is compatible with most client apps; we are confirmed to work with the official Mastodon <a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android">Android</a> and <a href="https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974">iOS</a> apps, <a href="https://pinafore.social/">Pinafore</a>, <a href="https://mastodon.social/@JPEGuin/109315609418460036">Mammoth</a>, and <a href="https://tooot.app/">tooot</a>, and looking into others like <a href="https://tapbots.com/ivory/">Ivory</a>. If your favorite isn’t working, please submit an <a href="https://github.com/cloudflare/wildebeest/issues">issue here</a>, we’ll do our best to help support it.</p>
    <div>
      <h3>Final words</h3>
      <a href="#final-words">
        
      </a>
    </div>
    <p>Wildebeest was built entirely on top of our <a href="/welcome-to-the-supercloud-and-developer-week-2022/">Supercloud</a> stack. It was one of the most complete and complex projects we have created that uses various Cloudflare products and features.</p><p>We hope this write-up inspires you to not only try deploying Wildebeest and joining the Fediverse, but also building your next application, however demanding it is, on top of Cloudflare.</p><p>Wildebeest is a minimally viable Mastodon-compatible server right now, but we will keep improving it with more features and supporting it over time; after all, we're using it for our official accounts. It is also open-sourced, meaning you are more than welcome to contribute with pull requests or feedback.</p><p>In the meantime, we opened a <a href="https://discord.com/channels/595317990191398933/1064925651464896552">Wildebeest room</a> on our <a href="https://discord.gg/cloudflaredev">Developers Discord Server</a> and are keeping an eye open on the GitHub repo <a href="https://github.com/cloudflare/wildebeest/issues">issues</a> tab. Feel free to engage with us; the team is eager to know how you use Wildebeest and answer your questions.</p><p><i>PS: The code snippets in this blog were simplified to benefit readability and space (the TypeScript types and error handling code were removed, for example). Please refer to the GitHub repo links for the complete versions.</i></p> ]]></content:encoded>
            <category><![CDATA[Wildebeest]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[D1]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Zero Trust]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[SASE]]></category>
            <guid isPermaLink="false">5dmHcGVas7xv8tKbRbWLWN</guid>
            <dc:creator>Celso Martinho</dc:creator>
            <dc:creator>Sven Sauleau</dc:creator>
        </item>
        <item>
            <title><![CDATA[Spice up your sites on Cloudflare Pages with Pages Functions General Availability]]></title>
            <link>https://blog.cloudflare.com/pages-function-goes-ga/</link>
            <pubDate>Thu, 17 Nov 2022 14:05:00 GMT</pubDate>
            <description><![CDATA[ Pages is officially a full stack platform with Pages Functions now Generally Available. Pages is harnessing the power and scalability of Workers and specializing them to align with the Pages developer experience. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4Qt2PNf7dtFOmbvvzhQvnn/03e6c9de323ee7a011607e61c5443610/image2-37.png" />
            
            </figure><p>Before we launched Pages back in April 2021, we knew it would be the start of something magical – an experience that felt “just right”. We envisioned an experience so simple yet so smooth that <b><i>any developer</i></b> could ship a website in seconds and add more to it by using the rest of our Cloudflare ecosystem.</p><p>A few months later, when we announced that Pages was a full stack platform in November 2021, that vision became a reality. Creating a development platform for just static sites was not the end of our Pages story, and with Cloudflare Workers already a part of our ecosystem, we knew we were sitting on untapped potential. With the introduction of Pages Functions, we empowered developers to take any static site and easily add in dynamic content with the power of Cloudflare Workers.</p><p>In the last year since Functions has been in open beta, we dove into an exploration on what kinds of full stack capabilities developers are looking for on their projects – and set out to fine tune the Functions experience into what it is today.</p><p><b>We’re thrilled to announce that Pages Functions is now generally available!</b></p>
    <div>
      <h2>Functions recap</h2>
      <a href="#functions-recap">
        
      </a>
    </div>
    <p>Though called “Functions” in the context of Pages, these functions running on our Cloudflare network are Cloudflare Workers in “disguise”. Pages harnesses the power and scalability of Workers and specializes them to align with the Pages experience our users know and love.</p><p>With Functions you can dream up the possibilities of dynamic functionality to add to your site – integrate with storage solutions, connect to third party services, use server side rendering with your favorite full stack frameworks and more. As Pages Functions opens its doors to production traffic, let’s explore some of the exciting features we’ve improved and added on this release.</p>
    <div>
      <h2>The experience</h2>
      <a href="#the-experience">
        
      </a>
    </div>
    
    <div>
      <h3>Deploy with Git</h3>
      <a href="#deploy-with-git">
        
      </a>
    </div>
    <p>Love to code? We’ll handle the infrastructure, and leave you to it.</p><p>Simply write a JavaScript/Typescript Function and drop it into a <b>functions</b> directory by committing your code to your Git provider. Our lightning fast CI system will build your code and deploy it alongside your static assets.</p>
    <div>
      <h3>Directly upload your Functions</h3>
      <a href="#directly-upload-your-functions">
        
      </a>
    </div>
    <p>Prefer to handle the build yourself? Have a special git provider not yet supported on Pages? No problem! After dropping your Function in your <b>functions</b> folder, you can build with your preferred CI tooling and then upload your project to Pages to be deployed.</p>
    <div>
      <h3>Debug your Functions</h3>
      <a href="#debug-your-functions">
        
      </a>
    </div>
    <p>While in beta, we learned that you and your teams value visibility above all. As on Cloudflare Workers, we’ve built a simple way for you to watch your functions as it processes requests – the faster you can understand an issue the faster you can react.</p><p>You can now easily view logs for your Functions by “tailing” your logs. For basic information like outcome and request IP, you can navigate to the Pages dashboard to obtain relevant logs.</p><div></div><p>For more specific filters, you can use</p>
            <pre><code>wrangler pages deployment tail</code></pre>
            <p>to receive a live feed of console and exception logs for each request your Function receives.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Bc7Zz0wMrVCuLV3Ll8RFN/e581f4cb24dc8c78f28346195d0cd789/image1-52.png" />
            
            </figure>
    <div>
      <h3>Get real time Functions metrics</h3>
      <a href="#get-real-time-functions-metrics">
        
      </a>
    </div>
    <p>In the dashboard, Pages aggregates data for your Functions in the form of request successes/error metrics and invocation status. You can refer to your metrics dashboard not only to better understand your usage on a per-project basis but also to get a pulse check on the health of your Functions by catching success/error volumes.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/20rgwkkI2erSSmySgkpdzV/8d1aba3bb5081816d1de891e28efb7eb/image4-20.png" />
            
            </figure>
    <div>
      <h3>Quickly integrate with the Cloudflare ecosystem</h3>
      <a href="#quickly-integrate-with-the-cloudflare-ecosystem">
        
      </a>
    </div>
    
    <div>
      <h4><b>Storage bindings</b></h4>
      <a href="#storage-bindings">
        
      </a>
    </div>
    <p>Want to go truly full stack? We know finding a storage solution that fits your needs and fits your ecosystem is not an easy task – but it doesn’t have to be!</p><p>With Functions, you can take advantage of our broad range of storage products including Workers KV, Durable Objects, R2, D1 and – very soon – <a href="https://developers.cloudflare.com/queues/">Queues</a> and <a href="https://developers.cloudflare.com/analytics/analytics-engine/">Workers Analytics Engine</a>! Simply create your namespace, bucket or database and add your binding in the Pages dashboard to get your full stack site up and running in just a few clicks.</p><p>From dropping in a quick comment system to rolling your own authentication to creating database-backed <a href="https://www.cloudflare.com/ecommerce/">eCommerce sites</a>, integrating with existing products in our developer platform unlocks an exponential set of use cases for your site.</p>
    <div>
      <h4><b>Secret bindings</b></h4>
      <a href="#secret-bindings">
        
      </a>
    </div>
    <p>In addition to adding environment variables that are available to your project at both build-time and runtime, you can now also add “secrets” to your project. These are encrypted environment variables which cannot be viewed by any dashboard interfaces, and are a great home for sensitive data like API tokens or passwords.</p>
    <div>
      <h3>Integrate with 3rd party services</h3>
      <a href="#integrate-with-3rd-party-services">
        
      </a>
    </div>
    <p>Our goal with Pages is always to meet you where you are when it comes to the tools you love to use. During this beta period we also noticed some consistent patterns in how you were employing Functions to integrate with common third party services. <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/">Pages Plugins</a> – our ready-made snippets of code – offers a plug and play experience for you to build the ecosystem of your choice around your application.</p><p>In essence, a Pages Plugin is a reusable – and customizable – chunk of runtime code that can be incorporated anywhere within your Pages application. It’s a “composable” Pages Function, granting Plugins the full power of Functions (i.e. Workers), including the ability to set up middleware, parameterized routes, and static assets.</p><p>With Pages Plugins you can integrate with a plethora of 3rd party applications – including officially supported <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/sentry/">Sentry</a>, <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/honeycomb/">Honeycomb</a>, <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/stytch/">Stytch</a>, <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/mailchannels/">MailChannels</a> and more.</p>
    <div>
      <h3>Use your favorite full stack frameworks</h3>
      <a href="#use-your-favorite-full-stack-frameworks">
        
      </a>
    </div>
    <p>In the spirit of meeting developers where they are at, this sentiment also comes in the form of Javascript frameworks. As a big supporter of not only widely adopted frameworks but up and coming frameworks, our team works with a plethora of framework authors to create opportunities for you to play with their new tech and deploy on Pages right out of the box.</p>
    <div>
      <h4>Now compatible with Next.js 13 and more!</h4>
      <a href="#now-compatible-with-next-js-13-and-more">
        
      </a>
    </div>
    <p>Recently, we announced our <a href="/next-on-pages/">support for Next.js applications</a> which opt in to the Edge Runtime. Today we’re excited to announce we are now compatible with Next.js 13. Next.js 13 brings some most-requested modern paradigms to the Next.js framework, including nested routing, React 18's Server Components and streaming.</p><p>Have a different preference of framework? No problem.</p><p>Go full stack on Pages to take advantage of server side rendering (SSR) with one of many other officially supported frameworks like <a href="https://remix.run/">Remix</a>, <a href="https://kit.svelte.dev/">SvelteKit</a>, <a href="https://qwik.builder.io/qwikcity/overview/">QwikCity,</a> <a href="https://start.solidjs.com/getting-started/what-is-solidstart">SolidStart</a>, <a href="https://astro.build/">Astro</a> and <a href="https://nuxtjs.org/">Nuxt</a>. You can <a href="/pages-full-stack-frameworks">check out our blog post on SSR support on Pages</a> and how to get started with some of these frameworks.</p>
    <div>
      <h3>Go fast in advanced mode</h3>
      <a href="#go-fast-in-advanced-mode">
        
      </a>
    </div>
    <p>While Pages Functions are powered by Workers, we understand that at face-value they are not exactly the same. Nevertheless, for existing users who are perhaps using Workers and are keen on trying Cloudflare Pages, we’ve <a href="https://developers.cloudflare.com/pages/platform/functions/advanced-mode/">got a direct path</a> to get you started quickly.</p><p>If you already have a single Worker and want an easy way to go full stack on Pages, you can use Pages Function’s “advanced mode”. Generate an <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch-event/#syntax-module-worker">ES module Worker</a> called <code>_worker.js</code> in the output directory of your project and deploy!This can be especially helpful if you’re a framework author or perhaps have a more complex use case that does not fit into our file-based router.</p>
    <div>
      <h2>Scaling without limits</h2>
      <a href="#scaling-without-limits">
        
      </a>
    </div>
    <p>So today, as we announce Functions as generally available we are thrilled to allow your traffic to scale. During the Open Beta period, we imposed a daily limit of 100,000 free requests per day as a way to let you try out the feature. While 100,000 requests per day remains the free limit today, you can now pay to truly go unlimited.</p><p>Since Functions are just “special” Workers, with this announcement you will begin to see your Functions usage reflected on your bill under the Workers Paid subscription or via your Workers Enterprise contract. Like Workers, when on a paid plan, you have the option to choose between our two usage models – Bundled and Unbound – and will be billed accordingly.</p><p>Keeping Pages on brand as Cloudflare’s “gift to the Internet”, you will get unlimited free static asset requests and will be billed primarily on dynamic requests. You can <a href="https://developers.cloudflare.com/pages/platform/functions/pricing/">read more</a> about how billing with Functions works in our documentation.</p>
    <div>
      <h2>Get started today</h2>
      <a href="#get-started-today">
        
      </a>
    </div>
    <p>To start jamming, head over to the <a href="https://developers.cloudflare.com/pages/platform/functions/">Pages Functions docs</a> and <a href="/pages-full-stack-frameworks">check out our blog</a> on some of the best frameworks to use to deploy your first full stack application. As you begin building out your projects be sure to let us know in the <a href="https://discord.com/channels/595317990191398933/910978223968518144">#functions channel</a> under Pages of our <a href="https://discord.gg/cloudflaredev">Cloudflare Developers Discord</a>. Happy building!</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Full Stack]]></category>
            <category><![CDATA[JAMstack]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">0qAOpwZHFx8u2Lq0PKf8c</guid>
            <dc:creator>Nevi Shah</dc:creator>
        </item>
        <item>
            <title><![CDATA[How we built it: the technology behind Cloudflare Radar 2.0]]></title>
            <link>https://blog.cloudflare.com/technology-behind-radar2/</link>
            <pubDate>Thu, 17 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ Radar 2.0 was launched last month during Cloudflare's Birthday Week as a complete product revamp. This blog explains how we built it technically. Hopefully, it will inspire other developers to build complex web apps using Cloudflare products. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Tbyq3gfFHHRXwc4Uny8RH/a7d2558532a5b33ce1ffa285c950afb2/image11-1.png" />
            
            </figure><p><a href="/radar2/">Radar 2.0</a> was built on the learnings of Radar 1.0 and was launched last month during Cloudflare's Birthday Week as a complete product revamp. We wanted to make it easier for our users to find insights and navigate our data, and overall provide a better and faster user experience.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/34b5YuwtsM5h8WwqSABuXw/dc314ea8e1a3a6b8db68bae7010e64ed/image16.png" />
            
            </figure><p>We're building a <a href="/welcome-to-the-supercloud-and-developer-week-2022/">Supercloud</a>. Cloudflare's products now include hundreds of features in networking, security, access controls, computing, storage, and more.</p><p>This blog will explain how we built the new Radar from an engineering perspective. We wanted to do this to demonstrate that anyone could build a somewhat complex website that involves demanding requirements and multiple architectural layers, do it on top of our stack, and how easy it can be.</p><p>Hopefully, this will inspire other developers to switch from traditional software architectures and build their applications using modern, more efficient technologies.</p>
    <div>
      <h2>High level architecture</h2>
      <a href="#high-level-architecture">
        
      </a>
    </div>
    <p>The following diagram is a birds-eye view of the Radar 2.0 architecture. As you can see, it's divided into three main layers:</p><ul><li><p>The Core layer is where we keep our data lake, data exploration tools, and backend API.</p></li><li><p>The Cloudflare network layer is where we host and run Radar and serve the public APIs.</p></li><li><p>The Client layer is essentially everything else that runs in your browser. We call it the Radar Web app.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7eBe8tSbh0Uocpgw1abbZ8/5b30a247f945240f2f9254f16c0a021a/image3-31.png" />
            
            </figure><p>As you can see, there are Cloudflare products <i>everywhere</i>. They provide the foundational resources to host and securely run our code at scale, but also other building blocks necessary to run the application end to end.</p><p>By having these features readily available and tightly integrated into our ecosystem and tools, at the distance of a click and a few lines of code, engineering teams don't have to reinvent the wheel constantly and can use their time on what is essential: their app logic.</p><p>Let's dig in.</p>
    <div>
      <h2>Cloudflare Pages</h2>
      <a href="#cloudflare-pages">
        
      </a>
    </div>
    <p>Radar 2.0 is deployed using <a href="https://pages.cloudflare.com/">Cloudflare Pages</a>, our <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">developer-focused website hosting platform</a>. In the early days, you could only host static assets on Pages, which was helpful for many use cases, including integrating with static site generators like <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-hugo-site/">Hugo</a>, <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-jekyll-site/">Jekyll</a>, or <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-gatsby-site/">Gatsby</a>. Still, it wouldn't solve situations where your application needs some sort of server-side computing or advanced logic using a single deployment.</p><p>Luckily Pages recently added support to run custom Workers scripts. With <a href="https://developers.cloudflare.com/pages/platform/functions/">Functions</a>, you can now run server-side code and enable any kind of dynamic functionality you'd typically implement using a separate Worker.</p><p>Cloudflare Pages Functions also allow you to use <a href="https://developers.cloudflare.com/workers/learning/using-durable-objects/">Durable Objects</a>, <a href="https://developers.cloudflare.com/workers/runtime-apis/kv/">KV</a>, <a href="https://developers.cloudflare.com/r2/">R2</a>, or <a href="https://developers.cloudflare.com/d1">D1</a>, just like a regular Worker would. We provide <a href="https://developers.cloudflare.com/pages/platform/functions/">excellent documentation</a> on how to do this and more in our Developer Documentation. Furthermore, the team wrote a blog on <a href="/building-full-stack-with-pages/">how to build a full-stack application</a> that describes all the steps in detail.</p><p>Radar 2.0 needs server-side functions for two reasons:</p><ul><li><p>To render Radar and run the server side of Remix.</p></li><li><p>To implement and serve our frontend API.</p></li></ul>
    <div>
      <h2>Remix and Server-side Rendering</h2>
      <a href="#remix-and-server-side-rendering">
        
      </a>
    </div>
    <p>We use Remix with Cloudflare Pages on Radar 2.0.</p><p><a href="https://remix.run/">Remix</a> follows a server/client model and works under the premise that you can't control the user's network, so web apps must reduce the amount of Javascript, CSS, and JSON they send through the wire. To do this, they move some of the logic to the server.</p><p>In this case, the client browser will get pre-rendered DOM components and the result of pre-fetched API calls with just the right amount of JSON, Javascript, and CSS code, rightfully adjusted to the UI needs. Here’s the <a href="https://remix.run/docs/en/v1/pages/technical-explanation">technical explanation</a> with more detail.</p><p>Typically, Remix would need a Node.js server to do all of this, but guess what: <a href="https://developers.cloudflare.com/pages/framework-guides/remix/">It can also run</a> on Cloudflare Workers and Pages.</p><p>Here’s the code to get the Remix server running on Workers, using Cloudflare Pages:</p>
            <pre><code>import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
import * as build from "@remix-run/dev/server-build";

const handleRequest = createPagesFunctionHandler({
  build: {
    ...build,
    publicPath: "/build/",
    assetsBuildDirectory: "public/build",
  },
  mode: process.env.NODE_ENV,
  getLoadContext: (context) =&gt; ({
    ...context.env,
    CF: (context.request as any).cf as IncomingRequestCfProperties | undefined,
  }),
});

const handler: ExportedHandler&lt;Env&gt; = {
  fetch: async (req, env, ctx) =&gt; {
    const r = new Request(req);
    return handleRequest({
      env,
      params: {},
      request: r,
      waitUntil: ctx.waitUntil,
      next: () =&gt; {
        throw new Error("next() called in Worker");
      },
      functionPath: "",
      data: undefined,
    });
  },
};</code></pre>
            <p>In Remix, <a href="https://remix.run/docs/en/v1/guides/api-routes">routes</a> handle changes when a user interacts with the app and changes it (clicking on a menu option, for example). A Remix route can have a <a href="https://remix.run/docs/en/v1/guides/data-loading"><i>loader</i></a>, an <a href="https://remix.run/docs/en/v1/guides/data-writes"><i>action</i></a> and a <a href="https://remix.run/docs/en/v1/api/conventions#root-layout-route"><i>default</i></a> export. The <i>loader</i> handles API calls for fetching data (GET method). The <i>action</i> handles submissions to the server (POST, PUT, PATCH, DELETE methods) and returns the response. The <i>default</i> export handles the UI code in React that’s returned for that route. A route without a <i>default</i> export returns only data.</p><p>Because Remix runs both on the server and the client, it can get smart and know what can be pre-fetched and computed server-side and what must go through the network connection, optimizing everything for performance and responsiveness.</p><p>Here’s an example of a Radar route, simplified for readability, for the <a href="https://radar.cloudflare.com/outage-center">Outage Center</a> page.</p>
            <pre><code>import type { MetaFunction } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";
import { type LoaderArgs } from "@remix-run/server-runtime";

export async function loader(args: LoaderArgs) {
  const ssr = await initialFetch(SSR_CHARTS, args);
  return { ssr, };
}

export default function Outages() {
  const { ssr } = useLoaderData&lt;typeof loader&gt;();

  return (
    &lt;Page
      filters={["timerange"]}
      title={
        &lt;&gt;
          &lt;Svg use="icon-outages" /&gt;
          {t("nav.main.outage-center")}
        &lt;/&gt;
      }
    &gt;
      &lt;Grid columns={[1, 1, 1, 1]}&gt;
        &lt;Card.Article colspan={[1, 1, 1, 1]} rowspan={[1, 1, 1, 1]}&gt;
          &lt;Card.Section&gt;
            &lt;Components.InternetOutagesChoropleth ssr={ssr} /&gt;
          &lt;/Card.Section&gt;
          &lt;Divider /&gt;
          &lt;Card.Section&gt;
            &lt;Components.InternetOutagesTable ssr={ssr} /&gt;
          &lt;/Card.Section&gt;
        &lt;/Card.Article&gt;
      &lt;/Grid&gt;
    &lt;/Page&gt;
  );
}</code></pre>
            <p>And here’s what it produces:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7wmqtv1VV0kSOU3UTlwXiz/fcbaf883e8f975e679069737e6750251/image18.png" />
            
            </figure><p>Remix and SSR can also help you with your <a href="https://developer.chrome.com/docs/lighthouse/overview/">Lighthouse</a> scores and SEO. It can drastically improve metrics like <a href="https://web.dev/cls/">Cumulative Layout Shift</a>, <a href="https://web.dev/fcp/">First Contentful Paint</a> and <a href="https://web.dev/lcp/">Largest Contentful Paint</a> by reducing the number of fetches and information traveling from the server to the browser and pre-rendering the DOM.</p><p>Another project porting their app to Remix is <a href="https://cloudflare.tv/">Cloudflare TV</a>. This is how their metrics looked before and after the changes.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/oR1Rqf8Mp1fYJRpQMBrgE/aa49405daef020536bfe82c22e42c5d1/image12.png" />
            
            </figure><p>Radar’s Desktop Lighthouse score is now nearly 100% on Performance, Accessibility, Best Practices, and SEO.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3fe4weO7U21a7ZNGW969su/e5c4250fe87be0cfa8bd6f879a721e03/image14.png" />
            
            </figure><p>Another Cloudflare product that we use extensively on Radar 2.0 is <a href="https://www.cloudflare.com/website-optimization/">Speed</a>. In particular, we want to mention the <a href="/early-hints/">Early Hints</a> feature. Early Hints is a new web <a href="https://developer.mozilla.org/docs/Web/HTTP/Status/103">standard</a> that defines a new HTTP 103 header the server can use to inform the browser which assets will likely be needed to render the web page while it's still being requested, resulting in dramatic load times improvements.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2OSrsfjFevZ4qx1TT4jYWb/a012f0b129b260e0f75a62742e9d9df4/image2-42.png" />
            
            </figure><p>You can use <a href="/early-hints-on-cloudflare-pages/">Cloudflare Pages with Early Hints</a>.</p>
    <div>
      <h2>APIs</h2>
      <a href="#apis">
        
      </a>
    </div>
    <p>Radar has two APIs. The backend which has direct access to our data sources, and the frontend, which is available on the Internet.</p>
    <div>
      <h3>Backend API</h3>
      <a href="#backend-api">
        
      </a>
    </div>
    <p>The backend API was written using <a href="https://www.python.org/">Python</a>, <a href="https://pandas.pydata.org/">Pandas</a> and <a href="https://fastapi.tiangolo.com/">FastAPI</a> and is protected by <a href="https://developers.cloudflare.com/cloudflare-one/policies/access/">Cloudflare Access</a>, <a href="https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/validating-json/">JWT tokens</a> and an <a href="https://developers.cloudflare.com/ssl/origin-configuration/authenticated-origin-pull/set-up/">authenticated origin pull</a> (AOP) configuration. Using Python allows anyone on the team, engineers or data scientists, to collaborate easily and contribute to improving and expanding the API, which is great. Our data science team uses <a href="https://jupyter.org/hub">JupyterHub</a> and <a href="https://docs.jupyter.org/en/latest/start/index.html">Jupyter Notebooks</a> as part of their data exploration workflows, which makes prototyping and reusing code, algorithms and models particularly easy and fast.</p><p>It then talks to the upstream frontend API via a <a href="https://strawberry.rocks/">Strawberry</a> based GraphQL server. Using <a href="https://graphql.org/">GraphQL</a> makes it easy to create complex queries, giving internal users and analysts the flexibility they need when building reports from our vast collection of data.</p>
    <div>
      <h3>Frontend API</h3>
      <a href="#frontend-api">
        
      </a>
    </div>
    <p>We built Radar's frontend API on top of Cloudflare <a href="https://developers.cloudflare.com/workers/">Workers</a>. This worker has two main functions:</p><ul><li><p>It fetches data from the backend API using GraphQL, and then transforms it.</p></li><li><p>It provides a public <a href="https://developers.cloudflare.com/radar">REST API</a> that anyone can use, including Radar.</p></li></ul><p>Using a worker in front of our core API allows us to easily add and separate microservices, and also adds notable features like:</p><ul><li><p>Cloudflare's <a href="https://developers.cloudflare.com/workers/runtime-apis/cache/">Cache API</a> allows finer control over what to cache and for how long and supports POST requests and customizable cache control headers, which we use.</p></li><li><p>Stale responses using <a href="https://developers.cloudflare.com/r2/">R2</a>. When the backend API cannot serve a request for some reason, and there’s a stale response cached, it’ll be served directly from R2, giving end users a better experience.</p></li><li><p><a href="https://en.wikipedia.org/wiki/Comma-separated_values">CSV</a> and <a href="https://en.wikipedia.org/wiki/JSON">JSON</a> output formats. The CSV format is convenient and makes it easier for data scientists, analysts, and others to use the API and consume our API data directly from other tools.</p></li></ul>
    <div>
      <h3>Open sourcing our OpenAPI 3 schema generator and validator</h3>
      <a href="#open-sourcing-our-openapi-3-schema-generator-and-validator">
        
      </a>
    </div>
    <p>One last feature on the frontend API is <a href="https://spec.openapis.org/oas/latest.html">OpenAPI 3</a> support. We automatically generate an OpenAPI schema and validate user input with it. This is done through a custom library that we built on top of <a href="https://github.com/kwhitley/itty-router">itty-router</a>, which we also use. Today we’re open sourcing this work.</p><p><a href="https://github.com/cloudflare/itty-router-openapi">itty-router-openapi</a> provides an easy and compact OpenAPI 3 schema generator and validator for Cloudflare Workers. Check our <a href="https://github.com/cloudflare/itty-router-openapi">GitHub repository</a> for more information and details on how to use it.</p>
    <div>
      <h3>Developer’s Documentation</h3>
      <a href="#developers-documentation">
        
      </a>
    </div>
    <p>Today we’re also launching our developer’s <a href="https://developers.cloudflare.com/radar">documentation pages for the Radar API</a> where you can find more information about our data license, basic concepts, how to get started and the available API methods. Cloudflare Radar's API is free, allowing academics, data sleuths and other web enthusiasts to investigate Internet usage across the globe, based on data from our global network.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1GbwacvF3M7mVj6kovY0x3/c9e8c3f3b7d2ef7b364f6740bfec2760/image6-7.png" />
            
            </figure><p>To facilitate using our API, we also put together a <a href="https://colab.research.google.com/github/cloudflare/radar-notebooks/blob/main/notebooks/example.ipynb">Colab Notebook template</a> that you can play with, copy and expand to your use case.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5IQocyWiVv1U9ecurP6AOg/1f6336488129f9f3ae6e804d15526d39/image7-4.png" />
            
            </figure>
    <div>
      <h2>The Radar App</h2>
      <a href="#the-radar-app">
        
      </a>
    </div>
    <p>The Radar App is the code that runs in your browser. We've talked about Remix, but what else do we use?</p><p>Radar relies on a lot of <b>data visualizations</b>. Things like charts and maps are essential to us. We decided to build our reusable library of visualization components on top of two other frameworks: <a href="https://airbnb.io/visx/">visx</a>, a "collection of expressive, low-level visualization primitives for React," <a href="https://d3js.org/">D3</a>, a powerful JavaScript library for manipulating the DOM based on data, and <a href="https://maplibre.org/">MapLibre</a>, an open-source map visualization stack.</p><p>Here’s one of our visualization components in action. We call it the “PewPew map”.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ENMTCsc23Vq5Of80vVqBP/fd8b4bab134c536c73f34c9ced3db670/image5-12.png" />
            
            </figure><p>And here’s the Remix React code for it, whenever we need to use it in a page:</p>
            <pre><code>&lt;Card.Section
    title={t("card.attacks.title")}
    description={t("card.attacks.description")}
  &gt;
    &lt;Flex gap={spacing.medium} align="center" justify="flex-end"&gt;
      &lt;SegmentedControl
        label="Sort order:"
        name="attacksDirection"
        value={attacksDirection}
        options={[
          { label: t("common.source"), value: "ORIGIN" },
          { label: t("common.target"), value: "TARGET" },
        ]}
      onChange={({ target }: any) =&gt; setAttacksDirection(target.value)}
      /&gt;
    &lt;/Flex&gt;

    &lt;Components.AttacksCombinedChart
      ssr={ssr}
      height={400}
      direction={attacksDirection}
    /&gt;
  &lt;/Card.Section&gt;</code></pre>
            
    <div>
      <h3>SVGs</h3>
      <a href="#svgs">
        
      </a>
    </div>
    <p>Another change we made to Radar was switching our images and graphical assets to <a href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">Scalable Vector Graphics</a>. SVGs are great because they're essentially a declarative graphics language. They're XML text files with vectorial information. And so, they can be easily manipulated, transformed, stored, or indexed, and of course, they can be rendered at any size, producing beautiful, crisp results on any device and resolution.</p><p>SVGs are also extremely small and efficient in size compared to bitmap formats and support <a href="https://www.w3.org/TR/SVGTiny12/i18n.html">internationalization</a>, making them easier to translate to other languages (localization), thus providing better <a href="https://www.a11yproject.com/">accessibility</a>.</p><p>Here’s an example of a Radar Bubble Chart, inspected, where you can see the SVG code and the  strings embedded.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1PJgCboffVUKXDjtRTPFov/b07a3c91bc2e3ad91bbfb150ace899bc/image17.png" />
            
            </figure>
    <div>
      <h3>Cosmos</h3>
      <a href="#cosmos">
        
      </a>
    </div>
    <p><a href="https://reactcosmos.org/">React Cosmos</a> is a "sandbox for developing and testing UI components in isolation." We wanted to use Cosmos with Radar 2.0 because it's the perfect project for it:</p><ol><li><p>It has a lot of visual components; some are complex and have many configuration options and features.</p></li><li><p>The components are highly reusable across multiple pages in different contexts with different data.</p></li><li><p>We have a multidisciplinary team; everyone can send a pull request and add or change code in the frontend.</p></li></ol><p>Cosmos acts as a component library where you can see our palette of ready-to-use visualizations and widgets, from simple buttons to complex charts, and you play with their options in real-time and see what happens. Anyone can do it, not only designers or engineers but also other project stakeholders. This effectively improves team communications and makes contributing and iterating quickly.</p><p>Here’s a screenshot of our Cosmos in action:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5LBKvrUOyalqcEkuie59dB/c571741695acf093204103d94ce6ebd5/image1-57.png" />
            
            </figure>
    <div>
      <h2>Continuous integration and development</h2>
      <a href="#continuous-integration-and-development">
        
      </a>
    </div>
    <p>Continuous integration is important for any team doing modern software. Cloudflare Pages provides multiple options to work with CI tools using direct uploads, out of the box. The team has put up <a href="https://developers.cloudflare.com/pages/how-to/use-direct-upload-with-continuous-integration/">documentation and examples</a> on how to do that with GitHub Actions, CircleCI, and Travis, but you can use others.</p><p>In our case, we use BitBucket and TeamCity internally to build and deploy our releases. Our workflow automatically builds, tests, and deploys Radar 2.0 within minutes on an approved PR and follow-up merge.</p><p>Unit tests are done with <a href="https://vitest.dev/">Vitest</a> and E2E tests with <a href="https://playwright.dev/">Playwright</a>. Visual Regression testing is planned and <a href="https://playwright.dev/docs/test-snapshots">Playwright can also help with that</a>.</p><p>Furthermore, we have multiple environments to stage and test our releases before they go live to production. Our <a href="https://www.cloudflare.com/learning/serverless/glossary/what-is-ci-cd/">CI/CD</a> setup makes it easy to switch from one environment to the other or quickly roll back any undesired deployment.</p><p>Again Cloudflare Pages makes it easy to do this using <a href="https://developers.cloudflare.com/pages/platform/preview-deployments/">Preview deployments</a>, aliases, or <a href="https://developers.cloudflare.com/pages/platform/branch-build-controls/">Branch build controls</a>. The same is true for regular Workers using <a href="https://developers.cloudflare.com/workers/platform/environments/">Environments</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Q9hXo3Iz04CtXpmaALbzI/0c40d53fd560b42f08098a00d00157e5/image19.png" />
            
            </figure>
    <div>
      <h3>Fast previews and notifications</h3>
      <a href="#fast-previews-and-notifications">
        
      </a>
    </div>
    <p>Radar 1.0 wasn't particularly fast doing CI/CD, we confess. We had a few episodes when a quick fix could take some good 30 minutes from committing the code to deployment, and we felt frustrated about it.</p><p>So we invested a lot in ensuring that the new CI would be fast, efficient, and furious.</p><p>One cool thing we ended up doing was fast preview links on any commit pushed to the code repository. Using a combination of intelligent caching during builds and doing asynchronous tests when the commit is outside the normal release branches, we were able to shorten the deployment time to seconds.</p><p>This is the notification we get in our chat when anyone pushes code to any branch:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5moqSeGzEAlNHnEJVoxwbO/900325ae89a90528c250ffd3fa4c4e0e/image8-2.png" />
            
            </figure><p>Anyone can follow a thread for a specific branch in the chat and get notified on new changes when they happen.</p><p>Blazing-fast builds, preview links and notifications are game-changers. An engineer can go from an idea or a quick fix to showing the result on a link to a product manager or another team member. Anyone can quickly click the link to see the changes on a fully working end-to-end version of Radar.</p>
    <div>
      <h2>Accessibility and localization</h2>
      <a href="#accessibility-and-localization">
        
      </a>
    </div>
    <p>Cloudflare is committed to web accessibility. Recently we announced how we upgraded Cloudflare’s Dashboard to <a href="/project-a11y/">adhere to industry accessibility standards</a>, but this premise is valid for all our properties. The same is true for localization. In 2020, we <a href="/internationalizing-the-cloudflare-dashboard/">internationalized</a> our Dashboard and added support for new languages and locales.</p><p>Accessibility and localization go hand in hand and are both important, but they are also different. The <a href="https://www.w3.org/TR/WCAG21/">Web Content Accessibility Guidelines</a> define many best practices around accessibility, including using <a href="https://color.cloudflare.design/">color</a> and contrast, tags, SVGs, shortcuts, gestures, and many others. The <a href="https://www.a11yproject.com/">A11Y project page</a> is an excellent resource for learning more.</p><p>Localization, though, also known as <a href="https://en.wikipedia.org/wiki/Internationalization_and_localization">L10n</a>, is more of a technical requirement when you start a new project. It's about making sure you choose the right set of libraries and frameworks that will make it easier to add new translations without engineering dependencies or code rewrites.</p><p>We wanted Radar to perform well on both fronts. Our design system takes Cloudflare's design and brand <a href="https://cloudflare.design/">guidelines</a> seriously and adds as many A11Y good practices as possible, and the app is fully aware of localization strings across its pages and UI components.</p><p>Adding a new language is as easy as translating a single JSON file. Here's a snippet of the en-US.json file with the default American English strings:</p>
            <pre><code>{
  "abbr.asn": "Autonomous System Number",
  "actions.chart.download.csv": "Download chart data in CSV",
  "actions.chart.download.png": "Download chart in PNG Format",
  "actions.chart.download.svg": "Download chart in SVG Format",
  "actions.chart.download": "Download chart",
  "actions.chart.maximize": "Maximize chart",
  "actions.chart.minimize": "Minimize chart",
  "actions.chart.share": "Share chart",
  "actions.download.csv": "Download CSV",
  "actions.download.png": "Download PNG",
  "actions.download.svg": "Download SVG",
  "actions.share": "Share",
  "alert.beta.link": "Radar Classic",
  "alert.beta.message": "Radar 2.0 is currently in Beta. You can still use {link} during the transition period.",
  "card.about.cloudflare.p1": "Cloudflare, Inc. ({website} / {twitter}) is on a mission to help build a better Internet. Cloudflare's suite of products protects and accelerates any Internet application online without adding hardware, installing software, or changing a line of code. Internet properties powered by Cloudflare have all web traffic routed through its intelligent global network, which gets smarter with every request. As a result, they see significant improvement in performance and a decrease in spam and other attacks. Cloudflare was named to Entrepreneur Magazine's Top Company Cultures 2018 list and ranked among the World's Most Innovative Companies by Fast Company in 2019.",
  "card.about.cloudflare.p2": "Headquartered in San Francisco, CA, Cloudflare has offices in Austin, TX, Champaign, IL, New York, NY, San Jose, CA, Seattle, WA, Washington, D.C., Toronto, Dubai, Lisbon, London, Munich, Paris, Beijing, Singapore, Sydney, and Tokyo.",
  "card.about.cloudflare.title": "About Cloudflare",
...</code></pre>
            <p>You can expect us to release Radar in other languages soon.</p>
    <div>
      <h2>Radar Reports and Jupyter notebooks</h2>
      <a href="#radar-reports-and-jupyter-notebooks">
        
      </a>
    </div>
    <p><a href="https://radar.cloudflare.com/reports">Radar Reports</a> are documents that use data exploration and storytelling to analyze a particular theme in-depth. Some reports tend to get updates from time to time. Examples of Radar Reports are our quarterly <a href="https://radar.cloudflare.com/reports/ddos-2022-q3">DDoS Attack Trends</a>, or the <a href="https://radar.cloudflare.com/reports/ipv6">IPv6 adoption</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1YdlIovSNvHy7YITYdxiCf/50804dd483563fd236d7b8ab6f05b8b1/image4-23.png" />
            
            </figure><p>The source of these Reports is <a href="https://jupyter.org/">Jupyter Notebooks</a>. Our Data Science team works on some use-case or themes with other stakeholders using our internal Jupyter Hub tool. After all the iteration and exploration are done, and the work is signed off, a notebook is produced.</p><p>A Jupyter Notebook is a <a href="https://ipython.org/ipython-doc/3/notebook/nbformat.html">JSON document</a> containing text, source code, rich media such as images or charts, and other metadata. It is the de facto standard for presenting data science projects, and every data scientist uses it.</p><p>With Radar 1.0, converting a Jupyter Notebook to a Radar page was a lengthy and manual process implicating many engineering and design resources and causing much frustration to everyone involved. Even updating an already-published notebook would frequently cause trouble for us.</p><p>Radar 2.0 changed all of this. We now have a fully automated process that takes a Jupyter Notebook and, as long as it's designed using a list of simple rules and internal guidelines, converts it automatically, hosts the resulting HTML and assets in an R2 bucket, and publishes it on the <a href="https://radar.cloudflare.com/reports">Reports</a> page.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5iS9T8mn23CDjg4nFfpqWT/efdeaa9d9b3ee645cd21b5014af13da1/image9-2.png" />
            
            </figure><p>The conversion to HTML takes into account our design system and UI components, and the result is a <a href="https://radar.cloudflare.com/reports/ddos-2022-q3">beautiful document</a>, usually long-form, perfectly matching Radar's look and feel.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4oXmxPoDH1NDJqsBaIRKK4/9cdd9229bd835c6722fb06c6350fbf0f/image13.png" />
            
            </figure><p>We will eventually open-source this tool so that anyone can use it.</p>
    <div>
      <h2>More Cloudflare, less to worry about</h2>
      <a href="#more-cloudflare-less-to-worry-about">
        
      </a>
    </div>
    <p>We gave examples of using Cloudflare's products and features to build your next-gen app without worrying too much about things that aren't core to your business or logic. A few are missing, though.</p><p>Once the app is up and running, you must protect it from bad traffic and malicious actors. Cloudflare offers you <a href="https://www.cloudflare.com/ddos/">DDoS</a>, <a href="https://www.cloudflare.com/waf/">WAF</a>, and <a href="https://www.cloudflare.com/products/bot-management/">Bot Management</a> protection out of the box at a click's distance.</p><p>For example, here are some of our security rules. This is traffic we don't have to worry about in our app because Cloudflare detects it and acts on it according to our rules.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/14D17IdfhuHOyzPeFPePyP/faa894184818241d101551d1815bf0d7/image10-1.png" />
            
            </figure><p>Another thing we don't need to worry about is redirects from the old site to the new one. Cloudflare has a feature called <a href="https://developers.cloudflare.com/rules/url-forwarding/bulk-redirects/create-dashboard/">Bulk Redirects</a>, where you can easily create redirect lists directly on the dashboard.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ieVLmpoynt1H7lDWykfs8/ca4136845ea8fa4f66166a3be7fa57b5/image15.png" />
            
            </figure><p>It's also important to mention that every time we talk about what you can do using our Dashboard, we're, in fact, also saying you can do precisely the same using <a href="https://api.cloudflare.com/">Cloudflare's APIs</a>. Our Dashboard is built entirely on top of them. And if you're the infrastructure as code kind of person, we have you covered, too; you can use the <a href="https://developers.cloudflare.com/terraform/tutorial/">Cloudflare Terraform provider</a>.</p><p>Deploying and managing Workers, R2 buckets, or Pages sites is obviously scriptable too. <a href="https://github.com/cloudflare/wrangler">Wrangler</a> is the command-line tool to do this and more, and it goes the extra mile to allow you to run your full app <a href="https://developers.cloudflare.com/workers/wrangler/commands/#dev">locally</a>, emulating our stack, on your computer, before deploying.</p>
    <div>
      <h2>Final words</h2>
      <a href="#final-words">
        
      </a>
    </div>
    <p>We hope you enjoyed this Radar team write-up and were inspired to build your next app on top of our <a href="/welcome-to-the-supercloud-and-developer-week-2022/">Supercloud</a>. We will continue improving and innovating on Radar 2.0 with new features, share our findings and open-sourcing our tools with you.</p><p>In the meantime, we opened a <a href="https://discord.gg/cloudflaredev">Radar room</a> on our Developers Discord Server. Feel free to <a href="https://discord.gg/cloudflaredev">join</a> it and ask us questions; the team is eager to receive feedback and discuss web technology with you.</p><p>You can also follow us <a href="https://twitter.com/cloudflareradar">on Twitter</a> for more Radar updates.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Radar]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">2H0o8Ld6ebN4hs7uhm1ELW</guid>
            <dc:creator>Celso Martinho</dc:creator>
            <dc:creator>Nuno Pereira</dc:creator>
            <dc:creator>Sofia Cardita</dc:creator>
            <dc:creator>Gabriel Massadas</dc:creator>
        </item>
        <item>
            <title><![CDATA[And here's another one: the Next.js Edge Runtime becomes the fourth full-stack framework supported by Cloudflare Pages]]></title>
            <link>https://blog.cloudflare.com/next-on-pages/</link>
            <pubDate>Mon, 24 Oct 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ You can now deploy SSR Next.js applications to the Cloudflare Pages platform ⚡️ ]]></description>
            <content:encoded><![CDATA[ <p></p><p>You can now deploy <a href="https://nextjs.org/">Next.js</a> applications which opt in to the <a href="https://nextjs.org/docs/api-reference/edge-runtime">Edge Runtime</a> on Cloudflare Pages. Next.js is the fourth full-stack web framework that the Pages platform officially supports, and it is <a href="https://almanac.httparchive.org/en/2022/jamstack">one of the most popular in the 'Jamstack-y' space</a>.</p><p>Cloudflare Pages started its journey as a platform for static websites, but with <a href="/cloudflare-pages-goes-full-stack/">last year's addition of Pages Functions</a> powered by Cloudflare Workers, the platform has progressed to support an even more diverse range of use cases. Pages Functions allows developers to sprinkle in small pieces of server-side code with its simple file-based routing, or, as we've seen with the adoption from other frameworks (namely <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site/">SvelteKit</a>, <a href="https://developers.cloudflare.com/pages/framework-guides/remix/">Remix</a> and <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-qwik-site/">Qwik</a>), Pages Functions can be used to power your entire full-stack app. The folks behind <a href="/remix-on-cloudflare-pages/">Remix previously talked about the advantages of adopting open standards</a>, and we've seen this again with Next.js' Edge Runtime.</p>
    <div>
      <h2>Next.js' Edge Runtime</h2>
      <a href="#next-js-edge-runtime">
        
      </a>
    </div>
    <p><a href="https://nextjs.org/docs/api-reference/edge-runtime">Next.js' Edge Runtime</a> is an experimental mode that developers can opt into which results in a different type of application being built. Previously, Next.js applications which relied on server-side rendering (SSR) functionality had to be deployed on a Node.js server. Running a Node.js server has significant overhead, and our Cloudflare Workers platform was fundamentally built on a different technology, <a href="https://developers.cloudflare.com/workers/learning/how-workers-works/">V8</a>.</p><p>However, when Next.js <a href="https://vercel.com/blog/introducing-the-edge-runtime">introduced the Edge Runtime mode</a> in June 2022, we saw the opportunity to bring this widely used framework to our platform. We're very excited that this is being developed in coordination with the <a href="/introducing-the-wintercg/">WinterCG</a> standards to ensure interoperability across the various web platforms and to ensure that developers have the choice on where they run their business, without fearing any significant vendor lock-in.</p><p>It’s important to note that some existing Next.js apps built for Node.js won't immediately work on Pages. If your application relies on any Node.js built-ins or long-running processes, then Pages may not support your app with today’s announcement as we're working on <a href="/node-js-support-cloudflare-workers/">expanding our support for Node.js</a>.</p><p>However, we see the migration to the Edge Runtime as an effort that's worthy of investment, to run your applications, well, on the edge! These applications are cheaper to run, respond faster to users and have the latest features that full-stack frameworks offer. We're seeing increased interest in third-party npm packages and libraries that support standardized runtimes, and in combination with Cloudflare's data products (e.g. <a href="https://www.cloudflare.com/products/workers-kv/">KV</a>, <a href="https://www.cloudflare.com/products/durable-objects/">Durable Objects</a> and <a href="/whats-new-with-d1/">D1</a>), we're confident that the edge is going to be the first place that people will want to deploy applications going forward.</p>
    <div>
      <h2>Deploy your Next.js app to Cloudflare Pages</h2>
      <a href="#deploy-your-next-js-app-to-cloudflare-pages">
        
      </a>
    </div>
    <p>Let’s walk through an example, creating a new Next.js application that opts into this Edge Runtime and deploying it to Cloudflare Pages.</p>
            <pre><code>npx create-next-app@latest my-app</code></pre>
            <p>This will create a new Next.js app in the <code>my-app</code> folder. The default template comes with a traditional Node.js powered API route, so let's update that to instead use the Edge Runtime.</p>
            <pre><code>// pages/api/hello.js

// Next.js Edge API Routes: https://nextjs.org/docs/api-routes/edge-api-routes

export const config = {
  runtime: 'experimental-edge',
}

export default async function (req) {
  return new Response(
    JSON.stringify({ name: 'John Doe' }),
    {
      status: 200,
      headers: {
        'Content-Type': 'application/json'
      }
    }
  )
}</code></pre>
            <p>Thanks to the Edge Runtime adopting the Web API standards, if you've ever written a Cloudflare Worker before, this might look familiar.</p><p>Next, we can update the global <code>next.config.js</code> configuration file to use the Edge Runtime. This will enable us to use <a href="https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props">the <code>getServerSideProps()</code> API</a> and server-side render (SSR) our webpages.</p>
            <pre><code>// next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    runtime: 'experimental-edge',
  },
  reactStrictMode: true,
  swcMinify: true,
}
module.exports = nextConfig</code></pre>
            <p>Finally, we're ready to deploy the project. Publish it to a GitHub or GitLab repository, create a new Pages project, and select "Next.js" from the list of framework presets. This will configure your project to use <a href="https://github.com/cloudflare/next-on-pages">the <code>@cloudflare/next-on-pages</code> CLI</a> which builds and transforms your project into something we can deploy on Pages. Navigate to the project settings and add an environment variable, <code>NODE_VERSION</code> set to <code>14</code> or greater, as well as the following compatibility flags: <code>streams_enable_constructors</code> and <code>transformstream_enable_standard_constructor</code>. You should now be able to deploy your Next.js application. If you want to read more, you can find a detailed guide in <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-nextjs-site/">our documentation</a>.</p>
    <div>
      <h2>How it runs on Cloudflare Pages</h2>
      <a href="#how-it-runs-on-cloudflare-pages">
        
      </a>
    </div>
    
    <div>
      <h3>Compatibility Dates and Compatibility Flags</h3>
      <a href="#compatibility-dates-and-compatibility-flags">
        
      </a>
    </div>
    <p>Cloudflare Workers has solved the versioning problem by <a href="/backwards-compatibility-in-cloudflare-workers/">introducing compatibility dates and compatibility flags</a>. While it has been in beta, Pages Functions has always defaulted to using the oldest version of the Workers runtime. We've now introduced controls to allow developers to set these dates and flags on their Pages projects environments.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1qm1x1OaVxhfm80IgIBhYd/e7bff63f0f14ee4a08ea82b14f3c8a53/image2-12.png" />
            
            </figure><p>By keeping this date recent, you are able to opt in to the latest features and bug fixes that the Cloudflare Workers runtime offers, but equally, you're completely free to keep the date on whatever works for you today, and we'll continue to support the functionality at that point in time, forever. We also allow you to set these dates for your production and preview environments independently which will let you test these changes out safely in a preview deployment before rolling it out in production.</p><p>We've been working on adding more support for the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Streams_API">Streams API</a> to the Workers Runtime, and some of this functionality is gated behind the flags we added to the project earlier. <a href="https://developers.cloudflare.com/workers/platform/compatibility-dates/#change-history">These flags are currently scheduled to graduate</a> and become on-by-default on a future compatibility date, 2022-11-30.</p>
    <div>
      <h3>The <code>@cloudflare/next-on-pages</code> CLI</h3>
      <a href="#the-cloudflare-next-on-pages-cli">
        
      </a>
    </div>
    <p>Vercel introduced the <a href="https://vercel.com/blog/build-output-api">Build Output API</a> in July 2022 as a "zero configuration" directory structure which the Vercel platform inherently understands and can deploy. We've decided to hook into this same API as a way to build Next.js projects consistently that we can understand and deploy.</p><p>The <a href="https://github.com/cloudflare/next-on-pages">open-source</a> <code>[@cloudflare/next-on-pages](https://github.com/cloudflare/next-on-pages)</code> <a href="https://github.com/cloudflare/next-on-pages">CLI</a> runs <code>npx vercel build</code> behind the scenes, which produces a <code>.vercel/output</code> directory. This directory conforms to the <a href="https://vercel.com/docs/build-output-api/v3">Build Output API</a>, and of particular interest, contains a <code>config.json</code>, <code>static</code> folder and folder of <code>functions</code>. The <code>@cloudflare/next-on-pages</code> CLI then parses this <code>config.json</code> manifest, and combines all the <code>functions</code> into a single <a href="https://developers.cloudflare.com/pages/platform/functions/#advanced-mode">Pages Functions 'advanced mode' <code>_worker.js</code></a>.</p><p>At this point, the build is finished. Pages then automatically picks up this <code>_worker.js</code> and deploys it with Pages Functions atop the <code>static</code> directory.</p><p>Although currently just an implementation detail, we opted to use this Build Output API for a number of reasons. We’re also exploring other similar functionality natively on the Pages platform. We already have one "magical" directory, the <code>functions</code> directory which we use for the file-based routing of Pages Functions. It's possible that we offer other fixed directory structures which would reduce the need for configuration of any projects using frameworks which adopt the API. Let us know <a href="https://discord.com/channels/595317990191398933/910978223968518144">in Discord</a> if you have any thoughts or preferences on what you would like to see!</p><p>Additionally, if more full-stack frameworks do adopt Vercel's Build Output API, we may have automatic support for them running on Pages with this CLI. We've only been experimenting with Next.js here so far (and SvelteKit, Remix and Qwik all have their own way of building their projects on Pages at the moment), but it's possible that in the future we may converge on a standard approach which could be shared between frameworks and platforms. We're excited to see how this might transpire. Again, <a href="https://discord.com/channels/595317990191398933/910978223968518144">let us know</a> if you have thoughts!</p>
    <div>
      <h3>Experimental webpack minification</h3>
      <a href="#experimental-webpack-minification">
        
      </a>
    </div>
    <p>As part of the compilation from <code>.vercel/output/functions</code> to an <code>_worker.js</code>, the <code>@cloudflare/next-on-pages</code> CLI can perform an experimental minification to give you more space for your application to run on Workers. Right now, most accounts are <a href="https://developers.cloudflare.com/workers/platform/limits/#worker-size">limited to a maximum script size of 1MB</a> (although this can be raised in some circumstances—<a href="https://docs.google.com/forms/d/e/1FAIpQLSd_fwAVOboH9SlutMonzbhCxuuuOmiU1L_I5O2CFbXf_XXMRg/viewform">get in touch</a>!). You can ordinarily fit quite a lot of code in this, but one thing notable about Next.js' build process at the moment is that it creates webpack-compiled, fully-formed and fully-isolated functions scripts in each of the directories in <code>.vercel/output/functions</code>. This means that each function ends up looking something like this:</p>
            <pre><code>let _ENTRIES = {};
(() =&gt; {
  // webpackBootstrap
})();

(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([100], {

  123: (() =&gt; {
    // webpack chunk #123
  }),
  234: (() =&gt; {
    // webpack chunk #234
  }),
  345: (() =&gt; {
    // webpack chunk #345
  }),

  // …lots of webpack chunks…

}, () =&gt; {
  // webpackRuntimeModules
}]);

export default {
  async fetch(request, env, ctx) {
    return _ENTRIES['some_function'].default.call(request);
  }
}</code></pre>
            <p>The script contains everything that's needed to deploy this function, and most of the logic exists in these webpack chunks, but that means that each function has a lot of code shared with its siblings. Quickly, you'll reach the 1MB limit, if you naively deployed all these functions together.</p><p>Our <code>@cloudflare/next-on-pages --experimental-minify</code> CLI argument deals with this problem by analyzing webpack chunks which are re-used in multiple places in this <code>.vercel/output/functions</code> directory and extracts out that code to a common place. This allows our compiler (esbuild) to efficiently combine this code, without duplicating it in all of these places. This process is experimental for the time being, while we look to make this as efficient as possible, without introducing any bugs as a result. Please <a href="https://github.com/cloudflare/next-on-pages/issues/">file an issue on GitHub</a> if you notice any difference in behavior when using <code>--experimental-minify</code>.</p>
    <div>
      <h2>What's next?</h2>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>Pages Functions has been in beta for almost a year, and we're very excited to say that general availability is just around the corner. We're polishing off the last of the remaining features which includes analytics, logging, and billing. In fact, for billing, we recently made <a href="https://developers.cloudflare.com/pages/platform/functions/billing/">the announcement of how you'll be able to use the Workers Paid plan to remove the request limits of the Pages Functions beta from November 15</a>.</p><p>Finally, we're also looking at how we can bring Wasm support to Pages Functions which will unlock ever more use-cases for your full-stack applications. Stay tuned for more information on how we'll be offering this soon.</p><p>Try creating a Next.js Edge Runtime application and deploying it to Cloudflare Pages with the example above or by following <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-nextjs-site/">the guide in our documentation</a>. Let us know if you have any questions or face any issues in <a href="https://discord.com/channels/595317990191398933/910978223968518144">Discord</a> or on <a href="https://github.com/cloudflare/next-on-pages/issues/">GitHub</a>, and please report any quirks of the <code>--experimental-minify</code> argument. As always, we're excited to see what you build!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Full Stack]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Connectivity Cloud]]></category>
            <guid isPermaLink="false">6H5XhDzpuEfNdN2fJj9PO2</guid>
            <dc:creator>Greg Brimble</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare Pages gets even faster with Early Hints]]></title>
            <link>https://blog.cloudflare.com/early-hints-on-cloudflare-pages/</link>
            <pubDate>Fri, 07 Oct 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ Announcing support for sending Early Hints from your Cloudflare Pages projects ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Last year, we demonstrated what we meant by “lightning fast”, <a href="/cloudflare-pages-is-lightning-fast/">showing Pages' first-class performance</a> in all parts of the world, and today, we’re thrilled to announce an integration that takes this commitment to speed even further – introducing Pages support for Early Hints! Early Hints allow you to unblock the loading of page critical resources, ahead of any slow-to-deliver HTML pages. Early Hints can be used to improve the loading experience for your visitors by significantly reducing key performance metrics such as the largest contentful paint (LCP).</p>
    <div>
      <h2>What is Early Hints?</h2>
      <a href="#what-is-early-hints">
        
      </a>
    </div>
    <p><a href="/early-hints/">Early Hints</a> is a new feature of the Internet which is <a href="https://developer.chrome.com/blog/early-hints/#implementing-early-hints">supported in Chrome since version 103</a>, and that <a href="/early-hints-performance/">Cloudflare made generally available</a> for websites using our network. Early Hints supersedes <a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY/m/vOWBKZGoAQAJ">Server Push</a> as a mechanism to "hint" to a browser about critical resources on your page (e.g. fonts, CSS, and above-the-fold images). The browser can immediately start loading these resources before waiting for a full HTML response. This uses time that was otherwise previously wasted! Before Early Hints, no work could be started until the browser received the first byte of the response. Now, the browser can fill this time usefully when it was previously sat waiting. Early Hints can bring significant improvements to the performance of your website, particularly for metrics such as LCP.</p>
    <div>
      <h3>How Early Hints works</h3>
      <a href="#how-early-hints-works">
        
      </a>
    </div>
    <p>Cloudflare caches any <code>preload</code> and <code>preconnect</code> type <code>Link</code> headers sent from your 200 OK response, and sends them early for any subsequent requests as a 103 Early Hints response.</p><p>In practical terms, an HTTP conversation now looks like this:</p><p><b>Request</b></p>
            <pre><code>GET /
Host: example.com</code></pre>
            <p><b>Early Hints Response</b></p>
            <pre><code>103 Early Hints
Link: &lt;/styles.css&gt;; rel=preload; as=style</code></pre>
            <p><b>Response</b></p>
            <pre><code>200 OK
Content-Type: text/html; charset=utf-8
Link: &lt;/styles.css&gt;; rel=preload; as=style

&lt;html&gt;
&lt;!-- ... --&gt;
&lt;/html&gt;</code></pre>
            
    <div>
      <h2>Early Hints on Cloudflare Pages</h2>
      <a href="#early-hints-on-cloudflare-pages">
        
      </a>
    </div>
    <p>Websites hosted on Cloudflare Pages can particularly benefit from Early Hints. If you're using <a href="/cloudflare-pages-goes-full-stack/">Pages Functions</a> to generate dynamic server-side rendered (SSR) pages, there's a good chance that Early Hints will make a significant improvement on your website.</p>
    <div>
      <h3>Performance Testing</h3>
      <a href="#performance-testing">
        
      </a>
    </div>
    <p>We created a simple demonstration e-commerce website in order to evaluate the performance of Early Hints.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/77w2Mw6t1Oku0C2uFulZvH/09416e738ac559f7977e8d76614ad0af/image2-7.png" />
            
            </figure><p>This landing page has the price of each item, as well as a remaining stock counter. The page itself is just hand-crafted HTML and CSS, but these pricing and inventory values are being templated in live for every request with <a href="https://developers.cloudflare.com/pages/platform/functions/">Pages Functions</a>. To simulate loading from an external data-source (possibly backed by <a href="https://developers.cloudflare.com/workers/runtime-apis/kv/">KV</a>, <a href="https://developers.cloudflare.com/workers/runtime-apis/durable-objects/">Durable Objects</a>, <a href="/introducing-d1/">D1</a>, or even an external API like Shopify) we've added a fixed delay before this data resolves. We include <code>preload</code> links in our response to some critical resources:</p><ul><li><p>an external CSS stylesheet,</p></li><li><p>the image of the t-shirt,</p></li><li><p>the image of the cap,</p></li><li><p>and the image of the keycap.</p></li></ul><p>The very first request makes a waterfall like you might expect. The first request is held blocked for a considerable amount of time while we resolve this pricing and inventory data. Once loaded, the browser parses the HTML, pulls out the external resources, and makes subsequent requests for their contents. The CSS and images extend the loading time considerably given their large dimensions and high quality. The largest contentful paint (LCP) occurs when the t-shirt image loads, and the document finishes once all requests are fulfilled.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/24N9FzqlRfCtlSGuPlUPic/8650a76a958f4946e20c6680cb01ac84/image1-10.png" />
            
            </figure><p>Subsequent requests are where things get interesting! These <code>preload</code> links are cached on Cloudflare's global network, and are sent ahead of the document in a 103 Early Hints response. Now, the waterfall looks much different. The initial request goes out the same, but now, requests for the CSS and images slide much further left since they can be started as soon as the 103 response is delivered. The browser starts fetching those resources while waiting for the original request to finish server-side rendering. The LCP again occurs once the t-shirt image has loaded, but this time, it's brought forward by <b>530ms</b> because it started loading <b>752ms</b> faster, and the document is fully loaded <b>562ms</b> faster, again because the external resources could all start loading faster.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Z05bcWVJtHoo9kkgl6IJj/941868fd08a160c81c61be2590b19107/image3-3.png" />
            
            </figure><p>The final four requests (highlighted in yellow) come back as 304 Not Modified responses using a <code>If-None-Match</code> header. By default, Cloudflare Pages requires the browser to confirm that all assets are fresh, and so, on the off chance that they were updated between the Early Hints response and when they come to being used, the browser is checking if they have changed. Since they haven't, there's no contentful body to download, and the response completes quickly. This can be avoided by setting a custom <code>Cache-Control</code> header on these assets using <a href="https://developers.cloudflare.com/pages/platform/headers/">a <code>_headers</code> file</a>. For example, you could cache these images for one minute with a rule like:</p>
            <pre><code># _headers

/*.png
  Cache-Control: max-age=60</code></pre>
            <p>We could take this performance audit further by exploring other features that Cloudflare offers, such as <a href="https://support.cloudflare.com/hc/en-us/articles/200168196">automatic CSS minification</a>, <a href="https://developers.cloudflare.com/images/">Cloudflare Images</a>, and <a href="https://developers.cloudflare.com/images/image-resizing/">Image Resizing</a>.</p><p>We already serve Cloudflare Pages from <a href="/cloudflare-pages-is-lightning-fast/">one of the fastest networks in the world</a> — Early Hints simply allows developers to take advantage of our global network even further.</p>
    <div>
      <h2>Using Early Hints and Cloudflare Pages</h2>
      <a href="#using-early-hints-and-cloudflare-pages">
        
      </a>
    </div>
    <p>The Early Hints feature on Cloudflare is currently restricted to caching <code>Link</code> headers in a webpage's response. Typically, this would mean that Cloudflare Pages users would either need to use the <code>_headers</code> file, or Pages Functions to apply these headers. However, for your convenience, we've also added support to transform any <code>&lt;link&gt;</code> HTML elements you include in your body into <code>Link</code> headers. This allows you to directly control the Early Hints you send, straight from the same document where you reference these resources – no need to come out of HTML to take advantage of Early Hints.</p><p>For example, for the following HTML document, will generate an Early Hints response:</p><p><b>HTML Document</b></p>
            <pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;link rel="preload" as="style" href="/styles.css" /&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;!-- ... --&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
            <p><b>Early Hints Response</b></p>
            <pre><code>103 Early Hints
Link: &lt;/styles.css&gt;; rel=preload; as=style</code></pre>
            <p>As previously mentioned, <code>Link</code> headers can also be set with a <a href="/custom-headers-for-pages/"><code>_headers</code> file</a> if you prefer:</p>
            <pre><code># _headers

/
  Link: &lt;/styles.css&gt;; rel=preload; as=style</code></pre>
            <p>Early Hints (and the automatic HTML <code>&lt;link&gt;</code> parsing) has already been enabled automatically for all <code>pages.dev</code> domains. If you have any custom domains configured on your Pages project, make sure to enable Early Hints on that domain in the Cloudflare dashboard under the "Speed" tab. More information can be found in our <a href="https://developers.cloudflare.com/pages/platform/early-hints/">documentation</a>.</p><p>Additionally, in the future, we hope to support the Smart Early Hints features. Smart Early Hints will enable Cloudflare to automatically generate Early Hints, even when no <code>Link</code> header or <code>&lt;link&gt;</code> elements exist, by analyzing website traffic and inferring which resources are important for a given page. We'll be sharing more about Smart Early Hints soon.</p><p>In the meantime, try out <a href="https://developers.cloudflare.com/pages/platform/early-hints/">Early Hints on Pages</a> today! Let us know how much of a loading improvement you see in <a href="https://discord.com/invite/cloudflaredev">our Discord server</a>.</p>
    <div>
      <h2>Watch on Cloudflare TV</h2>
      <a href="#watch-on-cloudflare-tv">
        
      </a>
    </div>
    <div></div><p></p> ]]></content:encoded>
            <category><![CDATA[Early Hints]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">3XJwgZh2bmW4PvjEU8BlCg</guid>
            <dc:creator>Greg Brimble</dc:creator>
        </item>
        <item>
            <title><![CDATA[Build your next big idea with Cloudflare Pages]]></title>
            <link>https://blog.cloudflare.com/big-ideas-on-pages/</link>
            <pubDate>Fri, 05 Aug 2022 16:30:00 GMT</pubDate>
            <description><![CDATA[ We believe every developer deserves to dream big without breaking the bank. That's why on Cloudflare Pages you can get unlimited requests, bandwidth, seats and projects --- for free! ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Have you ever had a surge of inspiration for a project? That feeling when you have a great idea – a big idea — that you just can’t shake? When all you can think about is putting your hands to your keyboard and hacking away? Building a website takes courage, creativity, passion and drive, and with <a href="/cloudflare-pages-ga/">Cloudflare Pages</a> we believe nothing should stand in the way of that vision.</p><p><b>Especially not a price tag.</b></p>
    <div>
      <h3>Big ideas</h3>
      <a href="#big-ideas">
        
      </a>
    </div>
    <p>We built Pages to be at the center of your developer experience – a way for you to get started right away without worrying about the heavy lift of setting up a fullstack app.  A quick commit to your git provider or direct upload to our platform, and your rich and powerful site is deployed to our <a href="/mid-2022-new-cities/">network of 270+ data centers in <b>seconds</b></a>. And above all, we built Pages to scale with you as you grow exponentially without getting hit by an unexpected bill.</p>
    <div>
      <h3>The limit does not exist</h3>
      <a href="#the-limit-does-not-exist">
        
      </a>
    </div>
    <p>We’re a platform that’s invested in your vision – no matter how wacky and wild (the best ones usually are!). That’s why for many parts of Pages we want your experience to be <i>limitless</i>.</p><p><b>Unlimited requests</b>: As your idea grows, so does your traffic. While thousands and millions of end users flock to your site, Pages is prepared to handle all of your traffic with no extra cost to you – even when millions turn to <i>billions.</i></p><p><b>Unlimited bandwidth:</b> As your traffic grows, you’ll need more bandwidth – and with Pages, we got you. If your site takes off in popularity one day, the next day shouldn’t be a cause for panic because of a nasty bill. It should be a day of celebration and pride. We’re giving unlimited bandwidth so you can keep your eyes focused on moving up and to the right.</p><p><b>Unlimited free seats:</b> With a rise in demand for your app, you’re going to need more folks working with you. We know from experience that more great ideas don’t just come from one person but a strong team of people. We want to be there every step of the way along with every person you want on this journey with you. Just because your team grows doesn’t mean your bill has to.</p><p><b>Unlimited projects</b>: With one great idea, means many more to follow. With Pages, you can deploy as many projects as you want – keep them coming! Not every idea is going to be the right one – we know this! Prove out your vision in private org-owned repos for free! Try out a plethora of development frameworks until you’ve found the perfect combination for your idea. Test your changes locally using our <a href="/wrangler-v2-beta/">Wrangler integration</a> so you can be confident in whatever you choose to put out into the world.</p>
    <div>
      <h3>Quick, easy and free integrations</h3>
      <a href="#quick-easy-and-free-integrations">
        
      </a>
    </div>
    <p><b>Workers:</b> Take your idea from static to dynamic with Pages’ native <a href="https://developers.cloudflare.com/pages/platform/functions/">integration with Cloudflare Workers</a>, our serverless functions offering. Drop your functions into your <b>functions</b> folder and deploy them alongside your static assets no extra configuration required! We announced <a href="/cloudflare-pages-goes-full-stack/">built-in support for Cloudflare Workers</a> back in November and have since announced framework integrations with Remix, Sveltekit and Qwik, and are working on fullstack support for Next.js within the year!</p><p><b>Cloudflare Access:</b> Working with more than just a team of developers? Send your staging changes to product managers and marketing teams with a unique preview URL for every deployment. And what’s more? You can enable protection for your preview links using our native integration with <a href="https://www.cloudflare.com/products/zero-trust/access/">Cloudflare Access</a> at no additional cost. With one click, send around your latest version without fear of getting into the wrong hands.</p><p><b>Custom domains:</b> With every Pages project, get a free <code>pages.dev</code> subdomain to deploy your project under. When you’re ready for the big day, with built in <a href="https://www.cloudflare.com/ssl-for-saas-providers/">SSL for SaaS</a>, bring that idea to life with a custom domain of its own!</p><p><b>Web Analytics:</b> When launch day comes around, check out just how well it’s going with our deep, privacy-first integration with <a href="https://www.cloudflare.com/web-analytics/">Cloudflare’s Web Analytics</a>. Track every single one of your sites’ progress and performance, including metrics about your traffic and core web vitals with just one click – completely on us!</p>
    <div>
      <h3>Wicked fast performance</h3>
      <a href="#wicked-fast-performance">
        
      </a>
    </div>
    <p>And the best part? Our generous free tier never means compromising <a href="/cloudflare-pages-is-lightning-fast/">site performance</a>. Bring your site closer to your users on your first deployment no matter where they are in the world. The Cloudflare network spans across 270+ cities around the globe and your site is distributed to each of them faster than you can say “it’s go time”. There’s also no need to choose regions for your deployments, we want you to have them all and get even more granular, so your big idea can truly go global.</p>
    <div>
      <h3>What else?</h3>
      <a href="#what-else">
        
      </a>
    </div>
    <p>Building on Pages is just the start of what your idea could grow to become. In the coming months you can expect deep integrations with our new Cloudflare storage offerings like <a href="https://www.cloudflare.com/developer-platform/products/r2/">R2</a>, our <a href="https://www.cloudflare.com/learning/cloud/what-is-object-storage/">object storage</a> service with zero <a href="https://www.cloudflare.com/learning/cloud/what-are-data-egress-fees/">egress fees</a> (open beta), and <a href="/introducing-d1/">D1</a> our first SQL database on the edge (private beta).</p><p>We’ve talked a lot about building your entire platform on Cloudflare. We’re reimagining this experience to be even easier and even more powerful.</p><p>Using just Cloudflare, you’ll be able to build big projects – like an entire store! You can use R2 to host the images, D1 to store product info, inventory data and user details, and Pages to seamlessly build and deploy. A frictionless dev experience for a full stack app that can live and work entirely from the edge. Best of all, don’t worry about getting hung up on cost, we’ll always have a generous free tier so you can get started right away.</p><p>At Cloudflare, we believe that every developer deserves to dream big. For the developers who love to build, who are curious, who explore, let us take you there – no surprises! Leave the security and scalability to us, so you can put your fingers to the keyboard and do what you do best!</p>
    <div>
      <h3>Give it a go</h3>
      <a href="#give-it-a-go">
        
      </a>
    </div>
    <p><a href="https://pages.cloudflare.com/">Learn more about Pages</a> and check out our <a href="https://developers.cloudflare.com/pages">developer documentation</a>. Be sure to join our active <a href="https://discord.com/invite/cloudflaredev">Cloudflare Developer Discord</a> and meet our community of developers building on our platform. You can chat directly with our product and engineering teams and get exclusive access to our offered betas!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">5gp6Zu82o0DkQgrDq0pKHK</guid>
            <dc:creator>Nevi Shah</dc:creator>
        </item>
        <item>
            <title><![CDATA[Announcing our Spring Developer Challenge]]></title>
            <link>https://blog.cloudflare.com/announcing-our-spring-developer-challenge/</link>
            <pubDate>Tue, 17 May 2022 12:58:47 GMT</pubDate>
            <description><![CDATA[ After many announcements from Platform Week, we’re thrilled to make one more: our Spring Developer Challenge! ]]></description>
            <content:encoded><![CDATA[ <p></p><p>After many announcements from Platform Week, we’re thrilled to make one more: our Spring Developer Challenge!</p><p>The theme for this challenge is building real-time, collaborative applications — one of the most exciting use-cases emerging in the Cloudflare ecosystem. This is an opportunity for developers to merge their ideas with our newly released features, earn recognition on our blog, and take home our best swag yet.</p><p>Here’s a list of our tools that will get you started:</p><ul><li><p><a href="https://developers.cloudflare.com/workers">Workers</a> can either be powerful middleware connecting your app to different APIs and an origin — or it can be the entire application itself. We recommend using <a href="https://github.com/lukeed/worktop">Worktop</a>, a popular framework for Workers, if you need TypeScript support, routing, and well-organized submodules. Worktop can also <a href="https://github.com/lukeed/worktop/issues/147">complement</a> your existing app even if it already uses a framework,  such as Svelte.</p></li><li><p><a href="https://developers.cloudflare.com/pages">Cloudflare Pages</a> makes it incredibly easy to deploy sites, which you can make into truly dynamic apps by putting a Worker in front <i>or</i> using the <a href="https://developers.cloudflare.com/pages">Pages Functions (beta)</a>.</p></li><li><p><a href="https://developers.cloudflare.com/workers/learning/using-durable-objects/">Durable Objects</a> are great for collaborative apps because you can use websockets while coordinating state at the edge, seen in this <a href="https://github.com/cloudflare/workers-chat-demo">chat demo</a>. To help scale any load, we also recommend <a href="https://github.com/cloudflare/dog">Durable Object Groups</a>.</p></li><li><p><a href="https://developers.cloudflare.com/workers/runtime-apis/kv/">Workers KV</a> provides a global key-value data store that securely stores and quickly serves data across Cloudflare’s network. <a href="/r2-open-beta/">R2</a> allows you to store enormous amounts of data without trapping you with costly egress services.</p></li></ul><p>Last year, our <a href="/tag/developer-spotlight/">Developer Spotlight series</a> highlighted how developers around the world built entire applications on Cloudflare. Our Discord server maintained that momentum with users demonstrating that <a href="https://discord.com/channels/595317990191398933/783765338692386886/964521676941455411">any type</a> of application can be built. Need a way to organize thousands of lines of JSON? <a href="https://jsonhero.io/">JSON Hero</a>, built with Remix and deployed with Workers, provides an incredibly readable UI for your JSON files. Trying to deploy a GraphQL server for your app that scales? <a href="https://github.com/launchport/helix-flare">helix-flare</a> deploys a GraphQL server easily through Workers and uses Durable Objects to coordinate data.</p><p>We hope developers continue to explore the boundaries of what they can build on Cloudflare as our platform evolves. During our Summer Developer Challenge in 2021, we received over 1,200 submissions that revealed you can build almost any app imaginable with Workers, Pages, and the rest of the developer ecosystem. We sent out hundreds of swag boxes to participants, to show our appreciation. The ensuing unboxing videos on <a href="https://twitter.com/KukicAdo/status/1435356070067662849?s=20&amp;t=cPyF9ekfCyrL06Ucn5WO2w">Twitter</a> and <a href="https://www.youtube.com/watch?v=TuYRhrnMUiQ">YouTube</a> thrilled our team.</p><p>This year’s Spring Developer Challenge is all about making real-time, collaborative apps such as chat rooms, games, web-based editing tools, or anything else in your imagination! Here are the rules:</p><p>This year’s Spring Developer Challenge is all about making real-time, collaborative apps such as chat rooms, games, web-based editing tools, or anything else in your imagination! Here are the rules:</p><ul><li><p>You must be at least 18 years old to participate</p></li><li><p>You can work in teams of up to 10 people per submission</p></li><li><p>The deadline to submit your repo is May 24 June 7</p></li></ul><p>Enter the challenge by going to <a href="https://challenge.developers.cloudflare.com"><b>this site</b></a>.</p><p>As you build your app, <a href="https://discord.gg/cloudflaredev">join our Discord</a> if you or your team need any help. We will be enthusiastically reviewing submissions, promoting them on Twitter, and sending out swag boxes.</p><p>If you’re new to Cloudflare or have an exciting idea as a developer, this is your opportunity to see how far our platform has evolved and get rewarded for it!</p> ]]></content:encoded>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Platform Week]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Durable Objects]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">1Zfd75h9CmfNmx9UBTLqM3</guid>
            <dc:creator>Albert Zhao</dc:creator>
        </item>
        <item>
            <title><![CDATA[Serving Cloudflare Pages sites to the IPFS network]]></title>
            <link>https://blog.cloudflare.com/cloudflare-pages-on-ipfs/</link>
            <pubDate>Mon, 16 May 2022 12:57:44 GMT</pubDate>
            <description><![CDATA[ Today, we're announcing we're bridging the two. We will make it possible for our customers to serve their sites on the IPFS network ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Four years ago, <a href="/distributed-web-gateway/">Cloudflare went Interplanetary</a> by offering a gateway to the IPFS network. This meant that if you hosted content on IPFS, we offered to make it available to every user of the Internet through HTTPS and with Cloudflare protection. IPFS allows you to choose a storage provider you are comfortable with, while providing a standard interface for Cloudflare to serve this data.</p><p>Since then, businesses have new tools to streamline web development. <a href="https://workers.dev">Cloudflare Workers</a>, <a href="https://pages.cloudflare.com">Pages</a>, and <a href="/introducing-r2-object-storage/">R2</a> are enabling developers to bring services online in a matter of minutes, with built-in scaling, security, and analytics.</p><p>Today, we're announcing we're bridging the two. We will make it possible for our customers to serve their sites on the IPFS network.</p><p>In this post, we'll learn how you will be able to build your website with Cloudflare Pages, and leverage the IPFS integration to make your content accessible and available across multiple providers.</p>
    <div>
      <h2>A primer on IPFS</h2>
      <a href="#a-primer-on-ipfs">
        
      </a>
    </div>
    <p>The InterPlanetary FileSystem (IPFS) is a peer-to-peer network for storing content on a distributed file system. It is composed of a set of computers called nodes that store and relay content using a common addressing system. In short, a set of participants agree to maintain a shared index of content the network can provide, and where to find it.</p><p>Let's take two random participants in the network: Alice, a cat person, and Bob, a dog person.</p><p>As a participant in the network, Alice keeps connections with a subset of participants, referred to as her peers. When Alice is making her content 🐱 available on IPFS, it means she announces to her peers she has content 🐱, and these peers stored in their routing table 🐱 is provided by Alice's node.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6BzC2QWnSrnF6lIqzMd0S8/df749e6c4e59acccb288c078235cab46/image7-8.png" />
            
            </figure><p>Each node has a routing table, and a datastore. The routing table retains a mapping of content to peers, and the datastore retains the content a given node stores. In the above case, only Alice has content, a 🐱.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1JKs4dKwaokLGutycmasSr/ee51d2f5c4b313c920f8f9ff22778b55/image1-41.png" />
            
            </figure><p>When Bob wants to retrieve 🐱, he tells his peers they want 🐱. These peers point him to Alice. Alice then provides 🐱 to Bob. Bob can verify 🐱 is the content they were looking for, because the content identifier he requested is derived from the 🐱 content itself, using a secure, cryptographic hash function.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6kYdWRivzzXiVbC9LsDWPf/9bdcb0ae5393a7c3551b7395ab5efd61/image3-28.png" />
            
            </figure><p>Even better, if Bob becomes a cat person, they can announce to their peers they are also providing a cat. Bob's love for cats could be genuine, or because they have interest in providing the content, such as a contract with Alice. IPFS provides a common ground to share content, without being opinionated on how this content has to be stored and its guarantees.</p>
    <div>
      <h2>How Pages websites are made available on IPFS</h2>
      <a href="#how-pages-websites-are-made-available-on-ipfs">
        
      </a>
    </div>
    <p>Content is made available as follows.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7y0PyVwr6OWWm4Ql8BDeZz/22401175f3cc9f85635a155ad1b4c78c/image2-37.png" />
            
            </figure><p>The components are:</p><ul><li><p>Pages storage: Storage solution for Cloudflare Pages.</p></li><li><p>IPFS Index Proxy: Service maintaining a map between IPFS CID and location of the data. This is operated on Cloudflare Workers and using Workers KV to store the mapping.</p></li><li><p>IPFS node: Cloudflare-hosted IPFS node serving Pages content. It has an in-house datastore module, able to communicate with the IPFS Index Proxy.</p></li><li><p>IPFS network: The rest of the IPFS network.</p></li></ul><p>When you opt in serving your Cloudflare Page on IPFS, a call is made to the IPFS index proxy. This call first fetches your Pages content, transforms it into a CID, then both populates IndexDB to associate the CID with the content and reaches out to Cloudflare IPFS node to tell them they are able to provide the CID.</p><p>For example, imagine your website structure is as follows:</p><ul><li><p>/</p><ul><li><p>index.html</p></li><li><p>static/</p><ul><li><p>cats.txt</p></li><li><p>beautiful_cats.txt</p></li></ul></li></ul></li></ul><p>To provide this website on IPFS, Cloudflare has to compute a CID for /. CIDs are built recursively. To compute the CID for a given folder /, one needs to have the CID of <code>index.html</code> and <code>static</code>/. <code>index.html</code> CID is derived from its binary representation, and static/ from cats.txt and beautiful_cats.txt.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7stjDpQPAhOugUqBcwRV8H/1bf1f181f61793fa0d6c1d1c2a860d77/image6-13.png" />
            
            </figure><p>This works similarly to a Merkle tree, except nodes can reference each other as long as they still form a Directed Acyclic Graph (DAG). This structure is therefore referred to as a <a href="https://docs.ipfs.io/concepts/merkle-dag/">MerkleDAG</a>.</p><p>In our example, it's not unlikely for <code>cats.txt</code> and <code>beautiful_cats.txt</code> to have data in common. In this case, Cloudflare can be smart in the way it builds the MerkleDAG.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6mqAMRlu7HoiESker0Tiur/49e0b2c53cdf847dc283391335408f15/image4-22.png" />
            
            </figure><p>This reduces the storage and bandwidth requirement when accessing the website on IPFS.</p><p><i>If you want to learn more about how you can model a file system on IPFS, you can check the</i> <a href="https://github.com/ipfs/specs/blob/master/UNIXFS.md"><i>UnixFS</i></a> <i>specification.</i></p><p>Cloudflare stores every CID and the content it references in IndexDB. This allows Cloudflare IPFS nodes to serve Cloudflare Pages assets when requested.</p>
    <div>
      <h2>Let's put this together</h2>
      <a href="#lets-put-this-together">
        
      </a>
    </div>
    <p>Let’s take an example: pages-on-ipfs.com is hosted on IPFS. It is built using Hugo, a static site generator, and Cloudflare Pages with the <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-hugo-site/">public documentation template</a>. Its source is available on <a href="https://github.com/thibmeu/pages-on-ipfs">GitHub</a>. If you have an IPFS compatible client, you can access it at ipns://pages-on-ipfs.com as well.</p><p>1. Read Cloudflare Pages deployment documentation</p><p>For the purpose of this blog, I want to create a simple Cloudflare page website. I have experience with Hugo, so I choose it as my framework for the project.</p><p>I type "<a href="https://lmddgtfy.net/?q=cloudflare%20pages">cloudflare pages</a>" in the search bar of my web browser, then head to the Read the docs &gt; Framework Guide &gt; <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-hugo-site/">Deploy a Hugo site</a>.</p><p>2. Create a site</p><p>This is where I use Hugo, and your configuration might differ. In short, I use hugo new site pages-on-ipfs, create an index and two static resources, et voilà. The result is available on the source <a href="https://github.com/thibmeu/pages-on-ipfs">GitHub</a> for this project.</p><p>3. Deploy using Cloudflare Pages</p><p>On the Cloudflare Dashboard, I go to Account Home &gt; Pages &gt; Create a project. I select the GitHub repository I created, and configure the build tool to build Hugo website. Basically, I follow what's written on <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-hugo-site/#deploying-with-cloudflare-pages">Cloudflare Pages documentation</a>.</p><p>Upon clicking Save and Deploy, my website is deployed on pages-on-ipfs.pages.dev. I also configure it to be available at pages-on-ipfs.com</p><p>4. Serve my content on IPFS</p><p>First, I opt in my zone on Cloudflare Pages integration with IPFS. This feature is not available yet for everyone to try out.</p><p>This allows Cloudflare to index the content of my website. Once indexed, I get the CID for my deployment baf...1. I can check that my content is available at this hash on IPFS using an IPFS gateway <a href="https://bafybeig7hluox6xefqdgmwcntvsguxcziw2oeogg2fbvygex2aj6qcfo64.ipfs.cf-ipfs.com">https://baf...1.ipfs.cf-ipfs.com/</a>.</p><p>5. Make my IPFS website available at pages-on-ipfs.com</p><p>Having one domain name to access both Cloudflare Pages and IPFS version, depending on if the client supports IPFS or not is ideal. Fortunately, the IPFS ecosystem supports such a feature via DNSLink. DNSLink is a way to specify the IPFS content a domain is associated with.</p><p>For pages-on-ipfs.com, I create a TXT record on _dnslink.pages-on-ipfs.com with value dnslink=/ipfs/baf...1. Et voilà. I can now access ipns://pages-on-ipfs.com via an IPFS client.</p><p>6. (Optional) Replicate my website</p><p>The content of my website can now easily be replicated and <a href="https://docs.ipfs.io/how-to/pin-files/">pinned</a> by other IPFS nodes. This can either be done at home via an IPFS client or using a pinning service such as <a href="https://www.pinata.cloud/">Pinata</a>.</p>
    <div>
      <h2>What's next</h2>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>We'll make this service available later this year as it is being refined. We are committed to make information move freely and help build a better Internet. Cloudflare Pages work of solving developer problems continues, as developers are now able to make their site accessible to more users.</p><p>Over the years, IPFS has been used by more and more people. While Cloudflare started by making IPFS content available to web users through an HTTP interface, we now think it's time to give back. Allowing Cloudflare assets to be served over a public distributed network extends developers and users capability on an open web.</p>
    <div>
      <h2>Common questions</h2>
      <a href="#common-questions">
        
      </a>
    </div>
    <ul><li><p>I am already hosting my website on IPFS. Can I pin it using Cloudflare?</p><ul><li><p>No. This project aims at serving <a href="https://www.cloudflare.com/developer-platform/solutions/hosting/">Cloudflare hosted content</a> via IPFS. We are still investigating how to best replicate and re-provide already-existing IPFS content via Cloudflare infrastructure.</p></li></ul></li><li><p>Does this make IPFS more centralized?</p><ul><li><p>No. Cloudflare does not have the authority to decide who can be a node operator nor what content they provide.</p></li></ul></li><li><p>Are there guarantees the content is going to be available?</p><ul><li><p>Yes. As long as you choose Cloudflare to host your website on IPFS, it will be available on IPFS. Should you move to another provider, it would be your responsibility to make sure the content remains available. IPFS allows for this transition to be smooth using a pinning service.</p></li></ul></li><li><p>Is IPFS private?</p><ul><li><p>It depends. Generally, no. IPFS is a p2p protocol. The nodes you peer with and request content from would know the content you are looking for. The privacy depends on the trust you have in your peers to not snoop on the data you request.</p></li></ul></li><li><p>Can users verify the integrity of my website?</p><ul><li><p>Yes. They need to access your website through an IPFS compatible client. Ideally, IPFS content integrity is turned into a web standard, similar to <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity">subresource integrity</a>.</p></li></ul></li></ul><p></p> ]]></content:encoded>
            <category><![CDATA[Platform Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <category><![CDATA[Distributed Web]]></category>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[IPFS]]></category>
            <guid isPermaLink="false">yHRMEOkly3EmYimxcRF3u</guid>
            <dc:creator>Thibault Meunier</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Pages Plugins]]></title>
            <link>https://blog.cloudflare.com/cloudflare-pages-plugins/</link>
            <pubDate>Thu, 12 May 2022 12:58:49 GMT</pubDate>
            <description><![CDATA[ Introducing Pages Plugins – reusable and customizable chunks of runtime code that can be incorporated anywhere within your Pages application. Try one of the officially supported Plugins from our partners or build your own ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Last November, we <a href="/cloudflare-pages-goes-full-stack/">announced</a> that Pages is now a full-stack development platform with our open beta integration with Cloudflare Workers. Using file-based routing, you can drop your Pages Functions into a <code>/functions</code> folder and deploy them alongside your static assets to add dynamic functionality to your site. However, throughout this beta period, we observed the types of projects users have been building, noticed some common patterns, and identified ways to make these users more efficient.</p><p>There are certain functionalities that are shared between projects; for example, validating authorization headers, creating an API server, reporting errors, and integrating with third-party vendors to track aspects like performance. The frequent need for these patterns across projects made us wonder, “What if we could provide the ready-made code for developers to add to their existing project?”</p><p>Introducing Pages Plugins!</p>
    <div>
      <h2>What’s a Pages Plugin?</h2>
      <a href="#whats-a-pages-plugin">
        
      </a>
    </div>
    <p>With <a href="/cloudflare-pages-goes-full-stack/">Pages Functions</a>, we introduced file-based routing, so users could avoid writing their own routing logic, significantly reducing the amount of boilerplate code a typical application requires. Pages Plugins aims to offer a similar experience!</p><p>A Pages Plugin is a reusable – and customizable – chunk of runtime code that can be incorporated anywhere within your Pages application. A Plugin is effectively a composable Pages Function, granting Plugins the full power of Functions (and therefore, Workers), including the ability to set up middleware, parameterized routes, and static assets.</p>
    <div>
      <h2>How does it work?</h2>
      <a href="#how-does-it-work">
        
      </a>
    </div>
    <p>Today, Pages Plugins is launching with a couple of ready-made solutions for Sentry, Honeycomb, and Stytch (more below), but it’s important to note that developers anywhere can create and share their Pages Plugins, too! You just need to install a Plugin, mount it to a route within the <code>/functions</code> directory, and configure the Plugin according to its needs.</p><p>Let’s take a look at a Plugins example for a hypothetical ACME logger:</p><p>Assume you find an <code>@acme/pages-plugin-logger</code> package on npm and want to use it within your application – you’d install, import, and invoke it as you would any other npm module. After passing through the required (hypothetical) configuration and mounting it as the top-level middleware’s <code>onRequest</code> export, the ACME logger will be reporting on all incoming requests:</p>
            <pre><code>// file: /functions/_middleware.ts

import MyLogger from "@acme/pages-plugin-logger";

// Setup logging for all URL routes &amp; methods
export const onRequest = MyLogger({
 endpoint: "https://logs.acme.com/new",
 secret: "password",
});</code></pre>
            <p>You can help grow the Plugins ecosystem by building and sharing your Plugins on <a href="https://www.npmjs.com/">npm</a> and our <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/community-plugins/">developer documentation</a>, and you can immediately get started by using one of Cloudflare’s official launch partner Plugins today.</p>
    <div>
      <h2>Introducing our Plugins launch partners</h2>
      <a href="#introducing-our-plugins-launch-partners">
        
      </a>
    </div>
    <p>With Pages, we’re always working to see how we can best cater to user workflows by integrating directly with users’ preferred tools. We see Plugins as an excellent opportunity to collaborate with popular third-party observability, monitoring, and authentication providers to provide their own Pages Plugins.</p><p>Today, we’re proud to launch our Pages Plugins with <a href="https://sentry.io/welcome/">Sentry</a>, <a href="https://www.honeycomb.io/">Honeycomb</a> and <a href="https://stytch.com/">Stytch</a> as official partners!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6UfnNcBSdnluYtxgQIpI4v/943ae59eecd5d8705836d705ae2ceaf7/image2-22.png" />
            
            </figure>
    <div>
      <h3><a href="https://sentry.io/welcome/">Sentry</a></h3>
      <a href="#">
        
      </a>
    </div>
    <p><a href="http://sentry.io/">Sentry</a> provides developer-first application monitoring for real-time insights into your production deployments. With Sentry you can see the errors, crashes, or latencies experienced while using your app and get the deep context needed to solve issues quickly, like the line of code where the error occurred, the developer or commit that introduced the error, or the API call or database query causing the slowdown. The <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/sentry">Sentry Plugin</a> automatically captures any exceptions in your Pages Functions and sends them to Sentry where you can aggregate, analyze, and triage any issues your application encounters.</p>
            <pre><code>// ./functions/_middleware.ts

import sentryPlugin from "@cloudflare/pages-plugin-sentry";

export const onRequest = sentryPlugin({
 dsn: "YOUR_SENTRY_DSN",
});</code></pre>
            
    <div>
      <h3><a href="https://www.honeycomb.io/">Honeycomb</a></h3>
      <a href="#">
        
      </a>
    </div>
    <p>Similarly, Honeycomb is also an observability and monitoring platform meant to visualize, analyze and improve application quality and performance to help you find patterns and outliers in your application data. The <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/honeycomb">Honeycomb Plugin</a> creates traces for every request that your Pages application receives and automatically sends that information to Honeycomb for analysis.</p>
            <pre><code>// ./functions/_middleware.ts

import honeycombPlugin from "@cloudflare/pages-plugin-honeycomb";

export const onRequest = honeycombPlugin({
 apiKey: "YOUR_HONEYCOMB_API_KEY",
 dataset: "YOUR_HONEYCOMB_DATASET_NAME",
});</code></pre>
            
    <div>
      <h3><a href="https://stytch.com/?utm_source=cloudflare&amp;utm_medium=blog">Stytch</a></h3>
      <a href="#">
        
      </a>
    </div>
    <p>Observability is just one use case of how Pages Plugins can help you build a more powerful app. Stytch is an API-first platform that improves security and promotes a better user experience with <a href="https://www.cloudflare.com/learning/security/threats/what-is-passwordless-authentication/">passwordless authentication</a>. Our <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/stytch">Stytch Plugin</a> transparently validates user sessions, allowing you to easily protect parts of your application behind a Stytch login.</p>
            <pre><code>// ./functions/_middleware.ts

import stytchPlugin from "@cloudflare/pages-plugin-stytch";
import { envs } from "@cloudflare/pages-plugin-stytch/api";

export const onRequest = stytchPlugin({
  project_id: "YOUR_STYTCH_PROJECT_ID",
  secret: "YOUR_STYTCH_PROJECT_SECRET",
  env: envs.live
});</code></pre>
            
    <div>
      <h2>More Plugins, more fun!</h2>
      <a href="#more-plugins-more-fun">
        
      </a>
    </div>
    <p>As a developer platform, it’s crucial to build relationships with the creators of the tooling and frameworks you use alongside Pages, and we look forward to growing our partnership ecosystem even more in the future. However, beyond partnerships, we realize there are some more extremely useful Plugins that we built out to get you started!</p><ul><li><p><b>Google Chat:</b> creates a Google Chat bot which can respond to messages. It also includes an API for interacting with Google Chat (for example, for creating messages) without the need for user input. This API is useful for situations such as alerts.</p></li><li><p><b>Cloudflare Access:</b> a middleware to validate Cloudflare Access JWT assertions. It also includes an API to lookup additional information about a given user's JSON Web Token.</p></li><li><p><b>Static forms</b>: intercepts static HTML form submissions and can perform actions such as storing the data in KV.</p></li><li><p><b>GraphQL</b>: creates a GraphQL API for a given schema. It also ships with <a href="https://github.com/graphql/graphql-playground">the GraphQL Playground</a> to simplify development and help you test out your API.</p></li></ul><p>Over the next couple of months we will be working to build out some of the most requested Plugins relevant to your projects. For now, you can find all officially supported Plugins in our <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/">developer documentation</a>.</p>
    <div>
      <h2>No time to wait? Author your own!</h2>
      <a href="#no-time-to-wait-author-your-own">
        
      </a>
    </div>
    <p>But don’t let us be your bottleneck! The beauty of Plugins is how easy they are to create and distribute. In fact, we <b>encourage</b> you to try out our <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/">documentation</a> in order to create and share your own Plugin because chances are if you’re building a Plugin for your own project, there is someone else who would benefit greatly from it too!</p><p>We're excited to see Plugins from the community solving their own common use-cases or as integrations with their favorite platforms. Once you’ve built a Plugin, you can surface your work if you choose by creating a PR against <a href="https://developers.cloudflare.com/pages/platform/functions/plugins/community-plugins/">the Community Plugins page in our documentation</a>. This way any Pages user can read about your Plugin and mount it in their own Pages application.</p>
    <div>
      <h2>What’s next for Pages Functions</h2>
      <a href="#whats-next-for-pages-functions">
        
      </a>
    </div>
    <p>As you try out Plugins and take advantage of our Functions offering, it’s important to note there are some truly exciting updates coming soon. As we march toward the Functions general availability launch, we will provide proper analytics and logging, so you can have better insight into your site’s performance and visibility into issues for debugging. Additionally, with R2 now in open beta, and <a href="https://www.cloudflare.com/developer-platform/products/d1/">D1</a> in the works, we’re excited to provide support for R2 and D1 bindings for your full stack Pages projects soon!</p><p>Of course, because Functions is still in open beta, we currently offer 100k requests per day for free, however, as we aim for general availability of Functions, you can expect a billing model similar to the <a href="https://developers.cloudflare.com/workers/platform/pricing/">Workers Paid</a> billing model.</p>
    <div>
      <h2>Share what you build</h2>
      <a href="#share-what-you-build">
        
      </a>
    </div>
    <p>While you begin building out your Plugin, be sure to reach out to us in our <a href="https://discord.gg/cloudflaredev">Cloudflare Developers Discord server</a> in the #pages-plugins channel. We’d love to see what you’re building and help you along the way!</p> ]]></content:encoded>
            <category><![CDATA[Platform Week]]></category>
            <category><![CDATA[Cloudflare Pages]]></category>
            <guid isPermaLink="false">sT8hM1mh7oDHKdzzd3F4R</guid>
            <dc:creator>Nevi Shah</dc:creator>
            <dc:creator>Greg Brimble</dc:creator>
        </item>
    </channel>
</rss>