Request Chaining Examples

January 18, 2026 · View on GitHub

Practical examples for chaining multiple requests and passing data between them.

Table of Contents


Overview

Request chaining allows you to:

  • Use data from one request in another
  • Build authentication flows (login → use token)
  • Create resources and use their IDs
  • Implement complex workflows

Key Tools:

  • lc.env - Persist data across sessions
  • lc.globals - Share data within session
  • lc.sendRequest() - Make requests within scripts

Basic Chaining Patterns

Store Data for Next Request

Request 1 - Post-response: Store user ID

// Post-response: Save user ID from response
if (lc.response.status === 200) {
    var user = lc.response.body.json();

    if (user && user.id) {
        lc.globals.set("user_id", user.id);
        lc.env.set("user_id", user.id.toString());
        console.log("Stored user ID: " + user.id);
    }
}

Request 2 - Pre-request: Use stored ID

// Pre-request: Use stored user ID in URL
var userId = lc.globals.get("user_id") || lc.env.get("user_id");

if (userId) {
    lc.request.url = lc.request.url.replace("{userId}", userId);
    console.log("Using user ID: " + userId);
} else {
    console.error("No user ID available - run Get User first");
}

Pass Data via Environment

// Request 1 - Post-response: Store multiple values
if (lc.response.status === 200) {
    var data = lc.response.body.json();

    lc.env.set("resource_id", data.id);
    lc.env.set("resource_name", data.name);
    lc.env.set("resource_type", data.type);

    console.log("Stored resource: " + data.name);
}
// Request 2 - Pre-request: Use stored values
var body = {
    resourceId: lc.env.get("resource_id"),
    resourceName: lc.env.get("resource_name"),
    action: "update"
};

lc.request.body.set(JSON.stringify(body));

Authentication Flows

Login → Use Token

Login Request - Post-response:

// Store auth tokens from login response
if (lc.response.status === 200) {
    var data = lc.response.body.json();

    if (data.access_token) {
        // Store in both globals (fast) and env (persistent)
        lc.globals.set("access_token", data.access_token);
        lc.env.set("access_token", data.access_token);

        // Store expiry
        if (data.expires_in) {
            var expiresAt = Date.now() + (data.expires_in * 1000);
            lc.globals.set("token_expires", expiresAt);
        }

        // Store user info
        if (data.user) {
            lc.globals.set("current_user", data.user);
            lc.env.set("user_id", data.user.id.toString());
        }

        console.log("Login successful - token stored");
    }
} else {
    console.error("Login failed: " + lc.response.status);
}

Protected Request - Pre-request:

// Add auth token to request
var token = lc.globals.get("access_token") || lc.env.get("access_token");

if (token) {
    lc.request.headers.set("Authorization", "Bearer " + token);
} else {
    console.error("No token available - please login first");
}

Auto Token Refresh

// Pre-request: Check and refresh token if needed
var token = lc.env.get("access_token");
var expiry = lc.globals.get("token_expires") || 0;
var now = Date.now();

// Refresh if expired or expiring soon (within 60 seconds)
if (!token || now >= expiry - 60000) {
    var refreshToken = lc.env.get("refresh_token");

    if (refreshToken) {
        console.log("Refreshing token...");

        lc.sendRequest({
            url: lc.env.get("auth_url") + "/token/refresh",
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ refresh_token: refreshToken })
        }, function(err, response) {
            if (err || response.status !== 200) {
                console.error("Token refresh failed");
                return;
            }

            var data = response.body.json();
            if (data && data.access_token) {
                lc.env.set("access_token", data.access_token);
                lc.globals.set("token_expires", now + (data.expires_in * 1000));
                console.log("Token refreshed");
            }
        });
    } else {
        console.error("No refresh token - please login again");
    }
}

// Use the (possibly refreshed) token
token = lc.env.get("access_token");
if (token) {
    lc.request.headers.set("Authorization", "Bearer " + token);
}

CRUD Operations

Create → Read → Update → Delete

1. Create Resource - Post-response:

// Store created resource ID
if (lc.response.status === 201) {
    var resource = lc.response.body.json();

    lc.globals.set("test_resource_id", resource.id);
    lc.env.set("test_resource_id", resource.id);

    console.log("Created resource: " + resource.id);
}

lc.test("Resource created", function() {
    lc.expect(lc.response.status).toBe(201);
});

2. Read Resource - Pre-request:

// Use stored ID in URL
var resourceId = lc.globals.get("test_resource_id");
if (resourceId) {
    lc.request.url = lc.request.url.replace("{id}", resourceId);
}

3. Update Resource - Pre-request:

// Use stored ID and add update data
var resourceId = lc.globals.get("test_resource_id");
if (resourceId) {
    lc.request.url = lc.request.url.replace("{id}", resourceId);

    var updateData = {
        name: "Updated Name " + Date.now(),
        updatedAt: lc.variables.isoTimestamp()
    };
    lc.request.body.set(JSON.stringify(updateData));
}

4. Delete Resource - Pre-request:

// Use stored ID for deletion
var resourceId = lc.globals.get("test_resource_id");
if (resourceId) {
    lc.request.url = lc.request.url.replace("{id}", resourceId);
    console.log("Deleting resource: " + resourceId);
}

4. Delete Resource - Post-response:

// Clean up stored references
if (lc.response.status === 204 || lc.response.status === 200) {
    lc.globals.unset("test_resource_id");
    lc.env.unset("test_resource_id");
    console.log("Resource deleted and references cleaned");
}

Data Extraction Patterns

Extract from Array Response

// Post-response: Extract specific item from list
if (lc.response.status === 200) {
    var data = lc.response.body.json();

    if (data.items && data.items.length > 0) {
        // Store first item
        var first = data.items[0];
        lc.globals.set("first_item_id", first.id);
        lc.globals.set("first_item", first);

        // Store last item
        var last = data.items[data.items.length - 1];
        lc.globals.set("last_item_id", last.id);

        // Store all IDs for batch operations
        var allIds = data.items.map(function(item) {
            return item.id;
        });
        lc.globals.set("all_item_ids", allIds);

        console.log("Stored " + allIds.length + " item IDs");
    }
}

Extract Nested Data

// Post-response: Extract from nested structure
if (lc.response.status === 200) {
    var data = lc.response.body.json();

    // Extract user info
    if (data.user) {
        lc.globals.set("user_id", data.user.id);
        lc.globals.set("user_email", data.user.email);
    }

    // Extract organization info
    if (data.user && data.user.organization) {
        lc.globals.set("org_id", data.user.organization.id);
        lc.globals.set("org_name", data.user.organization.name);
    }

    // Extract permissions
    if (data.permissions) {
        lc.globals.set("user_permissions", data.permissions);
    }
}

Extract Headers

// Post-response: Extract important headers
var rateLimitRemaining = lc.response.headers.get("X-RateLimit-Remaining");
var rateLimitReset = lc.response.headers.get("X-RateLimit-Reset");
var requestId = lc.response.headers.get("X-Request-ID");

if (rateLimitRemaining) {
    lc.globals.set("rate_limit_remaining", parseInt(rateLimitRemaining));

    if (parseInt(rateLimitRemaining) < 10) {
        console.warn("Rate limit low: " + rateLimitRemaining + " remaining");
    }
}

if (requestId) {
    lc.globals.set("last_request_id", requestId);
}

Error Handling

Handle Failed Chains

// Pre-request: Check prerequisites
var token = lc.env.get("access_token");
var resourceId = lc.globals.get("resource_id");

if (!token) {
    console.error("CHAIN BROKEN: No access token");
    console.error("Action: Run Login request first");
}

if (!resourceId) {
    console.error("CHAIN BROKEN: No resource ID");
    console.error("Action: Run Create Resource request first");
}

if (token && resourceId) {
    lc.request.headers.set("Authorization", "Bearer " + token);
    lc.request.url = lc.request.url.replace("{id}", resourceId);
    console.log("Chain prerequisites met");
}

Retry on Failure

// Post-response: Handle and store error for retry
if (lc.response.status >= 400) {
    var retryCount = lc.globals.get("retry_count") || 0;
    retryCount++;
    lc.globals.set("retry_count", retryCount);

    console.error("Request failed (attempt " + retryCount + ")");

    if (lc.response.status === 401) {
        console.error("Auth failed - token may be expired");
        lc.env.unset("access_token");
    } else if (lc.response.status === 429) {
        var retryAfter = lc.response.headers.get("Retry-After");
        console.error("Rate limited. Retry after: " + (retryAfter || "60") + "s");
    }
} else {
    // Reset retry count on success
    lc.globals.set("retry_count", 0);
}

Advanced Patterns

Dynamic Request with sendRequest

// Pre-request: Fetch config before main request
lc.sendRequest({
    url: lc.env.get("base_url") + "/config",
    method: "GET",
    headers: {
        "Authorization": "Bearer " + lc.env.get("access_token")
    }
}, function(err, response) {
    if (err || response.status !== 200) {
        console.error("Failed to fetch config");
        return;
    }

    var config = response.body.json();

    // Use config in current request
    if (config.apiVersion) {
        lc.request.headers.set("X-API-Version", config.apiVersion);
    }

    if (config.features && config.features.newEndpoint) {
        lc.request.url = lc.request.url.replace("/v1/", "/v2/");
    }

    lc.globals.set("api_config", config);
    console.log("Config loaded: API v" + config.apiVersion);
});

Create Parent, Then Child

// Pre-request: Create folder before uploading file
var folderId = lc.globals.get("upload_folder_id");

if (!folderId) {
    console.log("Creating upload folder...");

    lc.sendRequest({
        url: lc.env.get("base_url") + "/folders",
        method: "POST",
        headers: {
            "Authorization": "Bearer " + lc.env.get("access_token"),
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            name: "Upload Folder " + lc.variables.timestamp()
        })
    }, function(err, response) {
        if (err || response.status !== 201) {
            console.error("Failed to create folder");
            return;
        }

        var folder = response.body.json();
        lc.globals.set("upload_folder_id", folder.id);
        console.log("Created folder: " + folder.id);
    });
}

// Use folder ID in request
folderId = lc.globals.get("upload_folder_id");
if (folderId) {
    var body = lc.request.body.json() || {};
    body.folderId = folderId;
    lc.request.body.set(JSON.stringify(body));
}

Batch Processing

// Pre-request: Process items in batch
var itemIds = lc.globals.get("pending_items") || [];
var batchSize = 5;

if (itemIds.length > 0) {
    var batch = itemIds.slice(0, batchSize);
    var remaining = itemIds.slice(batchSize);

    lc.globals.set("current_batch", batch);
    lc.globals.set("pending_items", remaining);

    // Build batch request body
    var batchRequest = {
        items: batch,
        operation: "process"
    };
    lc.request.body.set(JSON.stringify(batchRequest));

    console.log("Processing batch of " + batch.length + " items");
    console.log("Remaining: " + remaining.length + " items");
}

Sequential Workflow State

// Track workflow state
var workflowState = lc.globals.get("workflow_state") || "init";

console.log("Current workflow state: " + workflowState);

switch (workflowState) {
    case "init":
        // First request - create resource
        lc.globals.set("workflow_state", "created");
        break;

    case "created":
        // Second request - verify resource
        var resourceId = lc.globals.get("resource_id");
        lc.request.url = lc.request.url.replace("{id}", resourceId);
        lc.globals.set("workflow_state", "verified");
        break;

    case "verified":
        // Third request - activate resource
        var resourceId = lc.globals.get("resource_id");
        lc.request.url = lc.request.url.replace("{id}", resourceId);
        var body = { status: "active" };
        lc.request.body.set(JSON.stringify(body));
        lc.globals.set("workflow_state", "complete");
        break;

    case "complete":
        console.log("Workflow complete!");
        break;
}

See Also


← Testing Examples | API Overview →