diff --git a/mint.json b/mint.json index ca985b26..98a2362e 100644 --- a/mint.json +++ b/mint.json @@ -1438,6 +1438,7 @@ "workflow/howto/monitor", "workflow/howto/security", "workflow/howto/parallel-runs", + "workflow/howto/invoke", "workflow/howto/schedule", "workflow/howto/changes", "workflow/howto/local-development", @@ -1467,6 +1468,7 @@ "pages": [ "workflow/examples/allInOne", "workflow/examples/waitForEvent", + "workflow/examples/invoke", "workflow/examples/customerOnboarding", "workflow/examples/authWebhook", "workflow/examples/paymentRetry", diff --git a/workflow/basics/context.mdx b/workflow/basics/context.mdx index c1d596f0..6fd05c3a 100644 --- a/workflow/basics/context.mdx +++ b/workflow/basics/context.mdx @@ -525,6 +525,27 @@ More details about the `Waiter` object: +### context.invoke + +Triggers another workflow run and waits for its completion. +The workflow continues when the invoked workflow finishes, whether successfully, with failure, or is canceled. +The response is the data returned by the invoked workflow, if any is returned. + +```ts +export const { POST } = serve<{ topic: string }>(async (context) => { + + const {response, isFailed, isCanceled} = await context.invoke("invoke another workflow", { + workflow: anotherWorkflowObject, + body: "test", + header: {...} + }); + +}) +``` + +Only workflows that served together can invoke each other. To learn more about how you can serve multiple workflows together, +checkout [Invoke other workflows](/workflow/howto/invoke) guide. + ### context.cancel diff --git a/workflow/howto/invoke.mdx b/workflow/howto/invoke.mdx new file mode 100644 index 00000000..a7ba5655 --- /dev/null +++ b/workflow/howto/invoke.mdx @@ -0,0 +1,57 @@ +--- +title: "Invoke other workflows" +--- + +You can start another workflow run inside a workflow and await its execution to complete. +This allows to orchestrate multiple workflows together without external syncranization. + +```typescript +const { response, isFailed, isCanceled } = await context.invoke("analyze-content", { + workflow: analyzeContent, + body: {/**/}, + headers: {/**/} +}) +``` + +As you may notice, we pass a workflow object to the invoke function. This object is initialized using the `createWorkflow()` function. + +Normally, workflows are initialized with `serve()` method and exposed as a standalone route in your application. +However, when workflows are defined separately using `serve()`, type safety is not guaranteed. + +To ensure type safety for request and response when invoking other workflows, we introduced the `createWorkflow()` function. +`createWorkflow()` returns a referenceable workflow object that can be used in `context.invoke()`. + +```typescript +import { WorkflowContext } from "@upstash/workflow"; +import { create, serve } from "@upstash/workflow/nextjs"; + +// 👇 anotherWorkflow: (string) => number +const anotherWorkflow = createWorkflow(async (context: WorkflowContext) => { + + await context.run("step-1", () => { + // Execute any business logic... + }), + + return {message: "This is the data returned by workflow"} +}); + +const someWorkflow = createWorkflow(async (context) => { + // 👇 type(response) = {message: string} + const { response } = await context.invoke("invoke ", { + workflow: anotherWorkflow, + // 👇 type(body) = string + body: "user-1" + }), +}); + +export const { POST } = serve([someWorkflow, anotherWorkflow]) +``` + +`createWorkflow()` does not expose your workflow like `serve()`, it just initializes the workflow object. +To be able to use the workflow, they must be exposed with `serve([])` function. + + +If workflows are going to be invoke each other, they must be exposed in the same `serve` endpoint. + +If you pass a workflow object which is initialized with `createWorkflow()` but not exposed inside the same `serve`, you will get a runtime error. +