在本示例中,我们实现使用 Workflow 绑定上的 schedules 字段按计划运行的 Workflow。该 Workflow 使用 REST API 为 D1 数据库启动备份,然后将 SQL 转储存储到 R2 存储桶。
Workflow 触发后,它调用 REST API 为特定数据库启动导出任务。然后再次调用同一端点检查备份任务是否就绪以及 SQL 转储是否可供下载。
如本示例所示,Workflows 处理响应和故障,从而减轻开发者的负担。Workflows 重试以下步骤:
- API 调用直到获得成功响应
- 从提供的 URL 获取备份
- 将文件保存到 R2
Workflow 可以运行直到备份文件就绪,处理所有可能的情况直到完成。
本示例提供了备份 D1 数据库的简化步骤,帮助你了解 Workflows 的可能性。在每个步骤中,它使用默认休眠和重试配置。在真实场景中,可能需要更多步骤和额外逻辑。
import {
WorkflowEntrypoint,
WorkflowStep,
WorkflowEvent,
} from "cloudflare:workers";
// We are using R2 to store the D1 backup
type Env = {
BACKUP_WORKFLOW: Workflow;
D1_REST_API_TOKEN: string;
BACKUP_BUCKET: R2Bucket;
ACCOUNT_ID: string;
DATABASE_ID: string;
};
// Workflow logic
export class backupWorkflow extends WorkflowEntrypoint<Env> {
async run(_event: WorkflowEvent<unknown>, step: WorkflowStep) {
const accountId = this.env.ACCOUNT_ID;
const databaseId = this.env.DATABASE_ID;
const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${databaseId}/export`;
const method = "POST";
const headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("Authorization", `Bearer ${this.env.D1_REST_API_TOKEN}`);
const bookmark = await step.do(
`Starting backup for ${databaseId}`,
async () => {
const payload = { output_format: "polling" };
const res = await fetch(url, {
method,
headers,
body: JSON.stringify(payload),
});
const { result } = (await res.json()) as any;
// If we don't get `at_bookmark` we throw to retry the step
if (!result?.at_bookmark) throw new Error("Missing `at_bookmark`");
return result.at_bookmark;
},
);
await step.do("Check backup status and store it on R2", async () => {
const payload = { current_bookmark: bookmark };
const res = await fetch(url, {
method,
headers,
body: JSON.stringify(payload),
});
const { result } = (await res.json()) as any;
// The endpoint sends `signed_url` when the backup is ready to download.
// If we don't get `signed_url` we throw to retry the step.
if (!result?.signed_url) throw new Error("Missing `signed_url`");
const dumpResponse = await fetch(result.signed_url);
if (!dumpResponse.ok) throw new Error("Failed to fetch dump file");
// Finally, stream the file directly to R2
await this.env.BACKUP_BUCKET.put(result.filename, dumpResponse.body);
});
}
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
return new Response("Not found", { status: 404 });
},
};以下是最小 package.json:
{
"devDependencies": {
"wrangler": "^3.99.0"
}
}将 D1_REST_API_TOKEN 创建为具有导出目标 D1 数据库权限的密钥(secret)。
以下是 Wrangler 配置文件:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "backup-d1",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-17",
"compatibility_flags": [
"nodejs_compat"
],
"vars": {
"ACCOUNT_ID": "account-id",
"DATABASE_ID": "database-id"
},
"workflows": [
{
"name": "backup-workflow",
"binding": "BACKUP_WORKFLOW",
"class_name": "backupWorkflow",
"schedules": ["0 0 * * *"]
}
],
"r2_buckets": [
{
"binding": "BACKUP_BUCKET",
"bucket_name": "d1-backups"
}
]
}"$schema" = "./node_modules/wrangler/config-schema.json"
name = "backup-d1"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-17"
compatibility_flags = [ "nodejs_compat" ]
[vars]
ACCOUNT_ID = "account-id"
DATABASE_ID = "database-id"
[[workflows]]
name = "backup-workflow"
binding = "BACKUP_WORKFLOW"
class_name = "backupWorkflow"
schedules = [ "0 0 * * *" ]
[[r2_buckets]]
binding = "BACKUP_BUCKET"
bucket_name = "d1-backups"每次调度运行都会自动创建新的 Workflow 实例。
调度的实例在 event.schedule 上包含匹配的 cron 表达式和调度触发时间。
配置 Workflow 调度时请使用最新 Wrangler 版本。如果本地 Wrangler schema 尚不识别 schedules,请在部署前更新 Wrangler。