From 3b8276663d0b2b9d8dc7f81df17be303453ce611 Mon Sep 17 00:00:00 2001 From: Christian Nunciato Date: Tue, 8 Sep 2026 17:08:38 -0700 Subject: [PATCH 1/4] Add Cloudflare TypeScript examples - cloudflare-ts-serverless-d1: Worker backed by a D1 SQL database - cloudflare-ts-dns: managing DNS records on a zone - cloudflare-ts-cdn-waf: CDN caching and rate limiting in front of an origin Co-Authored-By: Claude Opus 4.8 --- cloudflare-ts-cdn-waf/.gitignore | 2 + cloudflare-ts-cdn-waf/Pulumi.yaml | 12 ++++ cloudflare-ts-cdn-waf/README.md | 56 +++++++++++++++++++ cloudflare-ts-cdn-waf/index.ts | 60 ++++++++++++++++++++ cloudflare-ts-cdn-waf/package.json | 13 +++++ cloudflare-ts-cdn-waf/tsconfig.json | 24 ++++++++ cloudflare-ts-dns/.gitignore | 2 + cloudflare-ts-dns/Pulumi.yaml | 7 +++ cloudflare-ts-dns/README.md | 53 ++++++++++++++++++ cloudflare-ts-dns/index.ts | 38 +++++++++++++ cloudflare-ts-dns/package.json | 13 +++++ cloudflare-ts-dns/tsconfig.json | 24 ++++++++ cloudflare-ts-serverless-d1/.gitignore | 2 + cloudflare-ts-serverless-d1/Pulumi.yaml | 7 +++ cloudflare-ts-serverless-d1/README.md | 60 ++++++++++++++++++++ cloudflare-ts-serverless-d1/index.ts | 68 +++++++++++++++++++++++ cloudflare-ts-serverless-d1/package.json | 13 +++++ cloudflare-ts-serverless-d1/tsconfig.json | 24 ++++++++ 18 files changed, 478 insertions(+) create mode 100644 cloudflare-ts-cdn-waf/.gitignore create mode 100644 cloudflare-ts-cdn-waf/Pulumi.yaml create mode 100644 cloudflare-ts-cdn-waf/README.md create mode 100644 cloudflare-ts-cdn-waf/index.ts create mode 100644 cloudflare-ts-cdn-waf/package.json create mode 100644 cloudflare-ts-cdn-waf/tsconfig.json create mode 100644 cloudflare-ts-dns/.gitignore create mode 100644 cloudflare-ts-dns/Pulumi.yaml create mode 100644 cloudflare-ts-dns/README.md create mode 100644 cloudflare-ts-dns/index.ts create mode 100644 cloudflare-ts-dns/package.json create mode 100644 cloudflare-ts-dns/tsconfig.json create mode 100644 cloudflare-ts-serverless-d1/.gitignore create mode 100644 cloudflare-ts-serverless-d1/Pulumi.yaml create mode 100644 cloudflare-ts-serverless-d1/README.md create mode 100644 cloudflare-ts-serverless-d1/index.ts create mode 100644 cloudflare-ts-serverless-d1/package.json create mode 100644 cloudflare-ts-serverless-d1/tsconfig.json diff --git a/cloudflare-ts-cdn-waf/.gitignore b/cloudflare-ts-cdn-waf/.gitignore new file mode 100644 index 0000000000..c6958891dd --- /dev/null +++ b/cloudflare-ts-cdn-waf/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/node_modules/ diff --git a/cloudflare-ts-cdn-waf/Pulumi.yaml b/cloudflare-ts-cdn-waf/Pulumi.yaml new file mode 100644 index 0000000000..d5d51cd763 --- /dev/null +++ b/cloudflare-ts-cdn-waf/Pulumi.yaml @@ -0,0 +1,12 @@ +name: cloudflare-ts-cdn-waf +runtime: nodejs +description: Put Cloudflare's CDN and WAF in front of an origin server +template: + config: + zoneId: + description: The Cloudflare zone ID to configure + origin: + description: The origin hostname to proxy traffic to + hostname: + description: The hostname to serve through Cloudflare + default: www diff --git a/cloudflare-ts-cdn-waf/README.md b/cloudflare-ts-cdn-waf/README.md new file mode 100644 index 0000000000..385393c068 --- /dev/null +++ b/cloudflare-ts-cdn-waf/README.md @@ -0,0 +1,56 @@ +[![Deploy this example with Pulumi](https://www.pulumi.com/images/deploy-with-pulumi/dark.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-cdn-waf/README.md#gh-light-mode-only) +[![Deploy this example with Pulumi](https://get.pulumi.com/new/button-light.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-cdn-waf/README.md#gh-dark-mode-only) + +# Cloudflare CDN and WAF in front of an origin + +Puts Cloudflare in front of an existing origin server: a proxied DNS record routes traffic +through Cloudflare, a [cache ruleset](https://developers.cloudflare.com/cache/how-to/cache-rules/) +caches responses at the edge, and a [rate-limiting ruleset](https://developers.cloudflare.com/waf/rate-limiting-rules/) +protects the origin from abuse. + +## Prerequisites + +1. [Install Pulumi](https://www.pulumi.com/docs/install/) +1. [Install Node.js](https://www.pulumi.com/docs/iac/languages-sdks/javascript/) +1. A domain already added to Cloudflare as a [zone](https://developers.cloudflare.com/dns/zone-setups/). +1. Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) + with DNS and Zone WAF edit permissions, and export it: + + ```bash + export CLOUDFLARE_API_TOKEN= + ``` + +## Deploying the example + +1. Create a new stack: + + ```bash + pulumi stack init dev + ``` + +1. Configure the zone and origin: + + ```bash + pulumi config set zoneId + pulumi config set origin origin.example.com + ``` + +1. Install dependencies and deploy: + + ```bash + npm install + pulumi up + ``` + +1. The proxied hostname is exported as `url`: + + ```bash + pulumi stack output url + ``` + +## Cleaning up + +```bash +pulumi destroy +pulumi stack rm dev +``` diff --git a/cloudflare-ts-cdn-waf/index.ts b/cloudflare-ts-cdn-waf/index.ts new file mode 100644 index 0000000000..5aae0d0b07 --- /dev/null +++ b/cloudflare-ts-cdn-waf/index.ts @@ -0,0 +1,60 @@ +import * as pulumi from "@pulumi/pulumi"; +import * as cloudflare from "@pulumi/cloudflare"; + +// Import the program's configuration settings. +const config = new pulumi.Config(); +const zoneId = config.require("zoneId"); +const origin = config.require("origin"); +const hostname = config.get("hostname") || "www"; + +// A proxied DNS record, so traffic flows through Cloudflare's CDN and WAF. +const record = new cloudflare.DnsRecord("record", { + zoneId: zoneId, + name: hostname, + type: "CNAME", + content: origin, + ttl: 1, + proxied: true, +}); + +// Cache responses at the edge, overriding the origin's cache headers. +const cache = new cloudflare.Ruleset("cache", { + zoneId: zoneId, + name: "cache-everything", + kind: "zone", + phase: "http_request_cache_settings", + rules: [{ + action: "set_cache_settings", + expression: "true", + enabled: true, + actionParameters: { + cache: true, + edgeTtl: { + mode: "override_origin", + default: 3600, + }, + }, + }], +}); + +// Rate-limit requests per IP to protect the origin from abuse. +const rateLimit = new cloudflare.Ruleset("rate-limit", { + zoneId: zoneId, + name: "rate-limit", + kind: "zone", + phase: "http_ratelimit", + rules: [{ + action: "block", + expression: "true", + enabled: true, + ratelimit: { + characteristics: ["ip.src", "cf.colo.id"], + period: 60, + requestsPerPeriod: 100, + mitigationTimeout: 60, + }, + }], +}); + +// Export the URL served through Cloudflare. +export const url = pulumi.interpolate`https://${record.name}`; diff --git a/cloudflare-ts-cdn-waf/package.json b/cloudflare-ts-cdn-waf/package.json new file mode 100644 index 0000000000..008b319526 --- /dev/null +++ b/cloudflare-ts-cdn-waf/package.json @@ -0,0 +1,13 @@ +{ + "name": "cloudflare-ts-cdn-waf", + "type": "module", + "devDependencies": { + "@types/node": "22.13.14", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + }, + "dependencies": { + "@pulumi/cloudflare": "6.20.0", + "@pulumi/pulumi": "3.228.0" + } +} diff --git a/cloudflare-ts-cdn-waf/tsconfig.json b/cloudflare-ts-cdn-waf/tsconfig.json new file mode 100644 index 0000000000..d12a4cdf88 --- /dev/null +++ b/cloudflare-ts-cdn-waf/tsconfig.json @@ -0,0 +1,24 @@ +{ + "ts-node": { + "esm": true + }, + "compilerOptions": { + // Output + "outDir": "bin", + "sourceMap": true, + // Environment + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "nodenext", + "moduleDetection": "force", + "types": ["node"], + // Type Checking + "strict": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "files": [ + "index.ts" + ] +} diff --git a/cloudflare-ts-dns/.gitignore b/cloudflare-ts-dns/.gitignore new file mode 100644 index 0000000000..c6958891dd --- /dev/null +++ b/cloudflare-ts-dns/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/node_modules/ diff --git a/cloudflare-ts-dns/Pulumi.yaml b/cloudflare-ts-dns/Pulumi.yaml new file mode 100644 index 0000000000..5809396591 --- /dev/null +++ b/cloudflare-ts-dns/Pulumi.yaml @@ -0,0 +1,7 @@ +name: cloudflare-ts-dns +runtime: nodejs +description: Manage Cloudflare DNS records on a zone +template: + config: + zoneId: + description: The Cloudflare zone ID to manage records in diff --git a/cloudflare-ts-dns/README.md b/cloudflare-ts-dns/README.md new file mode 100644 index 0000000000..1c03b99be4 --- /dev/null +++ b/cloudflare-ts-dns/README.md @@ -0,0 +1,53 @@ +[![Deploy this example with Pulumi](https://www.pulumi.com/images/deploy-with-pulumi/dark.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-dns/README.md#gh-light-mode-only) +[![Deploy this example with Pulumi](https://get.pulumi.com/new/button-light.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-dns/README.md#gh-dark-mode-only) + +# Manage Cloudflare DNS records + +Manages a set of [DNS records](https://developers.cloudflare.com/dns/manage-dns-records/) on a +Cloudflare zone — an `A` record, a `CNAME`, and a `TXT` record. + +## Prerequisites + +1. [Install Pulumi](https://www.pulumi.com/docs/install/) +1. [Install Node.js](https://www.pulumi.com/docs/iac/languages-sdks/javascript/) +1. A domain already added to Cloudflare as a [zone](https://developers.cloudflare.com/dns/zone-setups/). +1. Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) + with DNS edit permissions, and export it: + + ```bash + export CLOUDFLARE_API_TOKEN= + ``` + +## Deploying the example + +1. Create a new stack: + + ```bash + pulumi stack init dev + ``` + +1. Set the zone to manage (find the zone ID on your domain's overview page in the Cloudflare dashboard): + + ```bash + pulumi config set zoneId + ``` + +1. Install dependencies and deploy: + + ```bash + npm install + pulumi up + ``` + +1. Inspect the created records: + + ```bash + pulumi stack output records + ``` + +## Cleaning up + +```bash +pulumi destroy +pulumi stack rm dev +``` diff --git a/cloudflare-ts-dns/index.ts b/cloudflare-ts-dns/index.ts new file mode 100644 index 0000000000..afb12e0e6f --- /dev/null +++ b/cloudflare-ts-dns/index.ts @@ -0,0 +1,38 @@ +import * as pulumi from "@pulumi/pulumi"; +import * as cloudflare from "@pulumi/cloudflare"; + +// Import the program's configuration settings. +const config = new pulumi.Config(); +const zoneId = config.require("zoneId"); + +// An A record pointing "www" at an origin server. +const www = new cloudflare.DnsRecord("www", { + zoneId: zoneId, + name: "www", + type: "A", + content: "192.0.2.1", + ttl: 3600, + proxied: false, +}); + +// A CNAME aliasing "docs" to an externally hosted site. +const docs = new cloudflare.DnsRecord("docs", { + zoneId: zoneId, + name: "docs", + type: "CNAME", + content: "hosting.example.com", + ttl: 3600, + proxied: false, +}); + +// A TXT record, e.g. for domain verification or SPF. +const txt = new cloudflare.DnsRecord("txt", { + zoneId: zoneId, + name: "mail", + type: "TXT", + content: "v=spf1 include:_spf.example.com ~all", + ttl: 3600, +}); + +// Export the fully-qualified names of the records. +export const records = [www.name, docs.name, txt.name]; diff --git a/cloudflare-ts-dns/package.json b/cloudflare-ts-dns/package.json new file mode 100644 index 0000000000..22f2ffec41 --- /dev/null +++ b/cloudflare-ts-dns/package.json @@ -0,0 +1,13 @@ +{ + "name": "cloudflare-ts-dns", + "type": "module", + "devDependencies": { + "@types/node": "22.13.14", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + }, + "dependencies": { + "@pulumi/cloudflare": "6.20.0", + "@pulumi/pulumi": "3.228.0" + } +} diff --git a/cloudflare-ts-dns/tsconfig.json b/cloudflare-ts-dns/tsconfig.json new file mode 100644 index 0000000000..d12a4cdf88 --- /dev/null +++ b/cloudflare-ts-dns/tsconfig.json @@ -0,0 +1,24 @@ +{ + "ts-node": { + "esm": true + }, + "compilerOptions": { + // Output + "outDir": "bin", + "sourceMap": true, + // Environment + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "nodenext", + "moduleDetection": "force", + "types": ["node"], + // Type Checking + "strict": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "files": [ + "index.ts" + ] +} diff --git a/cloudflare-ts-serverless-d1/.gitignore b/cloudflare-ts-serverless-d1/.gitignore new file mode 100644 index 0000000000..c6958891dd --- /dev/null +++ b/cloudflare-ts-serverless-d1/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/node_modules/ diff --git a/cloudflare-ts-serverless-d1/Pulumi.yaml b/cloudflare-ts-serverless-d1/Pulumi.yaml new file mode 100644 index 0000000000..18b6fe9aae --- /dev/null +++ b/cloudflare-ts-serverless-d1/Pulumi.yaml @@ -0,0 +1,7 @@ +name: cloudflare-ts-serverless-d1 +runtime: nodejs +description: A serverless Cloudflare Worker backed by a D1 SQL database +template: + config: + accountId: + description: The Cloudflare account ID to deploy into diff --git a/cloudflare-ts-serverless-d1/README.md b/cloudflare-ts-serverless-d1/README.md new file mode 100644 index 0000000000..197bcb4c45 --- /dev/null +++ b/cloudflare-ts-serverless-d1/README.md @@ -0,0 +1,60 @@ +[![Deploy this example with Pulumi](https://www.pulumi.com/images/deploy-with-pulumi/dark.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-serverless-d1/README.md#gh-light-mode-only) +[![Deploy this example with Pulumi](https://get.pulumi.com/new/button-light.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/cloudflare-ts-serverless-d1/README.md#gh-dark-mode-only) + +# Serverless Cloudflare Worker backed by a D1 database + +A [Cloudflare Worker](https://developers.cloudflare.com/workers/) that records each visit in a +[D1](https://developers.cloudflare.com/d1/) serverless SQL database and returns the running +total. The Worker is uploaded as a version and deployed with `cloudflare.WorkersDeployment`, +and is served on its `*.workers.dev` subdomain. + +## Prerequisites + +1. [Install Pulumi](https://www.pulumi.com/docs/install/) +1. [Install Node.js](https://www.pulumi.com/docs/iac/languages-sdks/javascript/) +1. Create a [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) + with Workers and D1 edit permissions, and export it: + + ```bash + export CLOUDFLARE_API_TOKEN= + ``` + +## Deploying the example + +1. Create a new stack: + + ```bash + pulumi stack init dev + ``` + +1. Set your Cloudflare account ID: + + ```bash + pulumi config set accountId + ``` + +1. Install dependencies: + + ```bash + npm install + ``` + +1. Run `pulumi up` to deploy: + + ```bash + pulumi up + ``` + +1. Visit the Worker's URL, refreshing a few times to watch the counter increment: + + ```bash + curl "$(pulumi stack output url)" + # Hello, world! This page has been visited 1 times. + ``` + +## Cleaning up + +```bash +pulumi destroy +pulumi stack rm dev +``` diff --git a/cloudflare-ts-serverless-d1/index.ts b/cloudflare-ts-serverless-d1/index.ts new file mode 100644 index 0000000000..27a20216de --- /dev/null +++ b/cloudflare-ts-serverless-d1/index.ts @@ -0,0 +1,68 @@ +import * as pulumi from "@pulumi/pulumi"; +import * as cloudflare from "@pulumi/cloudflare"; + +// Import the program's configuration settings. +const config = new pulumi.Config(); +const accountId = config.require("accountId"); + +// A D1 (serverless SQLite) database to store visits. +const db = new cloudflare.D1Database("db", { + accountId: accountId, + name: "visits-db", +}); + +// The Worker's code: records each visit in D1 and returns the running total. +const script = `export default { + async fetch(request, env, ctx) { + await env.DB.prepare( + "CREATE TABLE IF NOT EXISTS visits (id INTEGER PRIMARY KEY AUTOINCREMENT, visited_at TEXT)" + ).run(); + await env.DB.prepare("INSERT INTO visits (visited_at) VALUES (?1)") + .bind(new Date().toISOString()) + .run(); + const row = await env.DB.prepare("SELECT COUNT(*) AS count FROM visits").first(); + return new Response("Hello, world! This page has been visited " + row.count + " times."); + }, +}; +`; + +// A Cloudflare Worker, exposed on its workers.dev subdomain. +const worker = new cloudflare.Worker("worker", { + accountId: accountId, + name: "d1-app", + subdomain: { + enabled: true, + }, +}); + +// A version of the Worker containing the code and its D1 binding. +const version = new cloudflare.WorkerVersion("version", { + accountId: accountId, + workerId: worker.id, + compatibilityDate: "2025-01-01", + mainModule: "worker.js", + bindings: [{ + name: "DB", + type: "d1", + databaseId: db.uuid, + }], + modules: [{ + name: "worker.js", + contentType: "application/javascript+module", + contentBase64: Buffer.from(script).toString("base64"), + }], +}); + +// Deploy the version so it serves all of the application's traffic. +const deployment = new cloudflare.WorkersDeployment("deployment", { + accountId: accountId, + scriptName: worker.name, + strategy: "percentage", + versions: [{ + versionId: version.id, + percentage: 100, + }], +}); + +// Export the application's URL. +export const url = worker.subdomain.url; diff --git a/cloudflare-ts-serverless-d1/package.json b/cloudflare-ts-serverless-d1/package.json new file mode 100644 index 0000000000..fd857086ff --- /dev/null +++ b/cloudflare-ts-serverless-d1/package.json @@ -0,0 +1,13 @@ +{ + "name": "cloudflare-ts-serverless-d1", + "type": "module", + "devDependencies": { + "@types/node": "22.13.14", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + }, + "dependencies": { + "@pulumi/cloudflare": "6.20.0", + "@pulumi/pulumi": "3.228.0" + } +} diff --git a/cloudflare-ts-serverless-d1/tsconfig.json b/cloudflare-ts-serverless-d1/tsconfig.json new file mode 100644 index 0000000000..d12a4cdf88 --- /dev/null +++ b/cloudflare-ts-serverless-d1/tsconfig.json @@ -0,0 +1,24 @@ +{ + "ts-node": { + "esm": true + }, + "compilerOptions": { + // Output + "outDir": "bin", + "sourceMap": true, + // Environment + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "nodenext", + "moduleDetection": "force", + "types": ["node"], + // Type Checking + "strict": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "files": [ + "index.ts" + ] +} From 51d9a29a0bdc86db1f94472f6f2fc21624ae140b Mon Sep 17 00:00:00 2001 From: Christian Nunciato Date: Wed, 9 Sep 2026 13:07:59 -0700 Subject: [PATCH 2/4] cloudflare-ts-cdn-waf: fix rate-limit period/timeout for Free plan and export the FQDN url Co-Authored-By: Claude Opus 4.8 --- cloudflare-ts-cdn-waf/index.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cloudflare-ts-cdn-waf/index.ts b/cloudflare-ts-cdn-waf/index.ts index 5aae0d0b07..f1a08aa72e 100644 --- a/cloudflare-ts-cdn-waf/index.ts +++ b/cloudflare-ts-cdn-waf/index.ts @@ -7,6 +7,9 @@ const zoneId = config.require("zoneId"); const origin = config.require("origin"); const hostname = config.get("hostname") || "www"; +// Look up the zone so we can build the fully-qualified hostname. +const zone = cloudflare.getZoneOutput({ zoneId: zoneId }); + // A proxied DNS record, so traffic flows through Cloudflare's CDN and WAF. const record = new cloudflare.DnsRecord("record", { zoneId: zoneId, @@ -49,12 +52,14 @@ const rateLimit = new cloudflare.Ruleset("rate-limit", { enabled: true, ratelimit: { characteristics: ["ip.src", "cf.colo.id"], - period: 60, + // period and mitigationTimeout must be 10 on the Free plan; higher + // values require a paid plan. + period: 10, requestsPerPeriod: 100, - mitigationTimeout: 60, + mitigationTimeout: 10, }, }], }); // Export the URL served through Cloudflare. -export const url = pulumi.interpolate`https://${record.name}`; +export const url = pulumi.interpolate`https://${hostname}.${zone.name}`; From bf4b12c86b6264c1564af003efc5ef5f37c9fe01 Mon Sep 17 00:00:00 2001 From: Christian Nunciato Date: Wed, 9 Sep 2026 13:16:10 -0700 Subject: [PATCH 3/4] Add copyright headers and sort imports in Cloudflare examples Co-Authored-By: Claude Opus 4.8 --- cloudflare-ts-cdn-waf/index.ts | 4 +++- cloudflare-ts-dns/index.ts | 4 +++- cloudflare-ts-serverless-d1/index.ts | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cloudflare-ts-cdn-waf/index.ts b/cloudflare-ts-cdn-waf/index.ts index f1a08aa72e..d584a730ad 100644 --- a/cloudflare-ts-cdn-waf/index.ts +++ b/cloudflare-ts-cdn-waf/index.ts @@ -1,5 +1,7 @@ -import * as pulumi from "@pulumi/pulumi"; +// Copyright 2016-2026, Pulumi Corporation. All rights reserved. + import * as cloudflare from "@pulumi/cloudflare"; +import * as pulumi from "@pulumi/pulumi"; // Import the program's configuration settings. const config = new pulumi.Config(); diff --git a/cloudflare-ts-dns/index.ts b/cloudflare-ts-dns/index.ts index afb12e0e6f..032437e185 100644 --- a/cloudflare-ts-dns/index.ts +++ b/cloudflare-ts-dns/index.ts @@ -1,5 +1,7 @@ -import * as pulumi from "@pulumi/pulumi"; +// Copyright 2016-2026, Pulumi Corporation. All rights reserved. + import * as cloudflare from "@pulumi/cloudflare"; +import * as pulumi from "@pulumi/pulumi"; // Import the program's configuration settings. const config = new pulumi.Config(); diff --git a/cloudflare-ts-serverless-d1/index.ts b/cloudflare-ts-serverless-d1/index.ts index 27a20216de..912ef927dc 100644 --- a/cloudflare-ts-serverless-d1/index.ts +++ b/cloudflare-ts-serverless-d1/index.ts @@ -1,5 +1,7 @@ -import * as pulumi from "@pulumi/pulumi"; +// Copyright 2016-2026, Pulumi Corporation. All rights reserved. + import * as cloudflare from "@pulumi/cloudflare"; +import * as pulumi from "@pulumi/pulumi"; // Import the program's configuration settings. const config = new pulumi.Config(); From 79e1996c12926a193628707b9208dc242cd856cc Mon Sep 17 00:00:00 2001 From: Christian Nunciato Date: Wed, 9 Sep 2026 16:37:11 -0700 Subject: [PATCH 4/4] cloudflare-ts-serverless-d1: move Worker code to worker.js via contentFile Co-Authored-By: Claude Opus 4.8 --- cloudflare-ts-serverless-d1/index.ts | 19 ++----------------- cloudflare-ts-serverless-d1/worker.js | 12 ++++++++++++ 2 files changed, 14 insertions(+), 17 deletions(-) create mode 100644 cloudflare-ts-serverless-d1/worker.js diff --git a/cloudflare-ts-serverless-d1/index.ts b/cloudflare-ts-serverless-d1/index.ts index 912ef927dc..4ff971fbde 100644 --- a/cloudflare-ts-serverless-d1/index.ts +++ b/cloudflare-ts-serverless-d1/index.ts @@ -13,21 +13,6 @@ const db = new cloudflare.D1Database("db", { name: "visits-db", }); -// The Worker's code: records each visit in D1 and returns the running total. -const script = `export default { - async fetch(request, env, ctx) { - await env.DB.prepare( - "CREATE TABLE IF NOT EXISTS visits (id INTEGER PRIMARY KEY AUTOINCREMENT, visited_at TEXT)" - ).run(); - await env.DB.prepare("INSERT INTO visits (visited_at) VALUES (?1)") - .bind(new Date().toISOString()) - .run(); - const row = await env.DB.prepare("SELECT COUNT(*) AS count FROM visits").first(); - return new Response("Hello, world! This page has been visited " + row.count + " times."); - }, -}; -`; - // A Cloudflare Worker, exposed on its workers.dev subdomain. const worker = new cloudflare.Worker("worker", { accountId: accountId, @@ -37,7 +22,7 @@ const worker = new cloudflare.Worker("worker", { }, }); -// A version of the Worker containing the code and its D1 binding. +// A version of the Worker containing the code (worker.js) and its D1 binding. const version = new cloudflare.WorkerVersion("version", { accountId: accountId, workerId: worker.id, @@ -51,7 +36,7 @@ const version = new cloudflare.WorkerVersion("version", { modules: [{ name: "worker.js", contentType: "application/javascript+module", - contentBase64: Buffer.from(script).toString("base64"), + contentFile: "worker.js", }], }); diff --git a/cloudflare-ts-serverless-d1/worker.js b/cloudflare-ts-serverless-d1/worker.js new file mode 100644 index 0000000000..842aae8777 --- /dev/null +++ b/cloudflare-ts-serverless-d1/worker.js @@ -0,0 +1,12 @@ +export default { + async fetch(request, env) { + await env.DB.prepare( + "CREATE TABLE IF NOT EXISTS visits (id INTEGER PRIMARY KEY AUTOINCREMENT, visited_at TEXT)", + ).run(); + await env.DB.prepare("INSERT INTO visits (visited_at) VALUES (?1)") + .bind(new Date().toISOString()) + .run(); + const row = await env.DB.prepare("SELECT COUNT(*) AS count FROM visits").first(); + return new Response("Hello, world! This page has been visited " + row.count + " times."); + }, +};