Skip to Content
ExamplesWorkflowsWorkflow Variables

Data Mapping with Workflow Variables ✅

This example demonstrates how to use workflow variables to map data between steps in a Kastrax workflow.

Use Case: User Registration Process ✅

In this example, we’ll build a simple user registration workflow that:

  1. Validates user input
  2. Formats the user data
  3. Creates a user profile

Implementation ✅

src/kastrax/workflows/user-registration.ts
import { Step, Workflow } from "@kastrax/core/workflows"; import { z } from "zod"; // Define our schemas for better type safety const userInputSchema = z.object({ email: z.string().email(), name: z.string(), age: z.number().min(18), }); const validatedDataSchema = z.object({ isValid: z.boolean(), validatedData: z.object({ email: z.string(), name: z.string(), age: z.number(), }), }); const formattedDataSchema = z.object({ userId: z.string(), formattedData: z.object({ email: z.string(), displayName: z.string(), ageGroup: z.string(), }), }); const profileSchema = z.object({ profile: z.object({ id: z.string(), email: z.string(), displayName: z.string(), ageGroup: z.string(), createdAt: z.string(), }), }); // Define the workflow const registrationWorkflow = new Workflow({ name: "user-registration", triggerSchema: userInputSchema, }); // Step 1: Validate user input const validateInput = new Step({ id: "validateInput", inputSchema: userInputSchema, outputSchema: validatedDataSchema, execute: async ({ context }) => { const { email, name, age } = context; // Simple validation logic const isValid = email.includes('@') && name.length > 0 && age >= 18; return { isValid, validatedData: { email: email.toLowerCase().trim(), name, age, }, }; }, }); // Step 2: Format user data const formatUserData = new Step({ id: "formatUserData", inputSchema: z.object({ validatedData: z.object({ email: z.string(), name: z.string(), age: z.number(), }), }), outputSchema: formattedDataSchema, execute: async ({ context }) => { const { validatedData } = context; // Generate a simple user ID const userId = `user_${Math.floor(Math.random() * 10000)}`; // Format the data const ageGroup = validatedData.age < 30 ? "young-adult" : "adult"; return { userId, formattedData: { email: validatedData.email, displayName: validatedData.name, ageGroup, }, }; }, }); // Step 3: Create user profile const createUserProfile = new Step({ id: "createUserProfile", inputSchema: z.object({ userId: z.string(), formattedData: z.object({ email: z.string(), displayName: z.string(), ageGroup: z.string(), }), }), outputSchema: profileSchema, execute: async ({ context }) => { const { userId, formattedData } = context; // In a real app, you would save to a database here return { profile: { id: userId, ...formattedData, createdAt: new Date().toISOString(), }, }; }, }); // Build the workflow with variable mappings registrationWorkflow // First step gets data from the trigger .step(validateInput, { variables: { email: { step: 'trigger', path: 'email' }, name: { step: 'trigger', path: 'name' }, age: { step: 'trigger', path: 'age' }, } }) // Format user data with validated data from previous step .then(formatUserData, { variables: { validatedData: { step: validateInput, path: 'validatedData' }, }, when: { ref: { step: validateInput, path: 'isValid' }, query: { $eq: true }, }, }) // Create profile with data from the format step .then(createUserProfile, { variables: { userId: { step: formatUserData, path: 'userId' }, formattedData: { step: formatUserData, path: 'formattedData' }, }, }) .commit(); export default registrationWorkflow;

How to Use This Example ✅

  1. Create the file as shown above
  2. Register the workflow in your Kastrax instance
  3. Execute the workflow:
curl --location 'http://localhost:4111/api/workflows/user-registration/start-async' \ --header 'Content-Type: application/json' \ --data '{ "email": "user@example.com", "name": "John Doe", "age": 25 }'

Key Takeaways ✅

This example demonstrates several important concepts about workflow variables:

  1. Data Mapping: Variables map data from one step to another, creating a clear data flow.

  2. Path Access: The path property specifies which part of a step’s output to use.

  3. Conditional Execution: The when property allows steps to execute conditionally based on previous step outputs.

  4. Type Safety: Each step defines input and output schemas for type safety, ensuring that the data passed between steps is properly typed.

  5. Explicit Data Dependencies: By defining input schemas and using variable mappings, the data dependencies between steps are made explicit and clear.

For more information on workflow variables, see the Workflow Variables documentation.

Last updated on