This guide helps AI agents review, maintain, and contribute to Cloudflare Worker templates. It documents patterns, common issues, and testing strategies learned from real PR reviews.
# Validate everything before starting work
pnpm run check
# After making changes, auto-fix what's possible
pnpm run fix
# Run template-specific tests
cd <template-dir> && pnpm test
# Lint templates for compliance
pnpm run check:templatesWhen reviewing a template PR, verify these items in order:
- package.json - Has all required fields
- README.md - Has dashboard markers and deploy button at top
- Tests - Minimum 5 tests exist and pass
- Template Linter -
pnpm run check:templatespasses - Build - Template compiles without errors
Symptom: Template linter fails with missing description error.
Fix: Add a one-line description to package.json:
{
"name": "my-template",
"description": "Brief description of what this template does."
}Symptom: Dashboard won't display template details correctly.
Fix: Add markers around the content that should appear in Cloudflare Dashboard:
[](...)
<!-- dash-content-start -->
## Overview
This template demonstrates...
<!-- dash-content-end -->
## Getting Started
...Important: The Deploy to Cloudflare button should be at the TOP of the README, before the dash-content markers.
Symptom: Users can't easily find the deploy button.
Fix: Move the Deploy to Cloudflare button to the very top of README.md, before any other content.
Symptom: Template has fewer than 5 tests or no test file.
Fix: Create tests using @cloudflare/vitest-pool-workers. See Testing Patterns below.
Symptom: Template linter fails with configuration format error.
Fix: Convert wrangler.toml to wrangler.json or wrangler.jsonc.
Symptom: Slower deployments, potential dependency issues.
Fix: Run from repo root:
pnpm run fix:lockfilesEvery template needs a vitest.config.ts and test file(s). The simplest setup:
// vitest.config.ts
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersProject({
test: {
poolOptions: {
workers: {
singleWorker: true,
wrangler: {
configPath: "./wrangler.json",
},
},
},
},
});// test/index.test.ts
import { SELF } from "cloudflare:test";
import { describe, it, expect } from "vitest";
describe("Template Name", () => {
it("returns 200 on homepage", async () => {
const response = await SELF.fetch("https://example.com/");
expect(response.status).toBe(200);
});
it("returns HTML content", async () => {
const response = await SELF.fetch("https://example.com/");
const html = await response.text();
expect(html).toContain("<!DOCTYPE html>");
});
// Add at least 3 more meaningful tests...
});When a template uses Service Bindings to call other Workers, you need stub workers for testing. Define them inline in vitest.config.ts:
// vitest.config.ts
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
// Define stub worker as inline JavaScript
const stubWorkerScript = /* javascript */ `
export default {
async fetch(request) {
const url = new URL(request.url);
return new Response(\`Stub response for: \${url.pathname}\`, {
headers: { "Content-Type": "text/plain" },
});
},
};
`;
export default defineWorkersProject({
test: {
poolOptions: {
workers: {
singleWorker: true,
wrangler: {
configPath: "./wrangler.json",
},
miniflare: {
workers: [
{
name: "my-service-binding-name", // Must match binding name in wrangler.json
modules: [
{
type: "ESModule",
path: "index.js",
contents: stubWorkerScript,
},
],
compatibilityDate: "2024-01-01",
},
],
},
},
},
},
});KV Namespace: Automatically available via wrangler.json config, use env.MY_KV in tests.
D1 Database: Define migrations in wrangler.json, database is auto-created for tests.
R2 Bucket: Automatically available via wrangler.json config.
Durable Objects: Define in wrangler.json, accessible in tests.
Service Bindings: Use inline stub workers as shown above.
Aim for at least 5 tests covering:
- Happy path - Main functionality works
- Error handling - Invalid inputs return appropriate errors
- Edge cases - Empty data, missing parameters
- API endpoints - Each endpoint returns expected status/data
- HTML/UI - Key elements are present in responses
When reviewing template code, watch for these common issues:
Request Interception Order: Middleware or special endpoints must be checked BEFORE forwarding to upstream services.
// WRONG - Check happens after fetch, never executes
const response = await upstream.fetch(request);
if (url.pathname === "/__special") {
return new Response("special");
}
// CORRECT - Check happens before fetch
if (url.pathname === "/__special") {
return new Response("special");
}
const response = await upstream.fetch(request);Environment Variable Access: Bindings must be accessed from the env parameter, not global scope.
// WRONG - Won't work in Workers
const db = globalThis.DB;
// CORRECT - Access from env parameter
export default {
async fetch(request, env) {
const db = env.DB;
},
};A good template README should have:
- Deploy button at the very top
- Dashboard content section with overview (between markers)
- Screenshot or demo link
- Local development instructions
- Environment variables/secrets documentation
- Links to relevant Cloudflare docs
When updating a PR that exists on a different branch:
# Find the PR's actual branch name
gh pr view <PR_NUMBER> --json headRefName
# Push to the PR branch (not your local branch name)
git push origin HEAD:<pr-branch-name> --force-with-leaseExample:
# PR #877 is on branch "brayden/mfe"
git push origin HEAD:brayden/mfe --force-with-lease# Checkout the PR
gh pr checkout <PR_NUMBER>
# Run template linter first - catches most issues
pnpm run check:templates
# Run template's own tests
cd <template-dir> && pnpm testAddress issues in this order (most common first):
- Add missing
descriptionto package.json - Add/fix dashboard content markers in README.md
- Move Deploy button to top of README.md
- Add or fix tests to meet minimum of 5
- Fix any code bugs discovered during testing
# From repo root
pnpm run check:templates
cd <template-dir> && pnpm test# Commit with descriptive message
git add -A
git commit -m "Add tests and address review feedback for <template-name>"
# Push to PR branch
git push origin HEAD:<pr-branch-name> --force-with-leaseFor the full list of template requirements, see CONTRIBUTING.md.
The CONTRIBUTING.md file contains the authoritative checklist for template compliance, including:
package.jsonrequired fields andcloudflaremetadataREADME.mdcontent requirements and dashboard markerspackage-lock.jsongeneration.gitignorerequirements- Playwright E2E test requirements
- Worker secrets and environment variables
These items cannot be completed by external contributors:
cloudflare.preview_image_url- Screenshot uploaded by Growth teamcloudflare.preview_icon_url- Icon uploaded by Growth teamcloudflare.publish: true- Set by Cloudflare team when ready to publish
The linter doesn't catch everything. Manual checks needed for:
- Dashboard content marker placement
- Deploy button position
- Code logic bugs
- Test quality (only checks count, not coverage)
If tests fail with "binding not found" errors:
- Check wrangler.json has the binding defined
- For Service Bindings, add stub workers in vitest.config.ts
- Ensure binding names match exactly (case-sensitive)
The latest compatibility date supported by the installed Cloudflare Workers Runtime is "2025-09-06",
but you've requested "2025-10-08". Falling back to "2025-09-06"...
This warning is normal during local testing. The template linter enforces a specific compatibility date that may be newer than the locally installed runtime. Tests will still pass.