Skip to main content Inicio Creadores forceinjection domain-driven-design-skills cloudflare-workflows
cloudflare-workflows Build durable workflows with Cloudflare Workflows (GA April 2025). Features step.do, step.sleep, waitForEvent,
Vitest testing, and runs for hours to days with automatic retries and state persistence.
Use when: creating long-running workflows, implementing retry logic, building event-driven processes,
testing workflows with cloudflare:test, coordinating API calls, or troubleshooting NonRetryableError,
I/O context errors, serialization failures.
Keywords: cloudflare workflows, workflows workers, durable execution, workflow step,
WorkflowEntrypoint, step.do, step.sleep, workflow retries, NonRetryableError,
workflow state, wrangler workflows, workflow events, long-running tasks, step.sleepUntil,
step.waitForEvent, workflow bindings, vitest testing, cloudflare:test, introspectWorkflowInstance
Ir a la instalación Skills Marketplace Descubre y explora habilidades de IA creadas por la comunidad.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Copiar promptMostrar detalles del prompt Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/ForceInjection/domain-driven-design-skills --skill cloudflare-workflowsEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Descargar Zip Descargando... Más de este repositorio Conduct deep academic research for philosophy, neuroscience, cognitive science, and theoretical computer science (computability, complexity, AI theory, logic). Use when user asks to: research academic topics, find scholarly papers, conduct literature reviews, analyze citations, synthesize research findings, explore philosophical arguments, investigate consciousness/cognition, study computability/decidability/Turing machines, or analyze academic debates. Triggers on: 'research papers', 'literature review', 'academic sources', 'scholarly articles', 'philosophy of mind', 'computability theory', 'neuroscience studies', 'find papers on', 'what does the research say'.
Create clear action plans with steps, success criteria, and risk awareness. Use before implementing features, making changes, starting projects, or anytime you need a roadmap to success. Triggers on "plan this", "how should we approach", "what's the strategy", "steps to complete", or when facing complex multi-step work.
Add keyboard navigation to a feature using CommandRegistryService. Use when implementing keyboard shortcuts, vim-style navigation, or hotkeys for a page or component.
Explorador de archivos
2 archivos Ocupaciones relacionadas SOC
Basado en la clasificación ocupacional SOC
name cloudflare-workflows description Build durable workflows with Cloudflare Workflows (GA April 2025). Features step.do, step.sleep, waitForEvent,
Vitest testing, and runs for hours to days with automatic retries and state persistence.
Use when: creating long-running workflows, implementing retry logic, building event-driven processes,
testing workflows with cloudflare:test, coordinating API calls, or troubleshooting NonRetryableError,
I/O context errors, serialization failures.
Keywords: cloudflare workflows, workflows workers, durable execution, workflow step,
WorkflowEntrypoint, step.do, step.sleep, workflow retries, NonRetryableError,
workflow state, wrangler workflows, workflow events, long-running tasks, step.sleepUntil,
step.waitForEvent, workflow bindings, vitest testing, cloudflare:test, introspectWorkflowInstance
Cloudflare Workflows
Status : Production Ready ✅ (GA since April 2025)
Last Updated : 2025-11-25
Dependencies : cloudflare-worker-base (for Worker setup)
Latest Versions : wrangler@4.50.0, @cloudflare/workers-types@4.20251121.0
Recent Updates (2025) :
April 2025 : Workflows GA release - waitForEvent API, Vitest testing, CPU time metrics, 4,500 concurrent instances
October 2025 : Instance creation rate 10x faster (100/sec), concurrency increased to 10,000
2025 Limits : Max steps 1,024, state persistence 1MB/step (100MB-1GB per instance), event payloads 1MB, CPU time 5 min max
Testing : cloudflare:test module with introspectWorkflowInstance, disableSleeps, mockStepResult, mockEvent modifiers
Platform : Waiting instances don't count toward concurrency, retention 3-30 days, subrequests 50-1,000
Quick Start (5 Minutes)
npm create cloudflare@latest my-workflow -- --template cloudflare/workflows-starter --git --deploy
my-workflow
{
: ,
: ,
: ,
: [{
: ,
: ,
:
}]
}
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from ;
class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const result = await step.do( , async () => { /* work */ });
await step.sleep( , );
await step.do( , async () => { /* more work */ });
}
}
npm run deploy
npx wrangler workflows instances list my-workflow
false
cd
"name"
"my-workflow"
"main"
"src/index.ts"
"compatibility_date"
"2025-11-25"
"workflows"
"name"
"my-workflow"
"binding"
"MY_WORKFLOW"
"class_name"
"MyWorkflow"
'cloudflare:workers'
export
'process'
'wait'
'1 hour'
'continue'
CRITICAL : Extends WorkflowEntrypoint, implements run() with step methods, bindings in wrangler.jsonc
Step Methods
step.do() - Execute Work step.do <T>(name : string , config ?: WorkflowStepConfig , callback : () => Promise <T>): Promise <T>
name - Step name (for observability)
config (optional) - Retry configuration (retries, timeout, backoff)
callback - Async function that does the work
Returns: Value from callback (must be serializable)
const result = await step.do ('call API' , { retries : { limit : 10 , delay : '10s' , backoff : 'exponential' }, timeout : '5 min' }, async () => {
return await fetch ('https://api.example.com/data' ).then (r => r.json ());
});
CRITICAL - Serialization:
✅ Allowed: string, number, boolean, Array, Object, null
❌ Forbidden: Function, Symbol, circular references, undefined
Throws error if return value isn't JSON serializable
step.sleep() - Relative Sleep step.sleep (name : string , duration : WorkflowDuration ): Promise <void >
name - Step name
duration - Number (ms) or string: "second", "minute", "hour", "day", "week", "month", "year" (plural forms accepted)
await step.sleep ('wait 5 minutes' , '5 minutes' );
await step.sleep ('wait 1 hour' , '1 hour' );
await step.sleep ('wait 2 days' , '2 days' );
await step.sleep ('wait 30 seconds' , 30000 );
Note: Resuming workflows take priority over new instances. Sleeps don't count toward step limits.
step.sleepUntil() - Sleep to Specific Date step.sleepUntil (name : string , timestamp : Date | number ): Promise <void >
name - Step name
timestamp - Date object or UNIX timestamp (milliseconds)
await step.sleepUntil ('wait for launch' , new Date ('2025-12-25T00:00:00Z' ));
await step.sleepUntil ('wait until time' , Date .parse ('24 Oct 2024 13:00:00 UTC' ));
step.waitForEvent() - Wait for External Event (GA April 2025) step.waitForEvent <T>(name : string , options : { type : string ; timeout ?: string | number }): Promise <T>
name - Step name
options.type - Event type to match
options.timeout (optional) - Max wait time (default: 24 hours, max: 30 days)
Returns: Event payload sent via instance.sendEvent()
export class PaymentWorkflow extends WorkflowEntrypoint <Env , Params > {
async run (event : WorkflowEvent <Params >, step : WorkflowStep ) {
await step.do ('create payment' , async () => { });
const webhookData = await step.waitForEvent <StripeWebhook >(
'wait for payment confirmation' ,
{ type : 'stripe-webhook' , timeout : '1 hour' }
);
if (webhookData.status === 'succeeded' ) {
await step.do ('fulfill order' , async () => { });
}
}
}
export default {
async fetch (req : Request , env : Env ): Promise <Response > {
if (req.url .includes ('/webhook/stripe' )) {
const instance = await env.PAYMENT_WORKFLOW .get (instanceId);
await instance.sendEvent ({ type : 'stripe-webhook' , payload : await req.json () });
return new Response ('OK' );
}
}
};
try {
const event = await step.waitForEvent ('wait for user' , { type : 'user-submitted' , timeout : '10 minutes' });
} catch (error) {
await step.do ('send reminder' , async () => { });
}
WorkflowStepConfig interface WorkflowStepConfig {
retries ?: {
limit : number ;
delay : string | number ;
backoff ?: 'constant' | 'linear' | 'exponential' ;
};
timeout ?: string | number ;
}
Default: { retries: { limit: 5, delay: 10000, backoff: 'exponential' }, timeout: '10 minutes' }
{ retries : { limit : 3 , delay : '30 seconds' , backoff : 'constant' } }
{ retries : { limit : 5 , delay : '1 minute' , backoff : 'linear' } }
{ retries : { limit : 10 , delay : '10 seconds' , backoff : 'exponential' }, timeout : '5 minutes' }
{ retries : { limit : Infinity , delay : '1 minute' , backoff : 'exponential' } }
{ retries : { limit : 0 } }
Error Handling
NonRetryableError Force workflow to fail immediately without retrying:
import { WorkflowEntrypoint , WorkflowStep , WorkflowEvent } from 'cloudflare:workers' ;
import { NonRetryableError } from 'cloudflare:workflows' ;
export class MyWorkflow extends WorkflowEntrypoint <Env , Params > {
async run (event : WorkflowEvent <Params >, step : WorkflowStep ) {
await step.do ('validate input' , async () => {
if (!event.payload .userId ) {
throw new NonRetryableError ('userId is required' );
}
const user = await this .env .DB .prepare (
'SELECT * FROM users WHERE id = ?'
).bind (event.payload .userId ).first ();
if (!user) {
throw new NonRetryableError ('User not found' );
}
return user;
});
}
}
When to use NonRetryableError:
✅ Authentication/authorization failures
✅ Invalid input that won't change
✅ Resource doesn't exist (404)
✅ Validation errors
❌ Network failures (should retry)
❌ Rate limits (should retry with backoff)
❌ Temporary service outages (should retry)
Catch Errors to Continue Workflow Prevent workflow failure by catching optional step errors:
export class MyWorkflow extends WorkflowEntrypoint <Env , Params > {
async run (event : WorkflowEvent <Params >, step : WorkflowStep ) {
await step.do ('process payment' , async () => { });
try {
await step.do ('send email' , async () => { });
} catch (error) {
await step.do ('log failure' , async () => {
await this .env .DB .prepare ('INSERT INTO failed_emails VALUES (?, ?)' ).bind (event.payload .userId , error.message ).run ();
});
}
await step.do ('update status' , async () => { });
}
}
let result;
try {
result = await step.do ('call primary API' , async () => await callPrimaryAPI ());
} catch {
result = await step.do ('call backup API' , async () => await callBackupAPI ());
}
Triggering Workflows Configure binding (wrangler.jsonc):
{
"workflows" : [ {
"name" : "my-workflow" ,
"binding" : "MY_WORKFLOW" ,
"class_name" : "MyWorkflow" ,
"script_name" : "workflow-worker"
} ]
}
const instance = await env.MY_WORKFLOW .create ({ params : { userId : '123' } });
return Response .json ({ id : instance.id , status : await instance.status () });
const instance = await env.MY_WORKFLOW .get (instanceId);
const status = await instance.status ();
await instance.sendEvent ({ type : 'user-action' , payload : { action : 'approved' } });
await instance.pause ();
await instance.resume ();
await instance.terminate ();
State Persistence Workflows automatically persist state returned from step.do():
Primitives: string, number, boolean, null
Arrays, Objects, Nested structures
Functions, Symbols, circular references, undefined, class instances
const result = await step.do ('fetch data' , async () => ({
users : [{ id : 1 , name : 'Alice' }],
timestamp : Date .now (),
metadata : null
}));
const bad = await step.do ('bad' , async () => ({ data : [1 , 2 , 3 ], transform : (x ) => x * 2 }));
Access State Across Steps:
const userData = await step.do ('fetch user' , async () => ({ id : 123 , email : 'user@example.com' }));
const orderData = await step.do ('create order' , async () => ({ userId : userData.id , orderId : 'ORD-456' }));
await step.do ('send email' , async () => sendEmail ({ to : userData.email , subject : `Order ${orderData.orderId} ` }));
Observability
Built-in Metrics (Enhanced in 2025) Workflows automatically track:
Instance status : queued, running, complete, errored, paused, waiting
Step execution : start/end times, duration, success/failure
Retry history : attempts, errors, delays
Sleep state : when workflow will wake up
Output : return values from steps and run()
CPU time (GA April 2025): Active processing time per instance for billing insights
View Metrics in Dashboard Access via Cloudflare dashboard:
Workers & Pages
Select your workflow
View instances and metrics
Total instances created
Success/error rates
Average execution time
Step-level performance
CPU time consumption (2025 feature)
Programmatic Access const instance = await env.MY_WORKFLOW .get (instanceId);
const status = await instance.status ();
console .log (status);
CPU Time Configuration (2025):
{ "limits" : { "cpu_ms" : 300000 } }
Limits (Updated 2025) Feature Workers Free Workers Paid Max steps per workflow 1,024 1,024 Max state per step 1 MiB 1 MiB Max state per instance 100 MB 1 GB Max event payload size 1 MiB 1 MiB Max sleep/sleepUntil duration 365 days 365 days Max waitForEvent timeout 365 days 365 days CPU time per step 10 ms 30 sec (default), 5 min (max) Duration (wall clock) per step Unlimited Unlimited Max workflow executions 100,000/day Unlimited Concurrent instances 25 10,000 (Oct 2025, up from 4,500) Instance creation rate 100/second 100/second (Oct 2025, 10x faster) Max queued instances 100,000 1,000,000 Max subrequests per instance 50/request 1,000/request Retention (completed state) 3 days 30 days Max Workflow name length 64 chars 64 chars Max instance ID length 100 chars 100 chars
step.sleep() and step.sleepUntil() do NOT count toward 1,024 step limit
Waiting instances (sleeping, retrying, or waiting for events) do NOT count toward concurrency limits
Instance creation rate increased 10x (October 2025): 100 per 10 seconds → 100 per second
Max concurrency increased (October 2025): 4,500 → 10,000 concurrent instances
State persistence limits increased (2025): 128 KB → 1 MiB per step, 100 MB - 1 GB per instance
Event payload size increased (2025): 128 KB → 1 MiB
CPU time configurable via wrangler.jsonc: { "limits": { "cpu_ms": 300000 } } (5 min max)
Pricing Requires Workers Paid plan ($5/month)
First 10,000,000 step executions/month: FREE
After that: $0.30 per million step executions
What counts as a step execution:
Each step.do() call
Each retry of a step
step.sleep(), step.sleepUntil(), step.waitForEvent() do NOT count
Workflow with 5 steps, no retries: 5 step executions
Workflow with 3 steps, 1 step retries 2 times: 5 step executions (3 + 2)
10M simple workflows/month (5 steps each): ((50M - 10M) / 1M) × $0.30 = $12/month
Troubleshooting
Issue: "Cannot perform I/O on behalf of a different request" Cause: Trying to use I/O objects created in one request context from another request handler
Solution: Always perform I/O within step.do() callbacks
const response = await fetch ('https://api.example.com/data' );
const data = await response.json ();
await step.do ('use data' , async () => {
return data;
});
const data = await step.do ('fetch data' , async () => {
const response = await fetch ('https://api.example.com/data' );
return await response.json ();
});
Issue: NonRetryableError behaves differently in dev vs production Known Issue: Throwing NonRetryableError with empty message in dev mode causes retries, but works correctly in production
Workaround: Always provide a message to NonRetryableError
throw new NonRetryableError ();
throw new NonRetryableError ('User not found' );
Vitest Testing (GA April 2025) Workflows support full testing integration via cloudflare:test module.
Setup npm install -D vitest@latest @cloudflare/vitest-pool-workers@latest
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' ;
export default defineWorkersConfig ({ test : { poolOptions : { workers : { miniflare : { bindings : { MY_WORKFLOW : { scriptName : 'workflow' } } } } } } });
Introspection API import { env, introspectWorkflowInstance } from 'cloudflare:test' ;
it ('should complete workflow' , async () => {
const instance = await introspectWorkflowInstance (env.MY_WORKFLOW , 'test-123' );
try {
await instance.modify (async (m) => {
await m.disableSleeps ();
await m.mockStepResult ({ name : 'fetch data' }, { users : [{ id : 1 }] });
await m.mockEvent ({ type : 'approval' , payload : { approved : true } });
await m.mockStepError ({ name : 'call API' }, new Error ('Network timeout' ), 1 );
});
await env.MY_WORKFLOW .create ({ id : 'test-123' });
await expect (instance.waitForStatus ('complete' )).resolves .not .toThrow ();
} finally {
await instance.dispose ();
}
});
Test Modifiers
disableSleeps(steps?) - Skip sleeps instantly
mockStepResult(step, result) - Mock step.do() result
mockStepError(step, error, times?) - Force step.do() to throw
mockEvent(event) - Send mock event to step.waitForEvent()
forceStepTimeout(step, times?) - Force step.do() timeout
forceEventTimeout(step) - Force step.waitForEvent() timeout
Related Documentation