Skip to main content

Scripting (Developer View)

Pre-send / post-response scripts are Quick Request's Swiss army knife: dynamic signing, encryption & decryption, conditional assertions and cross-request value passing all live here. Scripts are written in JavaScript and run in the built-in QuickJS engine.

This page covers only what you can add in the UI and what scripts can do; for the full JavaScript API signatures see the Script API Reference.


One Request Has Two "Action Chains"​

On the request editor page you can add several steps to the pre-send and post-response chains; they run in order. Each step is one of:

Step typeWhat you do in the UITypical use
ScriptWrite a piece of JavaScriptChange headers/body, signing, decryption, conditional logic
Library scriptReference a saved shared scriptReuse "inject token", "unified logging" in many places
ExtractTake a value from the response and store it as a variablePass the login token to the next request
AssertAdd one validation against the responseStatus code = 200, a field exists
WaitPause for some millisecondsRate limiting, polling intervals

How to Write Scripts: Two Entry Points​

The script engine looks for well-known entry functions in your script:

// Pre-send: can change "the request that is about to go out"
async function onRequest(context, url, request) {
// e.g. inject a header
request.headers.set('X-Trace-Id', $uuid.v4());
return request; // you must return the request
}

// Post-response: can read and change "the response you received"
async function onResponse(context, url, response) {
// e.g. rewrite an encrypted response back to plaintext for humans
return response;
}
  • Write onRequest in the pre-send chain, onResponse in the post-response chain.
  • The function names must be exactly these, otherwise the script is treated as "no entry point" and skipped.
  • await is supported.

What Scripts Can Change​

Changing the request (the request object in onRequest)​

You wantHow
Set / override a headerrequest.headers.set('Authorization', 'Bearer ' + tok)
Add / change a query paramrequest.queries.set('ts', String(Date.now()))
Read the raw bodyvar text = request.body.parseText() or request.body.parseJSON()
Rewrite the whole bodyrequest.body = the new string (plain assignment)

Reading / changing the response (the response object in onResponse)​

You wantHow
Read the status coderesponse.statusCode
Read a response headerresponse.headers.get('Set-Cookie')
Read the bodyresponse.body.parseJSON() / .parseText()
Rewrite the whole response bodyresponse.body = the new string — the UI/result view shows the rewritten content (verified)

Built-in Capabilities — No Need to Reinvent​

Scripts ship with a batch of built-in tools (sdk.*) and $-prefixed native capabilities: encryption, digests, encoding, randomness and JSONPath are all covered:

CapabilityWhat to use
Read / write variables (cross-request passing)sdk.env.get(name) / sdk.env.set(name, value)
Declarative testssdk.test('status should be 200', function(){ sdk.expect(response.statusCode).toBe(200) })
JSONPath queriessdk.jsonPath('$.data.id')
AES encryption / decryption$crypto.encryptAES(plaintext, key[, iv]) / $crypto.decryptAES(ciphertext, key[, iv])
MD5 / SHA digests (signing)$hash.md5(...) / $hash.sha256(...) (returns hex, standard & interoperable)
UUID$uuid.v4()
String base64sdk.utils.base64Encode(str) / base64Decode(str)

⚠️ The engine is QuickJS: there is no browser crypto.subtle, and crypto.getRandomValues does not work in this engine (it doesn't fill the passed array) — use $crypto for AES, and crypto.randomUUID / $uuid for randomness. Don't reach for WebCrypto. Full signatures and caveats: Script API Reference.


Require Script Libraries​

Shared logic can also be loaded with CommonJS-style require() instead of being added as a step. The key is always @libs/<key>, resolved in this order:

  1. Script-library identifier — exact match, stable across renames (recommended)
  2. Script-library name — readable, but renaming the library breaks the reference (ambiguous duplicate names are skipped)
  3. Built-in libs — bundled with the app

Built-in libraries​

KeyExportsNotes
@libs/base64encode(str) / decode(text)UTF-8-safe Base64 (Chinese & emoji safe); decode ignores whitespace, invalid sequences kept as Latin-1
@libs/hexencode(str) / decode(text)UTF-8-safe lowercase hex; decode ignores non-hex chars, odd length throws

Both are also exposed as global aliases (base64.encode(...) / hex.encode(...)) — require just gives you the explicit import style.

const { encode } = require('@libs/base64');
encode('hello 中文'); // 'aGVsbG8g5Lit5aSp'

Your own script libraries​

Save reusable functions as a script library (export via module.exports, no entry function needed) and require it from any pre/post script:

// script library "sign-lib"
function sign(method, path, body, secret) {
return $hmac.sha256(method + path + body, secret);
}
module.exports = { sign };
// any request script
async function onRequest(context, url, request) {
const { sign } = require('@libs/sign-lib');
request.headers.set('X-Sign', sign('POST', request.path, request.body.parseText(), sdk.env.get('secret')));
return request;
}
  • Use Copy require statement in the library manager to get the stable identifier form.
  • Libraries may require other libraries — the whole dependency closure is bundled automatically (cycles resolve Node-style, no infinite loop).
  • The module cache is rebuilt on every execution: libraries never carry state across requests.
  • To reuse an npm package, bundle it into a single file first (e.g. esbuild --format=iife, or Browserify/Webpack), import the file as a script library, then require('@libs/<name>'). Pick pure-JS builds without Node (Buffer/fs/crypto/process) or DOM dependencies.
  • A missing key throws Error: require: 未找到库 [...] — pre-send failures abort the request, post-response failures keep the original response. require calls inside comments are ignored.

Full require semantics: Script API Reference.


How Multiple Scripts Cooperate​

When a request has several scripts, they share the same globalThis (verified). So you can:

Benefits: each script has a single responsibility and can be toggled independently. A "library script" is a full script too (with its own onRequest/onResponse) — add it as a step to run the whole chain, or keep pure functions in it with module.exports and load them with require('@libs/<key>') (see Require Script Libraries).

For concrete chained examples (login for token, AES round-trip) see recipes.