Skip to content

Scripting

Inline scripts and external JavaScript files handle synchronous or async preparation and conditional response processing that saved fields, variables, and captures cannot express. Use pre for signatures, timestamps, test data, and request mutations; use post to process a completed response after captures and before assertions.

For copyable examples, browse the Cookbooks for request preparation, response processing, testing, and request chaining.

Open the request or folder + menu and reveal Pre Script, Post Script, or Tests. Tabs with saved source are already visible. Press Return to edit inline JavaScript and Ctrl+S to save request or folder changes. For shared collection code, open F4 → Collection → Scripts; its drafts save when you leave the editor or change phases.

The code editor provides highlighting, folding, completion, parameter help, and diagnostics. Ctrl+Space requests assistance; Ctrl+Alt+F formats inline code. Semantic diagnostics are advisory. TUI Send and Runner check the syntax of applicable scripts and tests before HTTP without executing them during the check. CLI runs keep their existing runtime validation and execution behavior.

Choose External file to enter a collection-relative ./path/to/file.js. The path field completes directories and .js files. Ctrl+Alt+X, or Open Script in External Editor in the command palette, validates and opens that file using the editor selected in global Behavior settings. Noodle does not create or rewrite external script files. Switching a non-empty source between inline and external modes asks before discarding it.

On a request script tab, use Ctrl+Alt+R or Show Script Execution Order to inspect the active phase’s collection, folder, and request sources in execution order. g then d, f, or j jumps to the corresponding script or test tab when visible.

Noodle 0.9.1 moves every Noodle API under the frozen noodle namespace. Change request.headers.set(...) to noodle.request.headers.set(...), and prefix response, env, run, crypto, random, and cookies API accesses the same way. Bare API globals are unavailable. console and JavaScript built-ins such as Date, JSON, and Math remain global.

Prefer declarative request fields and $VARNAME substitution when they are enough. Use scripts.pre when the outgoing request needs computed values or coordinated mutations immediately before HTTP.

Scripts are a good fit for:

  • signing a request body with a secret key;
  • adding a timestamp, nonce, or digest;
  • transforming a JSON body before sending;
  • changing prepared authentication or query parameters; and
  • sharing a transient RunScope value with a later request.

Use captures to pass response values forward and assertions to check a response. Those jobs do not need scripting. Use scripts.post for conditional processing after captures and before assertions. Use Scripted Tests for programmable response checks with test() and expect() after assertions.

Add a string-valued pre member under the request’s top-level scripts field:

name: Create signed event
method: POST
url: $base_url/events
body_type: json
body: '{"name":"deploy"}'
scripts:
pre: |-
const timestamp = new Date().toISOString();
noodle.request.body.setJson({ ...noodle.request.body.json(), timestamp });
noodle.request.headers.set("X-Timestamp", timestamp);
noodle.request.headers.set(
"X-Signature",
noodle.crypto.hmacSha256(noodle.env.get("SIGNING_SECRET"), noodle.request.body.text(), "hex"),
);
noodle.run.set("temporary_nonce", noodle.crypto.randomBytes(12, "base64"));
console.info("prepared", timestamp);

Noodle Pre Script editor adding a request ID and timestamp, with passing script and assertion results

Use the Pre Script tab, edit request YAML directly, or open the YAML editor with Ctrl+Alt+E. Script source is literal and does not receive $VARNAME substitution.

An optional post string can accompany pre or stand alone. Empty strings are valid no-ops and are omitted when serializing YAML. Empty mappings, unknown keys, non-string sources, and malformed external paths are rejected.

Move reusable code into .js files and reference them from request YAML, collection settings.yml, or a nested folder.yml:

scripts:
pre: ./scripts/prepare.js
post: ./scripts/process.js
tests: ./scripts/check-response.js

Paths always start at the collection root, even from a nested request or folder. A reference must begin with ./, end with .js, and use forward slashes. Absolute paths, .., empty or . segments, backslashes, and NUL bytes are rejected. Files must be readable, valid UTF-8, regular files of at most 256 KiB. Symlinks within the collection are supported; escapes are rejected.

For example, scripts/check-response.js can contain:

test("request succeeded", () => {
expect(noodle.response.status).toBe(200);
});

A manual send or request run loads every applicable pre/post/test source before the first pre script. Collection runs and the F5 Runner preflight every selected request before any HTTP. Missing or invalid sources are configuration failures (CLI exit 2). Dynamically called saved children resolve their own sources before their pre scripts. Unselected requests do not block a run unless called this way.

File reads are shared within one run, including dataset rows and saved children; the next manual send reads current contents. Files use the same sandbox as inline code, with no import, require, module loading, or filesystem access. You can mix file references and inline blocks. See the external-script sample collection for preparation, response processing, inherited hooks, tests, and chaining.

The sandbox exposes the following APIs:

API Purpose
noodle.request Read the prepared request; mutate it in pre only
noodle.env Read the selected-environment snapshot
noodle.run Read RunScope values; pre/post may set or remove them, with optional persistence
noodle.crypto Create SHA-256, HMAC-SHA256, and random-byte values
noodle.random Bounded English test-data generators with per-invocation seeds
noodle.time Timestamps, ISO parsing, timezone formatting, and elapsed durations
noodle.response Post and tests: status, status text, timing, headers, text, and cached JSON
noodle.cookies Final-URL-scoped get/set/delete in post; read-only get in tests, when available
console Record bounded log, info, warn, and error messages

See the Script API reference for every method, accepted value, and fixed resource limit.

Every manual send, request run, collection run, and TUI Runner request uses the same order after source preflight:

  1. Apply folder overrides.
  2. Resolve the environment and current RunScope values.
  3. Substitute request variables and body templates once.
  4. Run pre blocks from collection to request.
  5. Send the prepared request.
  6. Evaluate captures.
  7. Run post blocks from collection to request against the response and committed captures.
  8. Evaluate assertions, including after post failure.
  9. Run inherited and request tests, including after HTTP, capture, post, or assertion failure.

Pre stages request and RunScope mutations until complete success. If pre throws or exceeds a limit, Noodle discards its changes and does not send HTTP. Successful request mutations remain in memory and are never written back to YAML.

Successful noodle.run.set values become available to later requests in the same ordered collection run, even when transport or response evaluation later fails. Manual sends and request run use a fresh scope.

scripts:
post: |-
if (noodle.response.status === 201) {
noodle.run.set("event_id", noodle.response.json().id);
console.info("created event");
}

Noodle Post Script editor preparing data for the next request, with expanded post-response diagnostics

Post runs once per completed HTTP response, including HTTP/capture failures, but not transport failures or intermediate redirects/authentication challenges. Request readers reflect the final prepared HTTP leg after signing and cookie/header preparation; every request mutator throws a read-only API error. Post failure discards only its staged RunScope/cookie changes, preserving the response, captures, earlier writes, and logs. Assertions and scripted tests still run. Successful post values reach later collection requests even when assertions fail.

noodle.response exposes status, statusText, timeMs, case-insensitive headers.get/has, lazy text(), and cached json(). Missing headers return null; invalid JSON throws a structured API error. Body readers have a separate 5 MiB UTF-8 cap; extracted RunScope values retain 256 KiB/depth-32 limits.

When cookies are enabled and available, post also exposes noodle.cookies.get(name), set({ name, value }), and delete(name). These operations are scoped to the final URL; cookies are host-only and domain is rejected. Cookie and RunScope changes commit together after complete validation, with deferred cookie persistence. sendCookies: false removes the capability but still processes response Set-Cookie. See the reference for exact attributes.

Both phases can persist or delete selected values:

noodle.run.set("BASE_URL", "https://api.example.com", { persist: "environment" });
noodle.run.set("ACCESS_TOKEN", token, { persist: "secret" });
noodle.run.unset("BASE_URL", { persist: "environment" });
noodle.run.unset("ACCESS_TOKEN", { persist: "secret" });

Manual sends and request run honor these options for an existing selected environment. Collection runs and the Runner keep them transient and report suppression. Without options, writes remain transient. noodle.env.get keeps its initial snapshot; noodle.run.get sees staged or committed scope writes. Persistent unset hides the baseline for the remaining run until another successful set or capture; plain unset removes only the transient override.

Environment operations cannot alter declared secrets. Secret set may promote an ordinary variable; secret unset removes its vault value and declaration, whereas CLI secret delete retains the declaration. Secret unset cannot delete an ordinary entry, empty secrets and reserved _color names are rejected, and secret writes never fall back to plaintext.

Successful pre persistence happens before HTTP. Captures save their original values before post persistence, so durable precedence is pre, capture, post. Each phase saves its latest explicit intent per key; later transient writes do not change the saved snapshot. Script failure discards that phase’s intents. Storage failure attempts rollback, retains successful runtime/request/cookie changes, and fails script diagnostics without skipping remaining phases. Each invocation permits 100 persistence keys and a 256 KiB intent batch; host storage work runs outside the VM deadline.

Both phases expose bounded English noodle.random generators:

noodle.random.seed(42);
noodle.run.set("test_user", {
id: noodle.random.uuid(),
name: noodle.random.name(),
email: noodle.random.exampleEmail(),
age: noodle.random.number({ min: 18, max: 80 }),
});

Each invocation has independent Faker 10.6.0 state; a seed resets only its sequence. Use RunScope to share generated values. Relative dates also need an explicit timezone-bearing refDate for reproducibility; current timestamps remain clock-based. Passwords become known secrets immediately, including after failure. Other generated data stays visible. IDs and passwords are test data with no security or uniqueness guarantee; generated URLs and paths perform no network or filesystem access. See the catalog and options.

Both phases expose noodle.time. Capture one instant when multiple values must agree, then apply generated values directly to the request in pre:

const now = noodle.time.now();
noodle.request.headers.set("X-Timestamp", noodle.time.iso(now));
noodle.run.set("expiresAt", noodle.time.add(now, 15, "minutes"));
const local = noodle.time.format(now, "YYYY-MM-DD HH:mm:ss Z", {
timeZone: "America/Santiago",
});

now() returns Unix milliseconds; unix() returns seconds. parse() accepts ISO dates at midnight UTC or timestamps with an explicit offset. Formatting defaults to UTC and supports named timezones. Arithmetic measures elapsed time: days always means 24 hours, including across daylight-saving changes.

Read expiresAt with noodle.run.get in post or substitute it in a later collection request. JavaScript Date and random timestamp generators remain available. See the time API reference for methods, tokens, and limits, or the expiration recipe.

The TUI Results tab shows Pre-request/Post-response rows with status, duration, log count, normalized errors, persistence outcomes, and expandable redacted messages. Human output uses Pre-script/Post-script labels and reports status and log count without printing log text. JSON includes scripts: { evaluated, results } in executed pre/post order.

Manual timeline entries retain bounded, redacted script diagnostics, logs, and persistence outcomes, with [TRUNCATED] markers for oversized text. Script source, capture results, and RunScope values stay excluded. Automation does not create timeline history. A successful manual send does store the prepared request snapshot after normal timeline redaction. See Inspect and save responses for the complete Results workflow.

Each invocation receives a fresh QuickJS runtime and context. Synchronous code, Promises, and top-level await use the same sandbox. Network calls use only noodle.runRequest and noodle.sendRequest in pre/post. Imports, Bun/process APIs, filesystem/shell access, raw fetch, timers, workers, and background tasks are unavailable. VM execution is limited to 500 ms across resumptions, with a 30-second wall deadline including child calls. See the full limits and transaction rules.

Treat every scripted collection as code you trust. noodle.env.get can read a secret from the selected environment, and a script can place that value in the URL, headers, or body sent immediately afterward. Review unfamiliar scripts before running the collection.

For CI, fail-fast, structured output, captures, and assertions around scripted requests, continue with Automation.

scripts:
pre: |-
const login = await noodle.runRequest("auth/login");
const token = login.json().token;
noodle.run.set("TOKEN", token);
noodle.request.headers.set("Authorization", `Bearer ${token}`);

Create auth/login.yml first. The call runs its complete lifecycle and exposes its captures and script writes through noodle.run.get. The parent commits the combined variables only on success. The request already passed substitution, so use explicit setters for the current request. Nested persistence is suppressed; the parent can explicitly persist selected values. Received cookies and HTTP effects survive variable rollback.

For direct HTTP and caught failures, see the chaining recipes. Child summaries appear in expandable Results rows, Runner details, CLI output, and bounded manual history, including failures handled by try/catch.

Declare scripts.pre, scripts.post, and tests as inline JavaScript or external file references in collection settings.yml, nested folder.yml, or request YAML.

  • Pre: collection → outermost folder → nearest folder → request.
  • Post, after captures: collection → outermost folder → nearest folder → request.
  • Tests, after assertions: collection → outermost folder → nearest folder → request.

All three phases use the same scope order. The most specific successful post write wins, including persisted writes. Versions 0.9.4 and 0.9.5 ran post blocks in reverse order; update collection or folder post blocks that depend on values produced by request post blocks when upgrading.

For a concrete example, see shared defaults and request post writes.

Collection and folder blocks run once per request, including each dataset row. Root folder.yml is ignored. An empty block is a no-op and does not disable inherited blocks.

Every block has a fresh QuickJS invocation. JavaScript locals are isolated; successful RunScope writes are visible to later blocks. A pre failure stops remaining pre blocks and HTTP. A post failure rolls back only that block and continues later posts, assertions, and tests. A top-level test error stops only its block. Successful persistence intents keep execution order; collection runs and F5 suppress persistence as before. Saved noodle.runRequest() calls use the same inheritance and cycle protections.

Executed blocks identify their phase, scope, scope ID, and source kind. source.path is the declaring YAML path; source.scopeId identifies the collection, folder path, or request ID. source.sourceKind is inline or external; external blocks add the collection-relative source.sourcePath. Test groups retain ordered invocations, including blocks declaring zero tests, and errors for multiple block errors; legacy error remains the first error. CLI, Results, and history show origins and bounded, redacted diagnostics and logs without retaining source code or RunScope values.

Inherited blocks share a 64 KiB console-text budget and a 256 KiB test-record budget per request. Logs become [TRUNCATED] at the limit; exhausted test records produce a test script error, and later blocks are still invoked.