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}}/settingsPattern 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:
1. CONTEXT Node (Recommended for Setup)
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
2. API Node Context Operations (Recommended for Response Data)
When to use: Extract data from API responses
How: Visual UI attached to API nodes, runs after response received
Two modes available:
Mode A: No-Code Context Operations (Recommended 90% of cases)
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 HookSetting 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.expectedTotalDynamic Configuration
// Set environment-specific values
context.baseUrl = environment.server.url;
context.apiKey = environment.auth.key;More advanced JSONata examples and patterns coming soon…