Chaining Requests

Real usage of the RecipeBox API is never one isolated call — a user creates a recipe, then fetches it, then updates it, then deletes it. Testing that flow means the id from the first response has to feed into every request that follows, and that id doesn't exist until the create call actually runs. Hardcoding 42 into a follow-up GET only works by luck. Chaining requests means capturing a value from one response and storing it as a variable that later requests can read.

The Problem: Data That Doesn't Exist Yet

Consider this realistic end-to-end flow for the RecipeBox API:

StepRequestWhy it needs the previous step's data
1POST /recipesCreates a new recipe; the server assigns its id — you can't know it in advance
2GET /recipes/{id}Needs the id from step 1 to fetch the exact recipe just created
3PUT /recipes/{id}Needs the same id to update it
4DELETE /recipes/{id}Needs the same id again, to clean up after the test

Step 1: Capture the id From the Create Response

On the Create Recipe request (POST {{baseUrl}}/recipes), add this to the Tests tab:

var jsonData = pm.response.json();

pm.test("recipe was created", function () {
  pm.response.to.have.status(201);
  pm.expect(jsonData).to.have.property("id");
});

// Save the new id so later requests in this collection can read it
pm.collectionVariables.set("recipeId", jsonData.id);

pm.collectionVariables.set() writes to a collection-scoped variable — the same scope covered in the previous lesson — so it's visible to every other request in the RecipeBox API collection, not just the one that set it.

Step 2: Use {{recipeId}} in the Next Request

Edit the Get Recipe By ID request's URL to use the variable instead of a literal number:

// Before
GET {{baseUrl}}/recipes/42

// After — reads whatever id the Create Recipe request just saved
GET {{baseUrl}}/recipes/{{recipeId}}

Do the same for Update Recipe and Delete Recipe: both become {{baseUrl}}/recipes/{{recipeId}}. Now all four requests operate on whichever recipe was actually created during this run — no guessing, no stale hardcoded id.

Verifying the Chain Worked

Add an assertion on Get Recipe By ID that confirms the fetched recipe really is the one just created, not a coincidence:

var jsonData = pm.response.json();
var expectedId = pm.collectionVariables.get("recipeId");

pm.test("fetched recipe id matches the one we created", function () {
  pm.expect(jsonData.id).to.eql(expectedId);
});

pm.collectionVariables.get() reads back the value that .set() stored, closing the loop between the two requests.

Why This Matters for Realistic Test Flows

  • It matches how the API is actually used. No real client creates a resource and then ignores the id it gets back — your tests shouldn't either.
  • It catches integration bugs single-request tests miss. If POST /recipes returns a 201 with the wrong id format, or the newly created recipe isn't immediately fetchable, chaining surfaces it; two isolated requests each "passing" on their own would not.
  • It enables cleanup. Chaining the id through to a final DELETE /recipes/{{recipeId}} means your test data doesn't pile up in the dev environment every time the collection runs.
  • It's what makes the Collection Runner and Newman useful. Both run every request in a folder in order — chained variables are exactly how state carries from one step to the next across that whole run (both are covered later in this track).
Note: pm.collectionVariables persists for the life of the collection (until changed again or the variable is cleared), while pm.variables.set() only lasts for the current request/run. Use collection variables — like recipeId here — for anything that needs to survive into a different request.
Next: Chaining covers data that only exists after a response comes back. Pre-request Scripts covers the opposite case — generating data before the request is even sent, such as a unique recipe title so repeated test runs never collide.