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.
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.
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).
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.
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.
Give your project a name and then fill out the build config:
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)
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 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)/*/
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