
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Wed, 15 Apr 2026 19:33:19 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[Build global MySQL apps using Cloudflare Workers and Hyperdrive]]></title>
            <link>https://blog.cloudflare.com/building-global-mysql-apps-with-cloudflare-workers-and-hyperdrive/</link>
            <pubDate>Tue, 08 Apr 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ MySQL comes to Cloudflare Workers and Hyperdrive: MySQL drivers and ORMs are now compatible with Workers runtime, and Hyperdrive allow you to connect to your regional database from Workers. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we’re announcing support for MySQL in Cloudflare Workers and Hyperdrive. You can now build applications on Workers that connect to your MySQL databases directly, no matter where they’re hosted, with native MySQL drivers, and with optimal performance. </p><p>Connecting to MySQL databases from Workers has been an area we’ve been focusing on <a href="https://blog.cloudflare.com/relational-database-connectors/"><u>for quite some time</u></a>. We want you to build your apps on Workers with your existing data, even if that data exists in a SQL database in us-east-1. But connecting to traditional SQL databases from Workers has been challenging: it requires making stateful connections to regional databases with drivers that haven’t been designed for <a href="https://blog.cloudflare.com/workerd-open-source-workers-runtime/"><u>the Workers runtime</u></a>. </p><p>After multiple attempts at solving this problem for Postgres, <a href="https://developers.cloudflare.com/hyperdrive/"><u>Hyperdrive</u></a> emerged as our solution that provides the best of both worlds: it supports existing database drivers and libraries while also providing best-in-class performance. And it’s such a critical part of connecting to databases from Workers that we’re making it free (check out the <a href="https://blog.cloudflare.com/how-hyperdrive-speeds-up-database-access"><u>Hyperdrive free tier announcement</u></a>).</p><p>With <a href="http://blog.cloudflare.com/full-stack-development-on-cloudflare-workers"><u>new Node.js compatibility improvements</u></a> and <a href="http://developers.cloudflare.com/changelog/2025-04-08-hyperdrive-mysql-support/"><u>Hyperdrive support for the MySQL wire protocol</u></a>, we’re happy to say MySQL support for Cloudflare Workers has been achieved. If you want to jump into the code and have a MySQL database on hand, this “Deploy to Cloudflare” button will get you setup with a deployed project and will create a repository so you can dig into the code. </p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/mysql-hyperdrive-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p><p>Read on to learn more about how we got MySQL to work on Workers, and why Hyperdrive is critical to making connectivity to MySQL databases fast.</p>
    <div>
      <h2>Getting MySQL to work on Workers</h2>
      <a href="#getting-mysql-to-work-on-workers">
        
      </a>
    </div>
    <p>Until recently, connecting to MySQL databases from Workers was not straightforward. While it’s been possible to make TCP connections from Workers <a href="https://blog.cloudflare.com/workers-tcp-socket-api-connect-databases/"><u>for some time</u></a>, MySQL drivers had many dependencies on Node.js that weren’t available on the Workers runtime, and that prevented their use.</p><p>This led to workarounds being developed. PlanetScale provided a <a href="https://planetscale.com/blog/introducing-the-planetscale-serverless-driver-for-javascript"><u>serverless driver for JavaScript</u></a>, which communicates with PlanetScale servers using HTTP instead of TCP to relay database messages. In a separate effort, a <a href="https://github.com/nora-soderlund/cloudflare-mysql"><u>fork</u></a> of the <a href="https://www.npmjs.com/package/mysql"><u>mysql</u></a> package was created to polyfill the missing Node.js dependencies and modify the <code>mysql</code> package to work on Workers. </p><p>These solutions weren’t perfect. They required using new libraries that either did not provide the level of support expected for production applications, or provided solutions that were limited to certain MySQL hosting providers. They also did not integrate with existing codebases and tooling that depended on the popular MySQL drivers (<a href="https://www.npmjs.com/package/mysql"><u>mysql</u></a> and <a href="https://www.npmjs.com/package/mysql2"><u>mysql2</u></a>). In our effort to enable all JavaScript developers to build on Workers, we knew that we had to support these drivers.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3s4E4XVAbvqRwyk6aITm1h/cb9700eae49d2593c9a834fc7a09018e/1.png" />
          </figure><p><sup><i>Package downloads from </i></sup><a href="https://www.npmjs.com/"><sup><i><u>npm</u></i></sup></a><sup><i> for </i></sup><a href="https://www.npmjs.com/package/mysql"><sup><i><u>mysql</u></i></sup></a><sup><i> and </i></sup><a href="https://www.npmjs.com/package/mysql2"><sup><i><u>mysql2</u></i></sup></a></p><p>Improving our Node.js compatibility story was critical to get these MySQL drivers working on our platform. We first identified <a href="https://nodejs.org/api/net.html"><u>net</u></a> and <a href="https://nodejs.org/api/stream.html"><u>stream</u></a> as APIs that were needed by both drivers. This, complemented by Workers’ <a href="https://blog.cloudflare.com/more-npm-packages-on-cloudflare-workers-combining-polyfills-and-native-code/"><u>nodejs_compat</u></a> to resolve unused Node.js dependencies with <a href="https://github.com/unjs/unenv"><code><u>unenv</u></code></a>, enabled the <a href="https://www.npmjs.com/package/mysql"><u>mysql</u></a> package to work on Workers:</p>
            <pre><code>import { createConnection } from 'mysql';

export default {
 async fetch(request, env, ctx): Promise&lt;Response&gt; {
    const result = await new Promise&lt;any&gt;((resolve) =&gt; {

       const connection = createConnection({
         host: env.DB_HOST,
         user: env.DB_USER,
         password: env.DB_PASSWORD,
         database: env.DB_NAME,
	  port: env.DB_PORT
       });

       connection.connect((error: { message : string }) =&gt; {
          if(error) {
            throw new Error(error.message);
          }
          
          connection.query("SHOW tables;", [], (error, rows, fields) =&gt; {
          connection.end();
         
          resolve({ fields, rows });
        });
       });

      });

     return new Response(JSON.stringify(result), {
       headers: {
         'Content-Type': 'application/json',
       },
     });
 },
} satisfies ExportedHandler&lt;Env&gt;;</code></pre>
            <p>Further work was required to get <a href="https://www.npmjs.com/package/mysql2"><u>mysql2</u></a> working: dependencies on Node.js <a href="https://nodejs.org/api/timers.html"><u>timers</u></a> and the JavaScript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><u>eval</u></a> API remained. While we were able to land support for <a href="https://github.com/cloudflare/workerd/pull/3346"><u>timers</u></a> in the Workers runtime, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><u>eval</u></a> was not an API that we could securely enable in the Workers runtime at this time. </p><p><a href="https://www.npmjs.com/package/mysql2"><u>mysql2</u></a> uses <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><u>eval</u></a> to optimize the parsing of MySQL results containing large rows with more than 100 columns (see <a href="https://github.com/sidorares/node-mysql2/issues/2055#issuecomment-1614222188"><u>benchmarks</u></a>). This blocked the driver from working on Workers, since the Workers runtime does not support this module. Luckily, <a href="https://github.com/sidorares/node-mysql2/pull/2289"><u>prior effort existed</u></a> to get mysql2 working on Workers using static parsers for handling text and binary MySQL data types without using <code>eval()</code>, which provides similar performance for a majority of scenarios. </p><p>In <a href="https://www.npmjs.com/package/mysql2"><u>mysql2</u></a> version <code>3.13.0</code>, a new option to disable the use of <code>eval()</code> was released to make it possible to use the driver in Cloudflare Workers:</p>
            <pre><code>import { createConnection  } from 'mysql2/promise';

export default {
 async fetch(request, env, ctx): Promise&lt;Response&gt; {
    const connection = await createConnection({
     host: env.DB_HOST,
     user: env.DB_USER,
     password: env.DB_PASSWORD,
     database: env.DB_NAME,
     port: env.DB_PORT

     // The following line is needed for mysql2 to work on Workers (as explained above)
     // mysql2 uses eval() to optimize result parsing for rows with &gt; 100 columns
     // eval() is not available in Workers due to runtime limitations
     // Configure mysql2 to use static parsing with disableEval
     disableEval: true
   });

   const [results, fields] = await connection.query(
     'SHOW tables;'
   );

   return new Response(JSON.stringify({ results, fields }), {
     headers: {
       'Content-Type': 'application/json',
       'Access-Control-Allow-Origin': '*',
     },
   });
 },
} satisfies ExportedHandler&lt;Env&gt;;</code></pre>
            <p>So, with these efforts, it is now possible to connect to MySQL from Workers. But, getting the MySQL drivers working on Workers was only half of the battle. To make MySQL on Workers performant for production uses, we needed to make it possible to connect to MySQL databases with Hyperdrive.</p>
    <div>
      <h2>Supporting MySQL in Hyperdrive</h2>
      <a href="#supporting-mysql-in-hyperdrive">
        
      </a>
    </div>
    <p>If you’re a MySQL developer, <a href="https://developers.cloudflare.com/hyperdrive/"><u>Hyperdrive</u></a> may be new to you. Hyperdrive solves a core problem: connecting from Workers to regional SQL databases is slow. Database drivers <a href="https://blog.cloudflare.com/how-hyperdrive-speeds-up-database-access/"><u>require many roundtrips</u></a> to establish a connection to a database. Without the ability to reuse these connections between Worker invocations, a lot of unnecessary latency is added to your application. </p><p>Hyperdrive solves this problem by pooling connections to your database globally and eliminating unnecessary roundtrips for connection setup. As a plus, Hyperdrive also provides integrated caching to offload popular queries from your database. We wrote an entire deep dive on how Hyperdrive does this, which you should definitely <a href="https://blog.cloudflare.com/how-hyperdrive-speeds-up-database-access/"><u>check out</u></a>. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/R8bfw57o8KEXHD7EstTmP/eee5182beb931373c25a1c42f5dd0ce3/2.png" />
          </figure><p>Getting Hyperdrive to support MySQL was critical for us to be able to say “Connect from Workers to MySQL databases”. That’s easier said than done. To support a new database type, Hyperdrive needs to be able to parse the wire protocol of the database in question, in this case, the <a href="https://dev.mysql.com/doc/dev/mysql-server/8.4.3/PAGE_PROTOCOL.html"><u>MySQL protocol</u></a>. Once this is accomplished, Hyperdrive can extract queries from protocol messages, cache results across Cloudflare locations, relay messages to a datacenter close to your database, and pool connections reliably close to your origin database. </p><p>Adapting Hyperdrive to parse a new language, MySQL protocol, is a challenge in its own right. But it also presented some notable differences with Postgres. While the intricacies are beyond the scope of this post, the differences in MySQL’s <a href="https://dev.mysql.com/doc/refman/8.4/en/authentication-plugins.html"><u>authentication plugins</u></a> across providers and how MySQL’s connection handshake uses <a href="https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html"><u>capability flags</u></a> required some adaptation of Hyperdrive. In the end, we leveraged the experience we gained in building Hyperdrive for Postgres to iterate on our support for MySQL. And we’re happy to announce MySQL support is available for Hyperdrive, with all of the <a href="https://developers.cloudflare.com/changelog/2025-03-04-hyperdrive-pooling-near-database-and-ip-range-egress/"><u>performance</u></a> <a href="https://developers.cloudflare.com/changelog/2024-12-11-hyperdrive-caching-at-edge/"><u>improvements</u></a> we’ve made to Hyperdrive available from the get-go!</p><p>Now, you can create new Hyperdrive configurations for MySQL databases hosted anywhere (we’ve tested MySQL and MariaDB databases from AWS (including AWS Aurora), GCP, Azure, PlanetScale, and self-hosted databases). You can create Hyperdrive configurations for your MySQL databases from the dashboard or the <a href="https://developers.cloudflare.com/workers/wrangler/"><u>Wrangler CLI</u></a>:</p>
            <pre><code>wrangler hyperdrive create mysql-hyperdrive 
--connection-string="mysql://user:password@db-host.example.com:3306/defaultdb"</code></pre>
            <p>In your Wrangler configuration file, you’ll need to set your Hyperdrive binding to the ID of the newly created Hyperdrive configuration as well as set Node.js compatibility flags:</p>
            <pre><code>{
 "$schema": "node_modules/wrangler/config-schema.json",
 "name": "workers-mysql-template",
 "main": "src/index.ts",
 "compatibility_date": "2025-03-10",
 "observability": {
   "enabled": true
 },
 "compatibility_flags": [
   "nodejs_compat"
 ],
 "hyperdrive": [
   {
     "binding": "HYPERDRIVE",
     "id": "&lt;HYPERDRIVE_CONFIG_ID&gt;"
   }
 ]
}</code></pre>
            <p>From your Cloudflare Worker, the Hyperdrive binding provides you with custom connection credentials that connect to your Hyperdrive configuration. From there onward, all of your queries and database messages will be routed to your origin database by Hyperdrive, leveraging Cloudflare’s network to speed up routing.</p>
            <pre><code>import { createConnection  } from 'mysql2/promise';

export interface Env {
 HYPERDRIVE: Hyperdrive;
}

export default {
 async fetch(request, env, ctx): Promise&lt;Response&gt; {
  
   // Hyperdrive provides new connection credentials to use with your existing drivers
   const connection = await createConnection({
     host: env.HYPERDRIVE.host,
     user: env.HYPERDRIVE.user,
     password: env.HYPERDRIVE.password,
     database: env.HYPERDRIVE.database,
     port: env.HYPERDRIVE.port,

     // Configure mysql2 to use static parsing (as explained above in Part 1)
     disableEval: true 
   });

   const [results, fields] = await connection.query(
     'SHOW tables;'
   );

   return new Response(JSON.stringify({ results, fields }), {
     headers: {
       'Content-Type': 'application/json',
       'Access-Control-Allow-Origin': '*',
     },
   });
 },
} satisfies ExportedHandler&lt;Env&gt;;</code></pre>
            <p>As you can see from this code snippet, you only need to swap the credentials in your JavaScript code for those provided by Hyperdrive to migrate your existing code to Workers. No need to change the ORMs or drivers you’re using! </p>
    <div>
      <h2>Get started building with MySQL and Hyperdrive</h2>
      <a href="#get-started-building-with-mysql-and-hyperdrive">
        
      </a>
    </div>
    <p>MySQL support for Workers and Hyperdrive has been long overdue and we’re excited to see what you build. We published a template for you to get started building your MySQL applications on Workers with Hyperdrive:</p><a href="https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/mysql-hyperdrive-template"><img src="https://deploy.workers.cloudflare.com/button" /></a>
<p></p><p>As for what’s next, we’re going to continue iterating on our support for MySQL during the beta to support more of the MySQL protocol and MySQL-compatible databases. We’re also going to continue to expand the feature set of Hyperdrive to make it more flexible for your full-stack workloads and more performant for building full-stack global apps on Workers.</p><p>Finally, whether you’re using MySQL, PostgreSQL, or any of the other compatible databases, we think you should be using Hyperdrive to get the best performance. And because we want to enable you to build on Workers regardless of your preferred database, <a href="https://blog.cloudflare.com/how-hyperdrive-speeds-up-database-access"><u>we’re making Hyperdrive available to the Workers free plan. </u></a></p><p>We want to hear your feedback on MySQL, Hyperdrive, and building global applications with Workers. Join the #hyperdrive channel in our <a href="http://discord.cloudflare.com/"><u>Developer Discord</u></a> to ask questions, share what you’re building, and talk to our Product &amp; Engineering teams directly.</p><p>Thank you to <a href="https://github.com/wellwelwel"><u>Weslley Araújo</u></a>, <a href="https://github.com/sidorares"><u>Andrey Sidorov</u></a>, <a href="https://github.com/shiyuhang0"><u>Shi Yuhang</u></a>, <a href="https://github.com/Mini256"><u>Zhiyuan Liang</u></a>, <a href="https://github.com/nora-soderlund"><u>Nora Söderlund</u></a> and other open-source contributors who helped push this initiative forward.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[MySQL]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Hyperdrive]]></category>
            <guid isPermaLink="false">77Nbj8Tnnrr6vMWsDSFekZ</guid>
            <dc:creator>Thomas Gauvin</dc:creator>
            <dc:creator>Andrew Repp</dc:creator>
            <dc:creator>Kirk Nickish</dc:creator>
            <dc:creator>Yagiz Nizipli</dc:creator>
        </item>
    </channel>
</rss>