Registreer om nieuwe berichten te ontvangen:

Cloudflare Workflows is nu GA: productieklare, duurzame uitvoering

2025-04-07

5 minuten leestijd

Bètaversies zijn handig voor feedback en herhaling, maar uiteindelijk wil niet iedereen proefkonijn zijn of de scherpe kantjes die bètasoftware met zich meebrengt, tolereren. Soms heb je dat grote, glimmende label 'Algemeen beschikbaar' (of blogbericht) nodig, en nu is het de beurt aan Workflows.

Workflows, onze serverless , duurzame uitvoeringsengine waarmee u langlopende, meerstapstoepassingen (sommigen noemen ze 'stapfuncties') op Workers kunt bouwen, is nu GA.

Kortom, dat betekent dat het klaar is voor productie , maar het betekent niet dat Workflows zal verstarren. We blijven Workflows opschalen (inclusief meer gelijktijdige instanties), bieden nieuwe mogelijkheden (zoals de nieuwe waitForEvent API) en maken het eenvoudiger om AI-agenten te bouwen met onze Agents SDK en Workflows.

Als u de voorkeur geeft aan code in plaats van proza, kunt u snel het Workflows starter-project installeren en met één enkele opdracht de code en de API gaan verkennen:

npm create cloudflare@latest workflows-starter -- 
--template="cloudflare/workflows-starter"

Hoe werken Workflows? Wat kan ik ermee bouwen? Hoe denk ik over het bouwen van AI-agenten met Workflows en de Agents SDK? Lees dan verder.

Bouwen met Workflows

Workflows is een duurzame uitvoeringsengine die is gebouwd op Cloudflare Workers en waarmee u veerkrachtige, meerstapstoepassingen kunt bouwen.

Workflows implementeert in de kern een stapsgewijze architectuur waarbij elke stap in uw toepassing onafhankelijk opnieuw kan worden uitgevoerd en de status tussen de stappen automatisch wordt behouden. Dit betekent dat zelfs als een stap mislukt vanwege een tijdelijke fout of een netwerkprobleem, Workflows alleen die stap opnieuw kunnen proberen zonder dat u de hele applicatie opnieuw hoeft te starten.

Wanneer u een workflow definieert, verdeelt u uw toepassing in logische stappen.

  • Elke stap kan code uitvoeren (step.do), Zet uw workflow in slaapstand (step.sleep of step.sleepUntil), of wacht op een gebeurtenis (step.waitForEvent).

  • Terwijl uw workflow wordt uitgevoerd, wordt automatisch de status opgeslagen die bij elke stap wordt geretourneerd. Zo weet u zeker dat uw toepassing precies kan doorgaan waar u was gebleven, zelfs na storingen of een periode van slaapstand. 

  • Dit duurzame uitvoeringsmodel is vooral krachtig voor toepassingen die coördinatie tussen meerdere systemen vereisen, gegevens in volgorde verwerken of langdurige taken moeten uitvoeren die minuten, uren of zelfs dagen kunnen duren.

Workflows zijn vooral handig bij het verwerken van complexe bedrijfsprocessen waar traditionele stateless functies moeite mee hebben.

Een workflow voor het verwerken van e-commerce commerceorders kan bijvoorbeeld de voorraad controleren, een betaalmethode belasten, een e-mailbevestiging verzenden en een database bijwerken. Dit alles gebeurt als afzonderlijke stappen. Als de betalingsverwerking mislukt vanwege een tijdelijke storing, proberen Workflows automatisch die stap opnieuw uit zodra de betalingsservice weer beschikbaar is. Dit gebeurt zonder de inventariscontrole te dupliceren of het hele proces opnieuw te starten. 

Hieronder ziet u hoe dit werkt: elke aanroep van een service kan worden gemodelleerd als een stap, die onafhankelijk opnieuw kan worden geprobeerd en, indien nodig, vanaf die stap kan worden hersteld:

import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';

// The params we expect when triggering this Workflow
type OrderParams = {
	orderId: string;
	customerId: string;
	items: Array<{ productId: string; quantity: number }>;
	paymentMethod: {
		type: string;
		id: string;
	};
};

// Our Workflow definition
export class OrderProcessingWorkflow extends WorkflowEntrypoint<Env, OrderParams> {
	async run(event: WorkflowEvent<OrderParams>, step: WorkflowStep) {
		// Step 1: Check inventory
		const inventoryResult = await step.do('check-inventory', async () => {
			console.log(`Checking inventory for order ${event.payload.orderId}`);

			// Mock: In a real workflow, you'd query your inventory system
			const inventoryCheck = await this.env.INVENTORY_SERVICE.checkAvailability(event.payload.items);

			// Return inventory status as state for the next step
			return {
				inStock: true,
				reservationId: 'inv-123456',
				itemsChecked: event.payload.items.length,
			};
		});

		// Exit workflow if items aren't in stock
		if (!inventoryResult.inStock) {
			return { status: 'failed', reason: 'out-of-stock' };
		}

		// Step 2: Process payment
		// Configure specific retry logic for payment processing
		const paymentResult = await step.do(
			'process-payment',
			{
				retries: {
					limit: 3,
					delay: '30 seconds',
					backoff: 'exponential',
				},
				timeout: '2 minutes',
			},
			async () => {
				console.log(`Processing payment for order ${event.payload.orderId}`);

				// Mock: In a real workflow, you'd call your payment processor
				const paymentResponse = await this.env.PAYMENT_SERVICE.processPayment({
					customerId: event.payload.customerId,
					orderId: event.payload.orderId,
					amount: calculateTotal(event.payload.items),
					paymentMethodId: event.payload.paymentMethod.id,
				});

				// If payment failed, throw an error that will trigger retry logic
				if (paymentResponse.status !== 'success') {
					throw new Error(`Payment failed: ${paymentResponse.message}`);
				}

				// Return payment info as state for the next step
				return {
					transactionId: 'txn-789012',
					amount: 129.99,
					timestamp: new Date().toISOString(),
				};
			},
		);

		// Step 3: Send email confirmation
		await step.do('send-confirmation-email', async () => {
			console.log(`Sending confirmation email for order ${event.payload.orderId}`);
			console.log(`Including payment confirmation ${paymentResult.transactionId}`);
			return await this.env.EMAIL_SERVICE.sendOrderConfirmation({ ... })
		});

		// Step 4: Update database
		const dbResult = await step.do('update-database', async () => {
			console.log(`Updating database for order ${event.payload.orderId}`);
			await this.updateOrderStatus(...)

			return { dbUpdated: true };
		});

		// Return final workflow state
		return {
			orderId: event.payload.orderId,
			processedAt: new Date().toISOString(),
		};
	}
}

Deze combinatie van duurzaamheid, automatische herhalingen en statuspersistentie maakt Workflows ideaal voor het bouwen van betrouwbare gedistribueerde applicaties die daadwerkelijke fouten probleemloos kunnen verwerken.

Mensen blijven betrokken

Workflows bestaan alleen uit code en dat maakt ze extreem krachtig: u kunt dynamisch en on-the-fly stappen definiëren, voorwaardelijk vertakken en API-aanroep maken naar elk systeem dat u nodig hebt. Maar soms heb je ook een Workflow nodig die wacht tot er iets in de echte wereld gebeurt.

Bijvoorbeeld:

  • Goedkeuring van een mens voor vooruitgang.

  • Een binnenkomende webhook, bijvoorbeeld van een Stripe-betaling of een GitHub-gebeurtenis.

  • Een statuswijziging, zoals het uploaden van een bestand naar R2, activeert een gebeurtenismelding en pusht vervolgens een verwijzing naar het bestand naar de workflow, zodat deze het bestand kan verwerken (of via een AI-model kan uitvoeren).

Met de nieuwe waitForEvent API in Workflows kunt u precies dat doen: 

let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }) 

Vervolgens kunt u een gebeurtenis naar een specifiek exemplaar sturen vanuit elke externe service die een HTTP-verzoek kan doen:

curl -d '{"transaction":"complete","id":"1234-6789"}' \
  -H "Authorization: Bearer ${CF_TOKEN}" \
\ "https://api.cloudflare.com/client/v4/accounts/{account_id}/workflows/{workflow_name}/instances/{instance_id}/events/{event_type}"

… of via de Workers API binnen een Worker zelf:

interface Env {
  MY_WORKFLOW: Workflow;
}

interface Payload {
  transaction: string;
  id: string;
}

export default {
  async fetch(req: Request, env: Env) {
    const instanceId = new URL(req.url).searchParams.get("instanceId")
    const webhookPayload = await req.json<Payload>()

    let instance = await env.MY_WORKFLOW.get(instanceId);
    // Send our event, with `type` matching the event type defined in
    // our step.waitForEvent call
    await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})
    
    return Response.json({
      status: await instance.status(),
    });
  },
};

U kunt zelfs wachten op meerdere gebeurtenissen met behulp van de typeparameter en/of meerdere gebeurtenissen racen met behulp van Promise.race om door te gaan, afhankelijk van welke gebeurtenis het eerst is ontvangen:

export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
	async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
		let state = await step.do("get some data", () => { /* step call here */ })
		// Race the events, resolving the Promise based on which event
// we receive first
		let value = Promise.race([
step.waitForEvent("payment success", { type: "payment-success-webhook", timeout: "4 hours" ),
step.waitForEvent("payment failure", { type: "payment-failure-webhook", timeout: "4 hours" ),
])
// Continue on based on the value and event received
	}
}

Om waitForEvent wat gedetailleerder te visualiseren, gaan we ervan uit dat we een workflow hebben die wordt geactiveerd door een codereviewagent die een GitHub-repository in de gaten houdt.

Omdat we niet op gebeurtenissen kunnen wachten, kan onze workflow niet zomaar menselijke goedkeuring krijgen om suggesties terug te schrijven (of zelfs een eigen PR in te dienen). Het zou potentieel kunnen peilen naar een bijgewerkte status, maar dat betekent dat we step.sleep voor willekeurige tijdsperioden moeten aanroepen, een opslagservice moeten peilen naar een bijgewerkte waarde en dit moeten herhalen als de waarde er niet is. Dat is een hoop code en ruimte voor fouten:

Zonder waitForEvent is het moeilijker om gegevens te verzenden naar een Workflow-instantie die actief is

Als we datzelfde voorbeeld zouden aanpassen om de nieuwe waitForEvent API te integreren, zouden we deze kunnen gebruiken om te wachten op menselijke goedkeuring voordat we een muterende wijziging doorvoeren: 

WaitForEvent toevoegen aan onze codebeoordelingsworkflow, zodat er expliciet om goedkeuring kan worden gevraagd.

Je kunt je zelfs voorstellen dat een AI-agent zelf namens een mens iets verstuurt en/of handelt: waitForEvent biedt een manier voor een workflow om iets in de wereld op te halen en te pauzeren voordat deze wordt gewijzigd (of niet).

Belangrijk is dat u waitForEvent net als elke andere stap in Workflows kunt aanroepen: u kunt het voorwaardelijk, en/of meerdere keren en/of in een lus aanroepen. Workflows zijn gewoon Workers: u beschikt over de volledige kracht van een programmeertaal en wordt niet beperkt door een domeinspecifieke taal (DSL) of configuratietaal.

Prijzen

Goed nieuws: er is niet veel veranderd sinds onze oorspronkelijke bèta-aankondiging! We voegen opslagprijzen toe voor de status die door uw Workflows wordt opgeslagen en behouden onze op CPU gebaseerde en op aanvraag (aanroep) gebaseerde prijzen als volgt:

Eenheid

Workers Free

Workers Paid

CPU-tijd (ms)

10 ms per workflow

30 miljoen CPU-milliseconden per maand inbegrepen

+$ 0,02 per extra miljoen CPU-milliseconden

Verzoeken

100.000 Workflow-aanroepen per dag (gedeeld met Workers)

10 miljoen inbegrepen per maand

+$ 0,30 per extra miljoen

Opslag (GB)

1GB

1 GB inbegrepen per maand + $ 0,20/ GB-maand

Omdat de opslagprijzen nieuw zijn, factureren we pas vanaf 15 september 2025 actief voor opslag. Gebruikers boven de limiet van 1 GB worden door ons op de hoogte gesteld voordat we kosten voor opslag in rekening brengen. Standaard verloopt de opslagstatus van Workflows na drie (3) dagen (Free abonnement) of dertig (30) dagen (betaald abonnement).

Als u zich afvraagt wat hier wordt bedoeld met "CPU-tijd", dan is dit de tijd dat uw workflow actief gebruikmaakt van computerbronnen. Hierbij is de tijd die wordt besteed aan het wachten op API-aanroep, het redeneren van LLM's of andere I/O (zoals het schrijven naar een database) niet meegerekend. Dat lijkt misschien een kleinigheid, maar in de praktijk kan het aardig oplopen: de meeste applicaties hebben maar een paar milliseconden CPU-tijd en meerdere seconden wall-tijd: een API of twee die 100 tot 250 ms nodig heeft om te reageren, tikt flink aan!

Facturering voor CPU, niet voor de tijd die wordt besteed wanneer een workflow inactief is of wacht.

Vooral workflow engines besteden vaak veel tijd aan wachten: het lezen van gegevens uit object storage (zoals Cloudflare R2), het aanroepen van API's of LLM's van derden zoals o3-mini of Claude 3.7 en zelfs het raadplegen van databases zoals D1, Postgres of MySQL. Met Workflows, net als met Workers, betaalt u niet voor de tijd dat uw applicatie wacht.

Begin met bouwen

U hebt dus een goed beeld van Workflows en hoe deze werken, en u wilt aan de slag. Wat nu?

  1. Bezoek de Workflows-documentatie om te leren hoe het werkt, de Workflows API te begrijpen en best practices te vinden

  2. Bekijk de code in het starterproject

  3. En tot slot kunt u de starter met een paar klikken implementeren in uw eigen Cloudflare-account:

Implementeren naar Cloudflare

We beschermen complete zakelijke netwerken, helpen klanten toepassingen op internet-schaal efficiënt te bouwen, versnellen websites en internettoepassingen, weren DDoS-aanvallen af, houden hackers op afstand, en kunnen je helpen bij je reis richting Zero Trust.

Bezoek 1.1.1.1 vanaf elk apparaat om aan de slag te gaan met onze gratis app die je internet sneller en veiliger maakt.

Als je meer wilt weten over onze missie om een beter internet te helpen opbouwen, klik dan hier. Als je op zoek bent naar een nieuwe carrièrerichting, bekijk dan onze openstaande vacatures.
Developer WeekCloudflare WorkersWorkflowsOntwikkelaarsplatform

Volg ons op X

Sid Chatterjee|@chatsidhartha
Matt Silverlock|@elithrar
Cloudflare|@cloudflare

Gerelateerde berichten