Node ReferenceContext & Variables

Context & Variables

Context variables serve as the data bus for your test flows - they’re how you store, transform, and pass data between nodes. Every piece of information that moves through your test must flow through context variables.

Context as Your Data Bus

Context is the central communication system that connects all nodes in your test. Think of it as a shared clipboard that all nodes can read from and write to, following specific scoping rules.

Essential Data Bus Patterns

Pattern 1: API Response → Context → Next Operation

Step 1: API Call

API Node: GET /users/current
Response: { "id": 123, "role": "admin", "permissions": ["read", "write"] }

Step 2: Store in Context (CONTEXT Node)

Set context.currentUserId = response.data.id
Set context.userRole = response.data.role
Set context.canWrite = response.data.permissions contains "write"

Step 3: Use in Next Operation

IF Node condition: context.userRole = "admin"
API Node URL: /admin/users/{{context.currentUserId}}/settings

Pattern 2: Prepare Data → Context → API Request

Step 1: Prepare Data (CONTEXT Node)

Set context.requestData = {
  "name": "Test User",
  "email": "test@example.com",
  "department": environment.testDepartment
}
Set context.timestamp = $now()

Step 2: Use in API Call

API Node: POST /users
Body: {
  "userData": "{{context.requestData}}",
  "createdAt": "{{context.timestamp}}"
}

How Context Works

Hierarchical Scope

  • Variables are visible to child nodes
  • Child nodes can override parent variables
  • Variables flow down the execution tree

Where You Can Modify Context

Context can be modified in four places, each serving different purposes:

When to use: Pre-configure variables before API calls

How: Visual UI with JSONata expressions

CONTEXT Node operations:
├── Set context.userId = 12345
├── Set context.userEmail = "test@example.com"
├── Set context.requestData = { "name": "Test", "active": true }
└── Set context.timestamp = $now()

Best for:

  • Setting up test data before API calls
  • Preparing request payloads
  • Configuring test parameters
  • Simple data transformations with JSONata

When to use: Extract data from API responses

How: Visual UI attached to API nodes, runs after response received

Two modes available:

API Node: GET /users/123
Response: { "id": 123, "name": "John", "role": "admin" }

Context Operations (in UI):
├── Set context.userId = response.data.id
├── Set context.userName = response.data.name
├── Set context.isAdmin = response.data.role = "admin"
└── Set context.userSummary = response.data.name & " (" & response.data.role & ")"

Benefits:

  • ✅ Visual, no coding required
  • ✅ JSONata expressions for transformations
  • ✅ QA-friendly
  • ✅ Easy to maintain

Mode B: After Response Hook (For Complex Logic)

When to use: Complex transformations that JSONata cannot handle

// After Response Hook (script)
async function afterResponse() {
  // Complex data transformation
  $context.processedData = $response.data.items
    .filter((item) => item.status === "active")
    .map((item) => ({
      ...item,
      formatted: $$DataUtils.transform(item),
    }));
 
  // Complex calculations
  $context.statistics = {
    total: $response.data.items.length,
    active: $response.data.items.filter((i) => i.active).length,
    avgValue: $$MathUtils.average($response.data.items.map((i) => i.value)),
  };
}

Best for:

  • Complex data transformations beyond JSONata capabilities
  • Calling global utility functions ($$Utils)
  • Advanced calculations and aggregations
  • When you need full JavaScript power

⚠️ Rule: If you can do it in Context Operations, do it there. Only use hooks when necessary.


3. Before Request Hook (For Dynamic Request Preparation)

When to use: Dynamically modify context right before API call

// Before Request Hook (script)
async function beforeRequest() {
  // Add computed values to context
  $context.requestHash = $$CryptoUtils.generateHash($request.body);
  $context.requestTimestamp = Date.now();
 
  // Set context based on configuration
  if ($context.config.debugMode) {
    $context.debugInfo = {
      url: $request.url,
      method: $request.method,
      timestamp: Date.now(),
    };
  }
}

Best for:

  • Computing values just before request
  • Setting debug/trace information
  • Dynamic configuration based on environment

4. Script Node (For Complex Preparation)

When to use: Complex data preparation that CONTEXT nodes cannot handle

How: Write async function runScript() with access to $context

// Script Node
async function runScript() {
  // Complex data structure preparation
  const rawData = $context.rawOrders;
 
  $context.processedOrders = rawData
    .filter((order) => order.status === "pending")
    .map((order) => {
      const items = order.items.map((item) => ({
        ...item,
        total: item.price * item.quantity,
      }));
 
      const subtotal = items.reduce((sum, item) => sum + item.total, 0);
      const tax = subtotal * 0.2;
 
      return {
        orderId: order.id,
        items,
        subtotal,
        tax,
        total: subtotal + tax,
        summary: `Order ${order.id}: ${items.length} items, $${subtotal + tax}`,
      };
    });
 
  // Set summary statistics
  $context.orderSummary = {
    totalOrders: $context.processedOrders.length,
    totalValue: $context.processedOrders.reduce((sum, o) => sum + o.total, 0),
    avgOrderValue:
      $context.processedOrders.reduce((sum, o) => sum + o.total, 0) /
      $context.processedOrders.length,
  };
}

Best for:

  • Complex multi-step data transformations
  • Algorithmic processing (loops, conditionals, calculations)
  • Preparing test data that requires complex logic
  • When CONTEXT node + JSONata is not enough

⚠️ Rule: Use Script Node sparingly. If CONTEXT node + JSONata can handle it, use that instead.


Decision Guide: Where to Modify Context?

Need to set up test data before API call?
  → Use CONTEXT Node

Need to extract data from API response?
  ├─ Simple extraction/transformation?
  │    → Use Context Operations (no-code)
  └─ Complex logic ($$Utils, algorithms)?
       → Use After Response Hook

Need to prepare complex data structure?
  ├─ Can JSONata handle it?
  │    → Use CONTEXT Node
  └─ Too complex for JSONata?
       → Use Script Node

Need to modify context dynamically during flow?
  → Use Before Request Hook

Setting Variables with CONTEXT Nodes

Use CONTEXT nodes to store and transform data:

CONTEXT Node operations:
├── Set context.userId = response.data.id
├── Set context.userEmail = response.data.user.email | lowercase
├── Set context.itemCount = $count(response.data.items)
└── Set context.activeItems = response.data.items[status="active"]

Variable Interpolation

Use variables throughout your test cases:

  • URLs: https://api.example.com/users/{{context.userId}}
  • Headers: Authorization: Bearer {{context.token}}
  • Request Bodies: Reference variables in JSON payloads
  • Assertions: Validate against stored values

Special Request Body Feature: Mixin

ReAPI provides a special __mixin__ feature for request bodies that allows you to merge context objects with additional properties:

Setup: Store object in context

CONTEXT Node:
Set context.baseUserData = {
  "name": "Test User",
  "email": "test@example.com",
  "department": "Engineering"
}

Usage: Merge with additional data

API Node: POST /users
Body: {
  "__mixin__": "{{context.baseUserData}}",
  "role": "developer",
  "active": true
}

Result: Merged request body

{
  "name": "Test User",
  "email": "test@example.com",
  "department": "Engineering",
  "role": "developer",
  "active": true
}

Key Features:

  • Object merging: __mixin__ only works with object context values
  • Property override: Additional properties can overwrite mixin properties
  • Clean syntax: No need for complex JSONata merging expressions
  • Nested usage: Can be used at any level within the request body structure

Advanced Example: Object Update Pattern

// Step 1: Get current user profile
API Node: GET /users/{{context.userId}}

// Step 2: Store entire object in context
CONTEXT Node:
Set context.userProfile = response.data

// Step 3: Update with modifications
API Node: PUT /users/{{context.userId}}
Body: {
  "__mixin__": "{{context.userProfile}}",
  "department": "Product Management",  // Change department
  "lastUpdated": "{{$now()}}"         // Add timestamp
}

Template Defaults Example:

CONTEXT Node:
Set context.orderDefaults = {
  "currency": "USD",
  "tax_rate": 0.08,
  "shipping_method": "standard"
}

API Node: POST /orders
Body: {
  "__mixin__": "{{context.orderDefaults}}",
  "customer_id": "{{context.customerId}}",
  "items": "{{context.orderItems}}",
  "shipping_method": "express",  // Overrides default
  "priority": "high"             // Additional property
}

JSONata Support

ReAPI uses JSONata for powerful data transformations:

  • Query and filter arrays
  • Transform object structures
  • Perform calculations
  • Handle conditional logic

Common Patterns

Chaining API Calls

// First call: Create user
context.newUserId = response.data.id;
 
// Second call: Get user details
// URL: /users/{{context.newUserId}}

Data Validation

// Store expected values
context.expectedTotal = 100;
 
// Later assertion: response.total = context.expectedTotal

Dynamic Configuration

// Set environment-specific values
context.baseUrl = environment.server.url;
context.apiKey = environment.auth.key;

More advanced JSONata examples and patterns coming soon…