DEV Community

Cover image for Astro on Cloudflare Workers
Pascal Schilp
Pascal Schilp

Posted on • Edited on

Astro on Cloudflare Workers

Update: Astro now ships an official cloudflare integration, you should probably use that instead :)

It's Platform Week at Cloudflare, and with that, comes all sort of goodies, like for example the release of Wrangler 2.0 beta. Interested in what's new? Sunil Pai wrote all about it here: 10 things I love about Wrangler v2.0.

Additionally, Astro recently released experimental support for server-side rendering Astro apps.

What better time to combine the two, jump on the hype train, and create a Cloudflare Worker adapter for Astro. While looking into creating this adapter, I admittedly got slightly sidetracked; If I can create an Astro adapter for Cloudflare Workers, an environment that's so similar to Service workers, why can't I run Astro in a Service Worker? 🤯. As it turns out, you totally can, and I wrote all about my crazy experimentation in that area here, are you excited for a streaming future via SWSR (Service Worker Side Rendering)? I know I am!

Getting started

First up, scaffold your Astro project with:

npm init astro@latest
Enter fullscreen mode Exit fullscreen mode

Next up, we have to install some dependencies; the new wrangler CLI, and the Cloudflare Worker adapter:

npm install -D wrangler
npm install -S astro-service-worker
Enter fullscreen mode Exit fullscreen mode

Once installed, add the adapter to your astro.config.mjs:

import { defineConfig } from 'astro/config';
import worker, { cloudflare } from 'astro-service-worker/adapter';

export default defineConfig({
  adapter: worker(cloudflare)
});
Enter fullscreen mode Exit fullscreen mode

You can now build your project by running:

npm run build
Enter fullscreen mode Exit fullscreen mode

If you don't yet have a wrangler.toml file, the adapter will create one for you during the build. If you do have one, make sure you point the main and bucket properties to the correct locations, here's an example of what they should look like:

name = "my-project"
main = "dist/worker/index.js" # The adapter will output the worker in this location
compatibility_date = "2022-05-10"

[site]
bucket = './dist' # The adapter will output static assets in this folder
Enter fullscreen mode Exit fullscreen mode

Once you're all setup, you can now deploy your app:

wrangler publish
Enter fullscreen mode Exit fullscreen mode

Workers Everywhere

You may have noticed that the instead of shipping a cloudflare() adapter, astro-service-worker instead ships a worker() adapter that takes a preset object. Many different providers and organisations seem to be embracing worker-like environments, and have even joined forces to set up a Community Group for web-interoperable JavaScript runtimes, and even Ryan Dahl (creater of Node/Deno) recently blogged about a similar future: JavaScript containers.

This kind of standardization is great, and the worker() adapter is built to be compatible with multiple worker-like environments. In fact; it uses the same service worker entrypoint as the client-side Service Worker integration, which is intended to run in the browser.

Shim Environment Specific Details

Since most of these environments support the same basics in the way of event handling, supporting API's like fetch, and Response, etc, it makes sense to make the worker() adapter modular. However, there may still be environment-specific details and differences between providers, like for example the way static files are handled, or polyfilling.

You can take care of these things in a so-called preset object. A preset object takes an optional shim array, which is an array of module specifiers pointing to environment-specific polyfills/shims, and an optional initConfig function that you can use to setup environment-specific configuration files, like for example a wrangler.toml in the case of Cloudflare.

For example the cloudflare preset contains the following shim:
cloudflare/static-assets.js:

import { getAssetFromKV } from '@cloudflare/kv-asset-handler';

self.MIDDLEWARE.push(async (event) => {
  try {
    return await getAssetFromKV(event);
  } catch {}
});
Enter fullscreen mode Exit fullscreen mode

That's all the cloudflare-specific logic required!

If you're interested in supporting your own worker-like environment, you can easily create a preset and provide your environment-specific code like so:

const myEnvironment = {
  /** Polyfill environment specific handling */
  shim: ['my-environment/static-assets.js'],
  /** Scaffold environment specific configuration file, like for example `wrangler.toml` */
  initConfig: (dir) => {}
}

worker(myEnvironment);
Enter fullscreen mode Exit fullscreen mode

For more information, see the documentation.

Offline

You can also combine this integration and go full-worker, by adding the serviceWorker integration:

import { defineConfig } from 'astro/config';
import worker, { cloudflare } from 'astro-service-worker/adapter';
import serviceWorker from 'astro-service-worker';

export default defineConfig({
  adapter: worker(cloudflare),
  integrations:[
    serviceWorker()
  ]
});
Enter fullscreen mode Exit fullscreen mode

And your Astro SSR app will now initially get rendered by a Cloudflare Worker, and then entirely in a Service Worker!

Top comments (0)