← Cloudflare Workflows / workflows / examples
Экспорт и сохранение базы данных D1
В этом примере мы реализуем Workflow, который запускается по расписанию с помощью schedules поле в привязке Workflow. Этот Workflow запускает резервное копирование базы данных D1 через REST API, а затем сохраняет SQL-дамп в R2 bucket.
При запуске 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 как secret с правом на экспорт целевой базы данных D1.
Вот конфигурационный файл 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-28",
"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-28"
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.
Запланированные экземпляры включают соответствующее cron-выражение и время запланированного запуска в event.schedule.
При настройке расписаний Workflow используйте последнюю версию Wrangler. Если локальная схема Wrangler не распознаёт schedules ещё, обновите Wrangler перед развёртыванием.