Skip to content
Cloudflare Docs

Infrastructure as Code (IaC)

Uploading and managing a Worker is easy with Wrangler, but sometimes you need to do it more programmatically. You might do this with IaC ("Infrastructure as Code") tools or by calling the Cloudflare API directly. Use cases for the API include build and deploy scripts, CI/CD pipelines, custom dev tools, or testing. We provide API SDK libraries for common languages that make interacting with the API easier, such as cloudflare-typescript or cloudflare-python. For IaC, a common tool is HashiCorp's Terraform where you can use the Cloudflare Provider to create and manage Workers resources.

Here are examples of deploying a Worker with common tools and languages. In particular, they highlight how to upload script content and metadata which is different with each approach. Reference the Upload Worker Module API docs here.

All of these examples need an account id and API token (not Global API key) to work.

Terraform

In this example, you need a local file named my-hello-world-script.mjs with script content similar to the above examples. Replace account_id with your own. Learn more about the Cloudflare Terraform Provider here, and see an example with all the Workers script resource settings here.

terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 5"
}
}
}
resource "cloudflare_workers_script" "my-hello-world-script" {
account_id = "<replace_me>"
script_name = "my-hello-world-script"
main_module = "my-hello-world-script.mjs"
content = trimspace(file("my-hello-world-script.mjs"))
bindings = [{
name = "MESSAGE"
type = "plain_text"
text = "Hello World!"
}]
}

Cloudflare API Libraries

JavaScript/TypeScript

This example uses the cloudflare-typescript library which provides convenient access to the Cloudflare REST API from server-side JavaScript or TypeScript.

#!/usr/bin/env -S npm run tsn -T
/*
* Generate an API token: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/
* (Not Global API Key!)
*
* Find your account id: https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/
*
* Set these environment variables:
* - CLOUDFLARE_API_TOKEN
* - CLOUDFLARE_ACCOUNT_ID
*/
import Cloudflare from "cloudflare";
import { toFile } from "cloudflare/index";
const apiToken = process.env["CLOUDFLARE_API_TOKEN"] ?? "";
if (!apiToken) {
throw new Error("Please set envar CLOUDFLARE_ACCOUNT_ID");
}
const accountID = process.env["CLOUDFLARE_ACCOUNT_ID"] ?? "";
if (!accountID) {
throw new Error("Please set envar CLOUDFLARE_API_TOKEN");
}
const client = new Cloudflare({
apiToken: apiToken,
});
async function main() {
const scriptName = "my-hello-world-script";
const scriptFileName = `${scriptName}.mjs`;
// Workers Scripts prefer Module Syntax
// https://blog.cloudflare.com/workers-javascript-modules/
const scriptContent = `
export default {
async fetch(request, env, ctx) {
return new Response(env.MESSAGE, { status: 200 });
}
};
`;
try {
// https://developers.cloudflare.com/api/resources/workers/subresources/scripts/methods/update/
const script = await client.workers.scripts.update(scriptName, {
account_id: accountID,
// https://developers.cloudflare.com/workers/configuration/multipart-upload-metadata/
metadata: {
main_module: scriptFileName,
bindings: [
{
type: "plain_text",
name: "MESSAGE",
text: "Hello World!",
},
],
},
files: {
// Add main_module file
[scriptFileName]: await toFile(
Buffer.from(scriptContent),
scriptFileName,
{
type: "application/javascript+module",
},
),
// Can add other files, such as more modules or source maps
// [sourceMapFileName]: await toFile(Buffer.from(sourceMapContent), sourceMapFileName, {
// type: 'application/source-map',
// }),
},
});
console.log("Script Upload success!");
console.log(JSON.stringify(script, null, 2));
} catch (error) {
console.error("Script Upload failure!");
console.error(error);
}
}
main();

Python

This example uses the cloudflare-python library.

12 collapsed lines
"""Workers Script Upload Example
Generate an API token: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/
(Not Global API Key!)
Find your account id: https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/
Set these environment variables:
- CLOUDFLARE_API_TOKEN
- CLOUDFLARE_ACCOUNT_ID
"""
import json
import os
from cloudflare import Cloudflare
from cloudflare import BadRequestError
API_TOKEN = os.environ.get("CLOUDFLARE_API_TOKEN")
if API_TOKEN is None:
raise Exception("Please set envar CLOUDFLARE_API_TOKEN")
ACCOUNT_ID = os.environ.get("CLOUDFLARE_ACCOUNT_ID")
if ACCOUNT_ID is None:
raise Exception("Please set envar CLOUDFLARE_ACCOUNT_ID")
client = Cloudflare(api_token=API_TOKEN)
def main() -> None:
script_name = "my-hello-world-script"
script_file_name = f"{script_name}.mjs"
# Workers Scripts prefer Module Syntax
# https://blog.cloudflare.com/workers-javascript-modules/
script_content = """
export default {
async fetch(request, env, ctx) {
return new Response(env.MESSAGE, { status: 200 });
}
};
"""
try:
# https://developers.cloudflare.com/api/resources/workers/subresources/scripts/methods/update/
script = client.workers.scripts.update(
script_name,
account_id=ACCOUNT_ID,
# https://developers.cloudflare.com/workers/configuration/multipart-upload-metadata/
metadata={
"main_module": script_file_name,
"bindings": [
{
"type": "plain_text",
"name": "MESSAGE",
"text": "Hello World!",
}
],
},
files={
# Add main_module file
script_file_name: (script_file_name, bytes(script_content, "utf-8"), "application/javascript+module")
# Can add other files, such as more modules or source maps
# source_map_file_name: (source_map_file_name, bytes(source_map_content, "utf-8"), "application/source-map")
},
)
print('Script Upload success!')
print(json.dumps(script, indent=2))
except BadRequestError as err:
print('Script Upload failure!')
print(err)
if __name__ == "__main__":
main()

Cloudflare REST API

Open a terminal or create a shell script to upload a Worker easily with curl. For this example, replace <account_id> and <api_token> with your own. What's notable about interacting with the Workers Script Upload API directly is that it uses multipart/form-data for uploading metadata, multiple JavaScript modules, source maps, and more. This is abstracted away in Terraform and the API libraries.

Terminal window
curl https://api.cloudflare.com/client/v4/accounts/<account_id>/workers/scripts/my-hello-world-script \
-X PUT \
-H 'Authorization: Bearer <api_token>' \
-F 'metadata={
"main_module": "my-hello-world-script.mjs",
"bindings": [
{
"type": "plain_text",
"name": "MESSAGE",
"text": "Hello World!"
}
],
"compatibility_date": "2025-03-11"
};type=application/json' \
-F 'my-hello-world-script.mjs=@-;filename=my-hello-world-script.mjs;type=application/javascript+module' <<EOF
export default {
async fetch(request, env, ctx) {
return new Response(env.MESSAGE, { status: 200 });
}
};
EOF