跳转到内容
搜索文档

导出并保存 D1 数据库

使用 Workflows 将 D1 数据库导出到 R2 存储

最后更新 查看 MarkdownAgent 设置

在本示例中,我们实现使用 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。

这篇文档对您有帮助吗?