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 type | What you do in the UI | Typical use |
|---|---|---|
| Script | Write a piece of JavaScript | Change headers/body, signing, decryption, conditional logic |
| Library script | Reference a saved shared script | Reuse "inject token", "unified logging" in many places |
| Extract | Take a value from the response and store it as a variable | Pass the login token to the next request |
| Assert | Add one validation against the response | Status code = 200, a field exists |
| Wait | Pause for some milliseconds | Rate 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
onRequestin the pre-send chain,onResponsein the post-response chain. - The function names must be exactly these, otherwise the script is treated as "no entry point" and skipped.
awaitis supported.
What Scripts Can Change
Changing the request (the request object in onRequest)
| You want | How |
|---|---|
| Set / override a header | request.headers.set('Authorization', 'Bearer ' + tok) |
| Add / change a query param | request.queries.set('ts', String(Date.now())) |
| Read the raw body | var text = request.body.parseText() or request.body.parseJSON() |
| Rewrite the whole body | request.body = the new string (plain assignment) |
Reading / changing the response (the response object in onResponse)
| You want | How |
|---|---|
| Read the status code | response.statusCode |
| Read a response header | response.headers.get('Set-Cookie') |
| Read the body | response.body.parseJSON() / .parseText() |
| Rewrite the whole response body | response.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:
| Capability | What to use |
|---|---|
| Read / write variables (cross-request passing) | sdk.env.get(name) / sdk.env.set(name, value) |
| Declarative tests | sdk.test('status should be 200', function(){ sdk.expect(response.statusCode).toBe(200) }) |
| JSONPath queries | sdk.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 base64 | sdk.utils.base64Encode(str) / base64Decode(str) |
⚠️ The engine is QuickJS: there is no browser
crypto.subtle, andcrypto.getRandomValuesdoes not work in this engine (it doesn't fill the passed array) — use$cryptofor AES, andcrypto.randomUUID/$uuidfor 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:
- Script-library identifier — exact match, stable across renames (recommended)
- Script-library name — readable, but renaming the library breaks the reference (ambiguous duplicate names are skipped)
- Built-in libs — bundled with the app
Built-in libraries
| Key | Exports | Notes |
|---|---|---|
@libs/base64 | encode(str) / decode(text) | UTF-8-safe Base64 (Chinese & emoji safe); decode ignores whitespace, invalid sequences kept as Latin-1 |
@libs/hex | encode(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
requireother 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, thenrequire('@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.requirecalls 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.