Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save danawoodman/0413b09a3f97db0b8eec6e6d707ef5b7 to your computer and use it in GitHub Desktop.
Save danawoodman/0413b09a3f97db0b8eec6e6d707ef5b7 to your computer and use it in GitHub Desktop.
Deploying CloudFlare Workers via CloudFlare Pages in a Turborepo monorepo

Deploying Cloudflare Workers via Cloudflare Pages in a Turborepo monorepo

Whew, what a mouthful.

Cloudflare Workers is an excellent platform for deploying a variety of applications but it has some limitations compared to Cloudflare's other product, Pages.

Pages gives you git integration which gives you auto-deploying via git push as well as pull request preview deployment links so you can test out features before pushing to production.

However, it's not super clear how to deploy a bare worker to Cloudflare Pages as Pages is more tailored right now for apps (SvelteKit, Astro, Next, etc), that is why I wrote up this little guide.

To accomplish all this, we are using what is called Advanced Mode in Cloudflare Pages, which is basically like ejecting from having Wrangler manage our build process for us.

Setup your monorepo

I'm assuming you're using a Turborepo monorepo here, but you can likely substitute a lot of this for another tool like Lerna, Nx, etc.

I'm also assuming the following pieces:

  • You have all your apps in apps/ folder
  • You have your packages (libraries) in packages/ which you import into your app code

I won't explain how to setup a monorepo, but I will note any key changes you need to make below.

Setup your app

Your Cloudflare Pages app only needs to consist of the following to get a "hello world" running:

apps/my-project/
  src/
    index.ts
  package.json
{
  "name": "my-project",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "run-p dev:*",
    "dev:wrangler": "wrangler pages dev dist --live-reload",
    "dev:esbuild": "npm run build -- --watch",
    "download-config": "npx wrangler pages download config my-project",
    "build": "esbuild src/index.ts --bundle --outfile=dist/_worker.js --format=esm",
    "deploy": "wrangler pages publish dist",
    "start": "wrangler dev",
    "cf-typegen": "wrangler types --env=production"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20240806.0",
    "typescript": "^5.5.4",
    "wrangler": "^3.71.0"
  }
}

Replace my-project everywhere in this file with your Cloudflare Pages app name.

NOTE: If your app needs a more advanced bundler than just esbuild, consider using tsup or something similar. Just note that you need the bundle to be built to apps/my-project/dist/_worker.js and it should be ESM. Also remember that Cloudflare Workers/Pages run in V8 isolates (like Web Workers), not Node.js, so some dependencies won't work in Cloudflare.

A hello world worker in Typescript:

// apps/my-project/src/index.ts
export default {
  fetch() {
    return new Response("Hello, world!");
  },
} satisfies ExportedHandler<Env>;

Now that you have your core files setup, run the following:

npm install
npm run download-config # will download a `wrangler.toml` file based on your Pages app
npm run cf-typegen # creates `worker-configuration.d.ts` from your wrangler.toml

The worker-configuration.d.ts will create a global Env type that your code above can use to have type checking for your bindings (KV, DO, D1, R2, etc).

Modify your turbo.json

Make sure to modify your turbo.json to handle your build and test commands and for it to pass in any environment variables your app needs to be build with, here is an example:

{
  "$schema": "https://turborepo.org/schema.json",
  "globalDependencies": [
    ".eslintrc.json",
    ".eslintrc.cjs",
    ".prettierrc",
    "tsconfig.json"
  ],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["src/**"],
      "outputs": [
        "dist/**",
        "build/**",
        "package/**",
        ".svelte-kit/**",
        ".vercel/**"
      ],
      "env": [
        "MY_API_TOKEN"
      ],
      "outputLogs": "new-only"
    },
    "test": {},
    "dev": {
      "dependsOn": ["^build"],
      "cache": false,
      "persistent": true
    }
  }
}

Commit all the newly generated files as well as your app code and push it up to your repo.

Create a new Cloudflare Pages app

Go to Workers & Pages and click the Pages tab to create a worker.

Click Connect to Git and find the monorepo you want to deploy apps from.

image

Give your project a name and then fill out the build config:

Set your build configuration

Configure your build process. If you're using Turborepo, you can run npm run build -- --filter my-project to only run npm run build for a package that has the name "my-project" in it's package.json file. This will also build all dependencies as needed.

I also like to run my test suite in the build process since Cloudlare Workers has no true CI syste and I don't want to setup a separate Github Action (or similar) to do this. This will fail to deploy your app if your tests fail to pass (which is what you almost always want to happen).

  • Location: Workers & Pages > [your worker] > Settings > Builds & deployments > Build configurations
  • Build command: npm run build -- --filter my-project && npm run test -- --filter my-project
  • Build ouput directory: apps/my-project/dist
  • Root directory: / (keep at the root of your repo so Turborepo can build all dependencies)

set build config

Deploy your app

After you set your build config, you can deploy your app.

If your app depends on bindings, your build will likely fail and you'll need to set those up before rebuilding.

Once you have created your app, let's make a change to only deploy your app when files for it change and not when unrelated files change:

Set your build watch paths

Set your build watch paths to watch your project folder (e.g. apps/my-project and any of your package dependencies (packages/*) for changes, then ignore all other apps in the apps/ folder.

This will make it so your Pages app will only re-deploy if one of these file changes, but not a different app. You can adjust these rules as needed.

  • Location: Workers & Pages > [your worker] > Settings > Builds & deployments > Build watch paths
  • Include paths: apps/my-project/*, packages/*
  • Exclude paths: apps/!(my-project)/*/

set build watch paths

Hook bindings

If you app needs somethign like KV, Durable Objects, R2, D1, etc, make sure to setup those bindings here

  • Location: Workers & Pages > [your worker] > Settings > Functions

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment