Every response Karate receives is automatically parsed and stored in the response variable. For a JSON API, response is a JavaScript object you can navigate with dot notation, store fields in variables, and use those variables in subsequent requests or assertions. This lesson covers the full extraction toolkit: dot notation, def, array access, Karate's built-in JavaScript functions, and the patterns for chaining multi-step workflows.
Dot notation — walking into JSON
Karate uses JavaScript-style dot notation to navigate response fields. No separate JSONPath library, no $ prefix:
# Response: { "id": 1, "name": "Alice", "address": { "city": "London" } }
* def userId = response.id
* def userName = response.name
* def city = response.address.cityFor the same response, traditional JSONPath would be $.id, $.name, $.address.city. Karate's dot notation is shorter and reads more naturally. Under the hood Karate translates it to JSONPath, but you never write the $ prefix in feature files.
Array access by index:
# Response: { "users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}] }
* def firstUser = response.users[0]
* def secondName = response.users[1].name
* def lastUser = response.users[response.users.length - 1]Negative indexing (response.users[-1]) is not supported by default — use response.users.length - 1 for the last element.
def — storing values for later
def is how you extract values from a response and carry them into subsequent steps:
Scenario: Create a user then verify they exist
Given path 'users'
And request { name: 'Alice', email: 'alice@test.com', role: 'admin' }
When method post
Then status 201
* def userId = response.id
* def userEmail = response.email
Given path 'users', userId
When method get
Then status 200
And match response.email == userEmail
And match response.id == userIduserId and userEmail are plain JavaScript values — strings, numbers, booleans, objects, or arrays depending on what the field contains. They're available anywhere below their def line in the same scenario.
You can also define variables using JavaScript expressions:
* def fullName = response.firstName + ' ' + response.lastName
* def isAdmin = response.role == 'admin'
* def userCount = response.users.lengthExtracting from arrays
When the response is an array, use array methods to filter or transform:
# Response is an array of users
* def users = response
* def adminUsers = karate.filter(users, function(u){ return u.role == 'admin' })
* def userNames = karate.map(users, function(u){ return u.name })
* def adminCount = karate.sizeOf(adminUsers)
And match adminCount > 0
And match userNames contains 'Alice'karate.filter() returns a subset of the array matching the predicate. karate.map() transforms each element. karate.sizeOf() returns the length of an array or object. These are JavaScript-style — if you've written any JavaScript, they'll feel familiar.
For a quick count without extracting the array:
And match response == '#[_ > 0]'This asserts the response is an array with at least one element, without storing anything.
Using karate.jsonPath() for complex paths
For paths that are hard to express with dot notation — particularly when a field name contains dots or special characters — use karate.jsonPath() explicitly:
* def val = karate.jsonPath(response, '$.meta[0].page.total')This is the escape hatch for unusual response structures. For normal APIs with sensible field names, dot notation is always cleaner.
Dynamic values from the system
Sometimes a test needs a value that isn't in the response — a timestamp, a random string, or a counter. Karate allows JavaScript expressions in def:
* def timestamp = java.lang.System.currentTimeMillis()
* def uniqueEmail = 'test-' + timestamp + '@test.com'
* def randomId = Math.floor(Math.random() * 100000)java.lang.System.currentTimeMillis() calls Java directly from Karate — useful for generating unique test data that won't collide across parallel runs.
The extraction and reuse flow
⚠️ Common mistakes
- Using
$JSONPath syntax inmatchexpressions. Karate uses dot notation, not$-prefixed JSONPath. Writingmatch $.users[0].name == 'Alice'is a syntax error. Writematch response.users[0].name == 'Alice'. Reservekarate.jsonPath()for the rare cases where dot notation can't express the path. - Trying to use a
defvariable across scenarios. Variables defined with* defin Scenario 1 are not visible in Scenario 2. Each scenario gets a fresh scope (plus Background). If you need a shared value, define it in Background (for static values) or use acallto a setup feature file (for dynamic values like auth tokens). - Calling
karate.filter()and expecting it to mutate the original array.karate.filter()returns a new array — the original is unchanged. Always assign the result:* def admins = karate.filter(response, function(u){ return u.role == 'admin' }).
🎯 Practice task
Practice extraction and chaining against JSONPlaceholder. 35–45 minutes.
- GET
/users/1. Use* defto extractid,name, andemailinto three separate variables. Then write three separatematchassertions that compareresponse.id == id,response.name == name,response.email == email. Run green — it's asserting against itself, but it proves extraction works. - GET
/users(returns an array). Extract the array into* def users = response. Usekarate.sizeOf(users)to assert there are exactly 10 users. Usekarate.map(users, function(u){ return u.id })to get a list of IDs and assertmatch ids == '#array'. - GET
/usersand usekarate.filter()to find all users whoseaddress.citystarts with'S'. Store the count and assert it is greater than 0 with amatchstatement. - Write a chained scenario: POST
/users(storeresponse.idasuserId), then GET/users/{userId}and assertresponse.id == userId. JSONPlaceholder returns fake data — the second GET won't find the POST (the API is stateless), so the GET will return{}. That's fine; the goal is to practice the chaining pattern, not validate persistence. - Dynamic data: define
* def uniqueEmail = 'user-' + java.lang.System.currentTimeMillis() + '@test.com'. POST a new user with that email. Assertresponse.email == uniqueEmail(if the API echoes it back). This is the pattern for generating collision-free test data. - Stretch: GET
/posts?userId=1(JSONPlaceholder posts for user 1). Extractresponse[0].titleand assert it is a non-empty string usingmatch firstTitle == '#? _.length > 0'.
Next lesson: schema validation with Karate's match syntax — validating entire response shapes with inline markers, reusable schema objects, and schema files.