Vayu Scripting Guide¶
Vayu uses QuickJS for JavaScript execution in pre-request and test scripts. The scripting API is compatible with Postman's pm object, making it easy to migrate tests from Postman.
Quick Start¶
// Test script example
pm.test('Status is 200', function() {
pm.expect(pm.response.code).to.equal(200);
});
pm.test('Response has user data', function() {
const json = pm.response.json();
pm.expect(json).to.have.property('id');
pm.expect(json.name).to.be.a('string');
});
The pm Object¶
pm.test()¶
Define a test with assertions.
pm.expect()¶
Create Chai-style expectations for assertions.
pm.expect(value).to.equal(expected);
pm.expect(value).to.not.equal(expected);
pm.expect(value).to.be.true;
pm.expect(value).to.be.false;
pm.expect(value).to.be.null;
pm.expect(value).to.be.undefined;
pm.expect(value).to.exist;
pm.expect(value).to.be.a('string');
pm.expect(value).to.be.an('array');
pm.expect(value).to.include(item);
pm.expect(value).to.have.length(n);
pm.expect(value).to.be.above(n);
pm.expect(value).to.be.below(n);
pm.expect(value).to.have.property('key');
Response Object (pm.response)¶
Access HTTP response data:
pm.response.code // Status code (number, e.g., 200)
pm.response.status // Alias for code
pm.response.responseTime // Perceived latency in ms (submit → completion).
// In load tests this includes generator-side
// queue wait. For pure server wire time, use
// responseTimeWire.
pm.response.responseTimeWire // CURLINFO_TOTAL_TIME in ms - DNS + TCP + TLS +
// send + recv. Matches the pre-v0.3 meaning of
// responseTime; use this to assert on server SLAs
// independent of generator load.
pm.response.responseTimeQueueWait // Generator-side queue overhead in ms,
// i.e. responseTime − responseTimeWire (clamped
// to >= 0). For single-shot sends this is ~0.
pm.response.headers // Headers object (lowercase keys)
pm.response.body // Raw body string
pm.response.text() // Body as string
pm.response.json() // Parse JSON (throws if invalid)
Response Assertions¶
pm.response.to.have.status(200);
pm.response.to.have.header('Content-Type');
pm.response.to.have.jsonBody();
Request Object (pm.request)¶
Access request data:
pm.request.method // HTTP method (string)
pm.request.url // Full URL (string)
pm.request.headers // Request headers (object)
pm.request.body // Request body (string, if any)
Environment Variables (pm.environment)¶
Access and modify environment variables:
// Get variable
const token = pm.environment.get('auth_token');
// Set variable (persists to environment)
pm.environment.set('auth_token', 'new_token_value');
Variables (pm.variables)¶
Access collection and global variables:
// Get variable (searches: environment → collection → global)
const value = pm.variables.get('baseUrl');
// Set variable
pm.variables.set('baseUrl', 'https://api.example.com');
Variable Resolution Order: 1. Environment variables 2. Collection variables 3. Global variables
Console Output¶
Log messages that appear in test results:
console.log('Response:', pm.response.json());
console.info('Info message');
console.warn('Warning message');
console.error('Error message');
Examples¶
Validate JSON Response¶
pm.test('User has correct fields', function() {
const json = pm.response.json();
pm.expect(json).to.have.property('id');
pm.expect(json.name).to.be.a('string');
pm.expect(json.email).to.include('@');
});
Check Status Codes¶
Set Variables from Response¶
// Extract token from response and save to environment
const json = pm.response.json();
pm.environment.set('userId', json.id);
pm.environment.set('token', json.token);
Pre-request Script¶
Modify request before sending:
// Add timestamp header
pm.request.headers['X-Timestamp'] = Date.now().toString();
// Add signature header
const secret = pm.environment.get('secret');
const data = pm.request.body + secret;
pm.request.headers['X-Signature'] = computeHash(data);
Response Time Assertion¶
pm.test('Response time is acceptable', function() {
pm.expect(pm.response.responseTime).to.be.below(1000);
});
Array Validation¶
pm.test('Returns array of users', function() {
const users = pm.response.json();
pm.expect(users).to.be.an('array');
pm.expect(users.length).to.be.above(0);
pm.expect(users[0]).to.have.property('id');
});
Header Validation¶
pm.test('Has Content-Type header', function() {
pm.response.to.have.header('Content-Type');
pm.expect(pm.response.headers['content-type']).to.include('application/json');
});
Limitations¶
QuickJS supports ES2020 features with some limitations:
- No ES2021+ features: No optional chaining (
?.), nullish coalescing (??), etc. - No Node.js APIs: No
require(),fs,http, etc. - Sandboxed: No filesystem or network access
- Memory limit: 64MB per script execution
- Timeout: 5 seconds per script (default), enforced by a wall-clock deadline - an
infinite-loop script is aborted and reported as an error rather than hanging the
engine. Configurable via the
scriptTimeoutsetting (milliseconds);0disables the limit.
Script Execution Context¶
Pre-request Scripts¶
- Execute before sending the HTTP request
- Can modify
pm.request(headers, body) - Can access
pm.environmentandpm.variables - Cannot access
pm.response(request hasn't been sent yet)
Test Scripts (Post-request)¶
- Execute after receiving the HTTP response
- Can access
pm.requestandpm.response - Can access
pm.environmentandpm.variables - Test results are included in the response
Load Test Scripts¶
- Test scripts in load tests are executed deferred (after test completion)
- Only a sample of responses are validated (configurable)
- Results are aggregated and reported in the final report
POST /runs'stestsfield carries the collection chain's test scripts as well as the request's own, composed the same way asPOST /execute(see Script Parts below) - a collection-level assertion is now checked under load, not only in design mode
Script Parts¶
A script's effective source is composed of parts: the collection chain's own script, then the request's own script, in that order. Each route accepts its own field(s), each in either of two forms:
POST /executetakespreRequestScript/postRequestScript(legacy single string, already-joined text) orpreRequestScripts/postRequestScripts(a list of parts). The load path (POST /runs) does not read either of these two keys.POST /runstakestests(the deferred validation script), either as a legacy single string or as a list of parts.POST /executedoes not readtests.
A list of parts is an array of objects, each recording where it came from -
{ "origin": "collection" | "request", "id": "...", "name": "...", "script":
"..." } - so a stored run can say which part is whose.
When both the list and the legacy string are sent for the same field, the list wins; they are never merged. Parts that are empty or only whitespace are dropped.
The parts are joined with a blank line ("\n\n") and run as a single script
in one shared JavaScript scope - one call to the script engine per field
(engine.execute() for preRequestScript(s) / postRequestScript(s),
engine.execute_test() for tests), not one call per part. That means a
const or let declared in the collection's part is visible to the
request's part, exactly as if one person had typed the whole thing into one
editor. It also means a syntax error's reported line number is counted from
the start of the joined text, not from the start of whichever part actually
has the mistake.
Error Handling¶
Script errors are caught and reported:
// If script throws, error is captured
try {
const json = pm.response.json();
} catch (e) {
// Error is reported in test results
}
Test failures don't stop script execution - all tests run and results are collected.
Best Practices¶
- Use descriptive test names:
pm.test('Status code is 200', ...) - Validate structure before accessing: Check if JSON exists before accessing properties
- Use environment variables: Store sensitive data in environments, not scripts
- Keep scripts simple: Complex logic should be in your application code
- Log debugging info: Use
console.log()to debug script issues
API Reference¶
For complete API documentation, see the Scripting Completions API which lists all available pm.* functions and properties.