Skip to content

Commit dcd7308

Browse files
committed
Add workers assets example and update workers upload example
1 parent 980edbf commit dcd7308

File tree

2 files changed

+567
-61
lines changed

2 files changed

+567
-61
lines changed

examples/workers/script-upload.ts

Lines changed: 143 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,168 @@
11
#!/usr/bin/env -S npm run tsn -T
22

3-
/*
4-
* Generate an API token: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/
5-
* (Not Global API Key!)
3+
/**
4+
* Create and deploy a Worker
5+
*
6+
* Docs:
7+
* - https://developers.cloudflare.com/workers/configuration/versions-and-deployments/
8+
* - https://developers.cloudflare.com/workers/platform/infrastructure-as-code/
9+
*
10+
* Prerequisites:
11+
* 1. Generate an API token: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/
12+
* 2. Find your account ID: https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/
13+
* 3. Find your workers.dev subdomain: https://developers.cloudflare.com/workers/configuration/routing/workers-dev/
614
*
7-
* Find your account id: https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/
15+
* Environment variables:
16+
* - CLOUDFLARE_API_TOKEN (required)
17+
* - CLOUDFLARE_ACCOUNT_ID (required)
18+
* - CLOUDFLARE_SUBDOMAIN (optional)
819
*
9-
* Set these environment variables:
10-
* - CLOUDFLARE_API_TOKEN
11-
* - CLOUDFLARE_ACCOUNT_ID
12-
*
13-
* ### Workers for Platforms ###
14-
*
15-
* For uploading a User Worker to a dispatch namespace:
16-
* https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/
17-
*
18-
* Define a "dispatchNamespaceName" variable and change the entire "const script = " line to the following:
19-
* "const script = await client.workersForPlatforms.dispatch.namespaces.scripts.update(dispatchNamespaceName, scriptName, {"
20+
* Usage:
21+
* Run this script to deploy a simple "Hello World" Worker.
22+
* Access it at: my-hello-world-worker.$subdomain.workers.dev
2023
*/
2124

25+
import { exit } from 'node:process';
26+
2227
import Cloudflare from 'cloudflare';
23-
import { toFile } from 'cloudflare/index';
2428

25-
const apiToken = process.env['CLOUDFLARE_API_TOKEN'] ?? '';
26-
if (!apiToken) {
27-
throw new Error('Please set envar CLOUDFLARE_ACCOUNT_ID');
29+
interface Config {
30+
apiToken: string;
31+
accountId: string;
32+
subdomain: string | undefined;
33+
workerName: string;
2834
}
2935

30-
const accountID = process.env['CLOUDFLARE_ACCOUNT_ID'] ?? '';
31-
if (!accountID) {
32-
throw new Error('Please set envar CLOUDFLARE_API_TOKEN');
36+
const WORKER_NAME = 'my-hello-world-worker';
37+
const SCRIPT_FILENAME = `${WORKER_NAME}.mjs`;
38+
39+
function loadConfig(): Config {
40+
const apiToken = process.env['CLOUDFLARE_API_TOKEN'];
41+
if (!apiToken) {
42+
throw new Error('Missing required environment variable: CLOUDFLARE_API_TOKEN');
43+
}
44+
45+
const accountId = process.env['CLOUDFLARE_ACCOUNT_ID'];
46+
if (!accountId) {
47+
throw new Error('Missing required environment variable: CLOUDFLARE_ACCOUNT_ID');
48+
}
49+
50+
const subdomain = process.env['CLOUDFLARE_SUBDOMAIN'];
51+
52+
return {
53+
apiToken,
54+
accountId,
55+
subdomain: subdomain || undefined,
56+
workerName: WORKER_NAME,
57+
};
3358
}
3459

60+
const config = loadConfig();
3561
const client = new Cloudflare({
36-
apiToken: apiToken,
62+
apiToken: config.apiToken,
3763
});
3864

39-
async function main() {
40-
const scriptName = 'my-hello-world-script';
41-
const scriptFileName = `${scriptName}.mjs`;
65+
async function findWorkerByName(name: string): Promise<Cloudflare.Workers.Worker | null> {
66+
const workers = await client.workers.list({ account_id: config.accountId });
67+
for await (const worker of workers) {
68+
if (worker.name === name) {
69+
return worker
70+
}
71+
}
72+
return null;
73+
}
74+
75+
async function main(): Promise<void> {
76+
try {
77+
console.log('🚀 Starting Worker creation and deployment...');
4278

43-
// Workers Scripts prefer Module Syntax
44-
// https://blog.cloudflare.com/workers-javascript-modules/
45-
const scriptContent = `
46-
export default {
47-
async fetch(request, env, ctx) {
48-
return new Response(env.MESSAGE, { status: 200 });
79+
const scriptContent = `
80+
export default {
81+
async fetch(request, env, ctx) {
82+
return new Response(env.MESSAGE, { status: 200 });
83+
},
84+
}`.trim();
85+
86+
// Until name aliasing is support, we need to list all Workers to check if one
87+
// with the name we're trying to use already exists
88+
let workerResult = await findWorkerByName(config.workerName);
89+
90+
// Create the Worker entity if it doesn't exist
91+
let worker;
92+
if (!workerResult) {
93+
console.log(`✏️ Creating Worker ${config.workerName}...`);
94+
worker = await client.workers.create({
95+
account_id: config.accountId,
96+
name: config.workerName,
97+
subdomain: {
98+
enabled: config.subdomain !== undefined,
99+
},
100+
observability: {
101+
enabled: true,
102+
},
103+
});
104+
} else {
105+
console.log(`♻️ Worker ${config.workerName} already exists. Using it.`);
106+
worker = {
107+
id: workerResult.id
49108
}
50-
};
51-
`;
109+
}
52110

53-
try {
54-
// https://developers.cloudflare.com/api/resources/workers/subresources/scripts/methods/update/
55-
const script = await client.workers.scripts.update(scriptName, {
56-
account_id: accountID,
57-
// https://developers.cloudflare.com/workers/configuration/multipart-upload-metadata/
58-
metadata: {
59-
main_module: scriptFileName,
60-
bindings: [
61-
{
62-
type: 'plain_text',
63-
name: 'MESSAGE',
64-
text: 'Hello World!',
111+
console.log(`⚙️ Worker id: ${worker.id}`);
112+
console.log('✏️ Creating Worker version...');
113+
114+
// Create the first version of the Worker
115+
const version = await client.workers.versions.create(worker.id, {
116+
account_id: config.accountId,
117+
main_module: SCRIPT_FILENAME,
118+
compatibility_date: new Date().toISOString().split('T')[0]!,
119+
bindings: [
120+
{
121+
type: 'plain_text',
122+
name: 'MESSAGE',
123+
text: 'Hello World!',
124+
},
125+
],
126+
modules: [
127+
{
128+
name: SCRIPT_FILENAME,
129+
content_type: 'application/javascript+module',
130+
content_base64: Buffer.from(scriptContent).toString('base64'),
131+
},
132+
],
133+
});
134+
135+
console.log(`⚙️ Version id: ${version.id}`);
136+
console.log('🚚 Creating Worker deployment...');
137+
138+
// Create a deployment and point all traffic to the version we created
139+
await client.workers.scripts.deployments.create(config.workerName, {
140+
account_id: config.accountId,
141+
strategy: 'percentage',
142+
versions: [
143+
{
144+
percentage: 100,
145+
version_id: version.id,
65146
},
66147
],
67-
},
68-
files: {
69-
// Add main_module file
70-
[scriptFileName]: await toFile(Buffer.from(scriptContent), scriptFileName, {
71-
type: 'application/javascript+module',
72-
}),
73-
// Can add other files, such as more modules or source maps
74-
// [sourceMapFileName]: await toFile(Buffer.from(sourceMapContent), sourceMapFileName, {
75-
// type: 'application/source-map',
76-
// }),
77-
},
78148
});
79-
console.log('Script Upload success!');
80-
console.log(JSON.stringify(script, null, 2));
149+
150+
console.log('✅ Deployment successful!');
151+
152+
if (config.subdomain) {
153+
console.log(`
154+
🌍 Your Worker is live!
155+
📍 URL: https://${config.workerName}.${config.subdomain}.workers.dev/
156+
`);
157+
} else {
158+
console.log(`
159+
⚠️ Set up a route, custom domain, or workers.dev subdomain to access your Worker.
160+
Add CLOUDFLARE_SUBDOMAIN to your environment variables to set one up automatically.
161+
`);
162+
}
81163
} catch (error) {
82-
console.error('Script Upload failure!');
83-
console.error(error);
164+
console.error('❌ Deployment failed:', error);
165+
exit(1);
84166
}
85167
}
86168

0 commit comments

Comments
 (0)