Skip to main content

Scripts

The Extract and Tests tabs cover the common cases without code. Scripts are the escape hatch for everything else — a computed signature, a conditional skip, decrypting a response, looping over an array, deriving one value from three. Each request carries two optional JavaScript snippets:

  • Pre-request — runs before the request is sent. Set variables, rewrite the outgoing request, or skip the step.
  • Post-response — runs after the response arrives. Read the response, derive variables, and assert with trq.test(...).

The Scripts tab — a pre-request script on the left, and the response Tests tab on the right with script (⚡) rows and captured Console output

Everything runs in Trq's own runtime — identically in Studio and the CLI — so a scripted test behaves the same on your machine and in CI.

Write a pre-request script​

  1. Select a request and open its Scripts tab.
  2. Make sure the Pre-request sub-tab is active (it's the default).
  3. Write JavaScript against the trq object. This example stamps a timestamp, saves it for templating, and signs the request:
// runs before the request is sent
const ts = Date.now();
trq.vars.set('ts', ts); // now usable as {{var.ts}} in this request

// derive an HMAC signature over the timestamp + path
const key = await crypto.subtle.importKey(
'raw', new TextEncoder().encode(trq.env.get('signingSecret')),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
const mac = await crypto.subtle.sign('HMAC', key,
new TextEncoder().encode(ts + trq.request.url));

trq.request.headers.set('X-Timestamp', String(ts));
trq.request.headers.set('X-Signature', btoa(String.fromCharCode(...new Uint8Array(mac))));
console.log('signed at', ts);
  1. Press Send (or Run the case). The request goes out with the header the script added — and the Console strip under the response Tests tab shows your console.log output.
tip
Pre-scripts run before templating

Because the pre-request script runs before {{...}} are resolved, a value you trq.vars.set('ts', …) in the script is available as {{var.ts}} in that same request's URL, headers, or body. This is what makes "compute a value, then send it" a one-step move.

Write a post-response script​

  1. On the same Scripts tab, switch to the Post-response sub-tab.
  2. Read the response through trq.response, then extract values and assert:
const data = trq.response.json; // parsed body (undefined if not JSON)

// derive a variable for later requests in the case
trq.vars.set('token', data.accessToken);

// scripted assertions — each becomes a row in the Tests results
trq.test('token is present', () => trq.expect(data.accessToken).toBeDefined());
trq.test('responded quickly', () => trq.expect(trq.response.durationMs).toBeLessThan(1500));
  1. Send again. In the response Tests tab, your checks appear alongside any declarative rows, each marked with a ⚡ so you can tell scripted checks from Tests-tab rows. A variable you set with trq.vars.set(...) is now usable as {{var.token}} in later requests — the same chaining model, driven by code.
Post-scripts run before the Tests rows

Order within a step is: pre-script → templating → send → Extract rows → post-script → Tests rows. So a post-script can read an extracted value, and a Tests row's expected can reference a variable the post-script just set.

Session-level scripts​

Need the same logic on every request — attach an auth header everywhere, or assert no response is ever a 5xx? Put it once at the session level instead of pasting it into each request.

An API session's Settings drawer — session-level pre-request and post-response script editors

  1. Open the API session's Settings (the ⚙ icon).
  2. Fill in Pre-request and/or Post-response under Scripts — run around every request.
  3. Save.

Session scripts wrap each request's own. The full order per request becomes:

session pre → request pre → templating → send → Extract → session post → request post → Tests

The trq API​

Available in both scripts​

trq.vars.get / set / has / unsetThe run's variable scope — the same store as {{var.*}} and Extract. Values are strings.
trq.env.get('name')Read a value from the active environment.
trq.template(str)Resolve {{var.*}} / {{env.*}} / {{uuid}} in a string, using the current run. Handy in a pre-request script to read the fully-templated body before signing or encrypting it (pre-scripts run before the built-in templating).
console.log / warn / errorCaptured per step and shown in the response Console strip.

Standard JavaScript is available (JSON, Math, Date, URL, RegExp, …) plus crypto (WebCrypto — crypto.subtle for signing and encryption), btoa / atob, and TextEncoder / TextDecoder. Top-level await works.

Pre-request only​

trq.request.url · .method · .bodyRead/write. Assigning an object to .body JSON-stringifies it.
trq.request.headers.get / set / removeHeader rows (case-insensitive). .all() lists the enabled rows.
trq.request.params.get / set / removeQuery-string rows.
trq.skip('reason')Skip this step — reported as skipped (⊘), not a failure.

Post-response only​

trq.response.status · .durationMs · .headers · .textThe completed response (read-only).
trq.response.jsonBody parsed once; undefined when the body isn't JSON.
trq.response.body = …Rewrite the response body — e.g. decrypt it. The new text then feeds the Extract rows, the Tests, and the response pane, so an encrypted transport becomes transparent to the rest of the step.
trq.test('name', fn)A scripted assertion — fn throwing (or returning false) fails the row. Shows in the Tests results marked ⚡.
trq.expect(actual)Matcher sugar for inside trq.test: .toBe · .toEqual · .toContain · .toMatch · .toBeGreaterThan · .toBeLessThan · .toBeTruthy · .toBeDefined.

More examples​

Skip a step conditionally (pre-request):

if (!trq.vars.get('orderId')) trq.skip('no order created earlier in this run');

Build a dynamic body (pre-request):

trq.request.body = JSON.stringify({
requestId: crypto.randomUUID(),
items: [{ sku: trq.vars.get('sku'), qty: 2 }],
});

Validate every item in a list (post-response):

const { items } = trq.response.json;
trq.test('all items have an id', () => {
for (const it of items) trq.expect(it.id).toBeDefined();
});

Decrypt an encrypted response (post-response) — the pre-request script encrypted the body and saved the session key; here we reverse it:

const b64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
const hex = (h) => Uint8Array.from(h.match(/../g).map((x) => parseInt(x, 16)));

const [ivHex, cipherHex, tagHex] = JSON.parse(trq.response.text).split('::');
const sealed = new Uint8Array([...hex(cipherHex), ...hex(tagHex)]);
const key = await crypto.subtle.importKey('raw', b64(trq.vars.get('sessionKey')),
{ name: 'AES-GCM' }, false, ['decrypt']);
const plain = new TextDecoder().decode(
await crypto.subtle.decrypt({ name: 'AES-GCM', iv: hex(ivHex), tagLength: 128 }, key, sealed));

// hand the plaintext back — Extract rows and Tests now run on the JSON
trq.response.body = plain;
trq.test('response decrypts', () => trq.expect(plain.length).toBeGreaterThan(0));
Transparent encryption

Put the encrypt in a session-level pre-request script and the decrypt in a session-level post-response script (Settings → Scripts), and every request in the case is encrypted going out and decrypted coming back — while your per-request Body, Extract, and Tests keep working on plain JSON. Make the pre-script encrypt trq.template(trq.request.body) so {{var.*}} resolve first, and have the post-script pass non-encrypted or error responses through untouched.

Failure semantics​

  • A script that throws (or times out) fails the step — the reason reads pre-request script: … or post-response script: … — and stops the case, like a network error.
  • A trq.test failure is just a failing assertion row: the step fails and the row shows red, but other tests in the same script still run.
  • trq.skip ends the step successfully without sending (⊘ in the run output).

Limits​

Scripts run sandboxed with a 5-second budget each. There is deliberately no fetch, require, or filesystem access — a request belongs in the request list where it's visible, replayable, and reported. Console output is capped per step.

Next: run the whole case and read the report.