Updated Scripting System (revised) #5221
Replies: 10 comments 6 replies
|
#5191 serves as the first step toward improving the scripting developer experience, as outlined in this RFC. It replaces CodeMirror with Monaco in the scripting editors as part of the experimental scripting sandbox, unlocking TypeScript-powered IntelliSense, real-time validation, and JSDoc support. Future efforts will continue to build on this foundation. |
|
We've introduced a It's not recommended to migrate or rework your existing scripts for the new scripting APIs just yet. Breaking changes are expected as the system evolves. The rollout is intentionally gradual to gather feedback and refine based on real-world use. All future changes will be scoped to the experimental scripting sandbox (enabled by default; can be toggled in |
|
Not sure if this has been covered yet, but I think one important function would be an Imagine and endpoint like this: In the scripting, I would need to generate a UUID v4 for the random key, and then get the query string part (probably via Not sure if |
|
#5417 brings chai-powered assertions and the Postman compatibility layer to the experimental scripting sandbox and is live with the v2025.10.0 release. This update enables advanced testing patterns through The rollout continues to be gradual to gather feedback and refine based on real-world use. Breaking changes are still expected as the system evolves. |
|
Is it a feasible option to expose WebSocket-related events to scripting? If this is available, we could add a feature to allow scripting in Realtime -> WebSockets. This would help with WebSocket automations and enable the creation of template responses based on messages received via WebSocket. |
|
#5596 implements The implementation routes requests through Hoppscotch's interceptor system, respecting the configured interceptor ( |
|
As per #5807 (comment) tested it with v2026.1.0:
What does not yet work is importing custom scripts in a pre-request script, e.g.: const queryParameters = pm.require('query_parameters');
const randomizer = pm.require('import_data_generator')
queryParameters.setAdminApiKey();
// ^Property 'setAdminApiKey' does not exist on type 'never'. |
|
Very happy to read about the Planned Future Extensions, mostly |
|
#5745 brings collection-level pre-request and test scripts to the scripting runtime and is live with the v2026.4.0 release. This addresses one of the planned future extensions discussed above. Child collection and root-level scripts now cascade through the request lifecycle (root → folder → request for pre-request, reverse for post-request), with each script running in its own isolated scope. Scripts execute as inherited chains, with environment changes made by earlier scripts available to later scripts through the sandbox environment APIs. |
|
The scripting redesign is valuable, but the main design pressure is capability control. As scripts gain I would define a capability manifest for the experimental sandbox. For example: can read request, can write request, can read response, can access environment variables, can call A few safety details would help:
For WebSocket scripting, I would be extra conservative. Event-driven scripts can run repeatedly and unexpectedly, so rate limits and stop conditions should be part of the API from the beginning. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
RFC: Updated Scripting System (revised)
Preface
Hoppscotch's scripting system has remained relatively dormant for a long time now. With the increased adoption and requirement for more capabilities, it is long overdue for a revamped scripting system that evolves with the times and takes into account the capabilities important for script implementations these days.
We had a previous proposed RFC defined in #2292. This RFC supersedes the previous one and reconsiders certain decisions described in it. This will serve as a living document of the implementation and will be updated to reflect any changes in decisions.
Authors
Goals
The goals of the updated scripting APIs are as follows:
JavaScript Implementation
The implementation comprises the following set of features:
Deprecation of the
pwnamespaceWe will be deprecating the legacy
pwnamespace from Hoppscotch with the advent of the new scripting system. We will not be backporting any of the new features to it, and any improvements we will be making won't be available here. There is no specific timeline at which the functionality will be fully removed yet (probably we will keep it for a long time), but we will start showing deprecation warnings when it is used. ThepwAPI will be superseded by thehoppAPI.Introduction of the
hoppnamespaceThe
hoppnamespace is where the new set of scripting APIs and features will be landing. The reason for this shift is so we can break and change certain behaviour that people expect from thepwAPI. Hence, the shift from thehoppAPI topw, although they will look similar, may introduce behaviour changes. Each section of the API will be defined in its section below.Single script for each request
We propose moving away from the traditional separate pre-request and post-response scripts to a unified script approach. Instead of having two separate script tabs, users will write a single script that defines callbacks for different phases of the request lifecycle.
The script execution model works as follows:
hopp.onBeforeRequest(callback)to specify code that runs before the request is senthopp.onResponse(callback)to specify code that runs after the response is receivedhoppnamespace objectsExample usage:
This approach eliminates the need for a separate shared API since variables can be naturally shared within the same script scope.
TypeScript Language Server and Type Validation
We propose implementing comprehensive TypeScript-based code completion, linting, and type validation to significantly improve the developer experience. While scripts will continue to be written in JavaScript (no TypeScript-specific syntax required), the intelligence and validation will be powered by the TypeScript language service.
Implementation Details
Type Definitions
We will provide comprehensive TypeScript type definitions for:
hopp.*namespace APIs and their methodsBenefits
The type validation will be non-blocking - scripts with type errors can still be executed, but users will receive clear warnings about potential issues.
ESM Module Loading
We propose introducing support for ECMAScript Module (ESM) imports, allowing users to leverage external libraries and modules within their scripts. This significantly expands the capabilities of scripts by enabling access to the vast JavaScript ecosystem.
Remote Module Imports
Users can import modules directly from CDN sources using standard ESM import syntax:
Supported CDN Sources
We plan to support importing from popular CDN services that provide ESM-compatible modules:
Automatic TypeScript Type Support
Following the Deno-style type import mechanism, we propose implementing automatic TypeScript type definitions when available:
X-TypeScript-TypesHTTP header?no-dtsto import URLs if the provided types are incorrect:Security and Limitations
Example Usage Patterns
This ESM support will bring the Hoppscotch scripting environment closer to modern JavaScript development practices while maintaining security and sandbox isolation.
hopp.envnamespaceWe propose that the
hopp.envnamespace will replacepw.envand provide a lot more features and fine-grained control around environments in the app.The main changes around it are as follows:
Ability to specify target environment for operations
The env API will now feature
hopp.env.activeandhopp.env.globalnamespaces, which will have operations within them that will allow you to query specifically within the selected and the global environments (eg,hopp.env.active.get("username")will give you the value within the selected environment and ignore the global environment.). The previous way of doinghopp.env.getand so on will still maintain the current behaviour and still look at both global and selected together.hopp.env.globalwill be empty whilehopp.env.activewould hold the environment variables.Ability to delete environment variables
hopp.env.deleteoperation can be used to delete an environment variable. If multiple declarations are present, the highest precedence version of the variable will be deleted.hopp.env.activeandhopp.env.globalnamespaces also have a version of this function that will target the deletion to specific cases.Recursive Resolution by default
Environment Variables have an ability to be templates and have parts of it be derived from other environment variables (e.g., I have a variable "x" with the value "hello", I can create a variable "y" with the value "<> world", when Hoppscotch uses the environment variable "y", it will resolve and substitute
<<x>>with its value at that point). These are currently available to scripts withpw.env.getResolveand friends. Inhopp.env,hopp.env.getby default will do recursive resolution, to opt out of this, we will be introducinghopp.env.getRaw, which will returnthe environment variable value as is.Manage initial values
Environment variables have initial and current values present with them. The current values will be available to you through the standard operations (
pw.env.get/pw.env.set). With the new API, we are introducing a couple of new functions:hopp.env.reset(key: string): void- Resets an environment variable back to the initial value (current value will be set to the initial value of the environment variable). This function also has the focused active and global counterparts (hopp.env.active.resetandhopp.env.global.reset)hopp.env.getInitialRaw(key: string): string | null- Returns the initial value of an environment variable, returns null if it can't find it. This function also has the focused active and global counterparts (hopp.env.active.getInitialRawandhopp.env.global.getInitialRaw). Note that there is nohopp.env.getInitialfunction, asgetInitialRawdoesn't resolve recursively and just returns the value as is.hopp.env.setInitial(key: string, value: string): void- Updates the initial value of the environment variable of the given key withvalue. This function also has the focused active and global counterparts (hopp.env.active.setInitialandhopp.env.global.setInitial).Full API
So the full API under the
hopp.envlooks as follows:Request Variables, although similar to environment variables, are still considered separate. Their access will still be provided through
hopp.request.variablesAPI.hopp.requestnamespaceWe propose that the
hopp.requestnamespace will provide access to the current request data and allow modification of the request before it is sent. The design follows an immutable property pattern with dedicated mutation functions.Immutable Properties
All request properties will be read-only and cannot be directly modified:
hopp.request.url: string- The complete request URLhopp.request.method: string- The HTTP method (GET, POST, etc.)hopp.request.params: object- Request params as key-value pairshopp.request.headers: object- Request headers as key-value pairshopp.request.body: any- The request body datahopp.request.auth: object- Authentication configurationMutation Functions
Request modification will be performed through dedicated setter functions. These functions will only be available in the
hopp.onBeforeRequestcontext and will throw an error if called withinhopp.onResponse:hopp.request.setUrl(url: string): void- Update the request URLhopp.request.setMethod(method: string): void- Update the HTTP methodhopp.request.setHeader(name: string, value: string): void- Set a single headerhopp.request.setHeaders(headers: object): void- Replace all headershopp.request.removeHeader(name: string): void- Remove a specific headerhopp.request.setParam(name: string, value: string): void- Set a single paramhopp.request.setParams(params: object): void- Replace all paramshopp.request.removeParam(name: string): void- Remove a specific paramhopp.request.setBody(body: any): void- Update the request bodyhopp.request.setAuth(auth: object): void- Update authentication configurationRequest Variables
Request Variables are a Hoppscotch-specific feature that allows variables scoped to individual requests. These are particularly useful for URL path variables or request-specific templating.
We propose that the API will provide:
hopp.request.variables.get(key: string): string | null- Get a request variable valuehopp.request.variables.set(key: string, value: string): void- Set a request variable (only inhopp.onBeforeRequest)Request variables will follow the same scoping rules as other Hoppscotch variables and will be resolved using the
<<variable>>syntax within request configurations.Example usage
hopp.responsenamespaceWe propose that the
hopp.responsenamespace will provide read-only access to the response data returned by the server. This namespace will only be available within thehopp.onResponsecallback context.Response Properties
hopp.response.statusCode: number- The HTTP status code (e.g., 200, 404, 500)hopp.response.statusText: string- The HTTP status text (e.g., "OK", "Not Found", "Internal Server Error")hopp.response.headers: object- Response headers as key-value pairshopp.response.responseTime: number- Time taken for the request to complete in millisecondsResponse Body Access
We propose that the response body will be accessed through the
hopp.response.bodyobject, which will provide multiple ways to parse and access the response data:hopp.response.body.asJSON(): any- Parse the response body as JSON. Throws an error if the response is not valid JSONhopp.response.body.asText(): string- Get the response body as a plain text stringhopp.response.body.bytes(): Uint8Array- Get the raw response body as a Uint8Array for binary data handlingUsage Examples
All
hopp.responseproperties and methods will be read-only, and attempting to modify them will result in an error. The response object will only be available during thehopp.onResponsecallback execution.Context Restrictions
We propose that the
hopp.responsenamespace will only be accessible within thehopp.onResponsecallback. Outside of this context:hopp.response.statusCode,hopp.response.statusText,hopp.response.headers, andhopp.response.responseTimewill all return nullhopp.response.body.asJSON(),hopp.response.body.asText(), orhopp.response.body.bytes()will throw an error with a descriptive message like "Response body is not available outside of onResponse callback"This design ensures that response data is only accessed when it's actually available and prevents common errors from accessing response data at inappropriate times.
hopp.cookiesnamespaceWe propose that the
hopp.cookiesnamespace will provide comprehensive cookie management capabilities for handling HTTP cookies associated with different domains. Unlike Postman's callback-based approach, all cookie operations in Hoppscotch will be synchronous.Cookie Object Structure
We propose that cookies in Hoppscotch will be represented as objects with the following properties:
Cookie Management API
All cookie operations will require specifying the domain as the first parameter:
hopp.cookies.get(domain: string, cookieName: string): Cookie | null- Get a specific cookie object, returns null if not foundhopp.cookies.set(domain: string, cookie: Cookie): void- Set a cookie (create or update)hopp.cookies.has(domain: string, cookieName: string): boolean- Check if a cookie exists for the given domainhopp.cookies.getAll(domain: string): Cookie[]- Get all cookies for a specific domainhopp.cookies.delete(domain: string, cookieName: string): void- Delete a specific cookiehopp.cookies.clear(domain: string): void- Delete all cookies for a specific domainUsage Examples
Cookie mutation operations (
set,delete,clear) will be available in bothhopp.onBeforeRequestandhopp.onResponsecontexts, allowing for cookie management throughout the request lifecycle.Testing API
We propose implementing a robust testing framework for writing assertions and test cases within scripts. The API will be designed to be familiar to users coming from other testing frameworks while providing deep integration with Hoppscotch's request/response data.
Test Structure
Tests will be organized using the
hopp.test()function, which will group related assertions under a descriptive name:Assertion Library
We propose that the
hopp.expect()function will be powered by the Chai assertion library, providing access to all of Chai's built-in assertions and matchers:This will give users access to the full range of Chai's BDD assertion style, including:
.equal(),.eql(),.deep.equal().be.a(),.be.an().have.property(),.have.own.property().have.length(),.have.lengthOf().include(),.contain().be.above(),.be.below(),.be.within().be.true,.be.false,.be.ok.notmodifierHoppscotch Custom Assertions
In addition to the full Chai assertion library, we propose providing several custom assertion methods specifically designed for API testing:
Basic Custom Assertions
hopp.expect(value).toBe(expected)- Exact equality using strict comparison (===), recommended for primitive typeshopp.expect(value).toBeType(type)- Type checking where type is one of: "string", "number", "boolean", "object", "undefined", "bigint", "symbol", "function"hopp.expect(value).toHaveLength(number)- Check that an object has a .length property set to a specific numeric valuehopp.expect(value).toInclude(item)- Check that a string or array contains a specific valueHTTP Status Code Level Assertions
We propose providing convenient matchers for HTTP status code ranges:
hopp.expect(statusCode).toBeLevel2xx()- Status code between 200-299 (success)hopp.expect(statusCode).toBeLevel3xx()- Status code between 300-399 (redirection)hopp.expect(statusCode).toBeLevel4xx()- Status code between 400-499 (client error)hopp.expect(statusCode).toBeLevel5xx()- Status code between 500-599 (server error)Negation Support
All custom assertions will support negation using the
.notmodifier:Common Testing Patterns
Test Execution and Reporting
Error Handling
If an assertion fails, the test framework will:
The testing API will only be meaningful within the
hopp.onResponsecontext, as it typically validates response data, though environment and request data can also be tested.Postman Compatibility Layer
To ease migration from Postman and support existing workflows, we propose providing a comprehensive
pmAPI compatibility layer. This layer will implement Postman's scripting API as best-faith wrappers on top of the nativehoppAPI implementations.Implementation Strategy
We propose that the
pmAPI will follow a dual approach for compatibility:Supported Features: All commonly used Postman APIs will be implemented as wrappers around the corresponding
hoppAPIs, maintaining the same interface and behaviour patterns that Postman users expect.Unsupported Features: Features that don't exist in Hoppscotch (such as collection runner-specific features, Postman Vault, etc.) will be present with proper TypeScript type definitions but will throw a descriptive error when called: "Not supported by Hoppscotch".
HTTP Requests with hopp.fetch
We propose that the foundation for HTTP requests in the new system will be
hopp.fetch, which will implement the standard Fetch API while routing requests through Hoppscotch's interceptor system.Key Features:
fetchfunction will also be available and work identically tohopp.fetchPostman API Mappings
We propose that the
pmAPI will provide familiar Postman interfaces mapped to Hoppscotch functionality:Environment and Variable Management
Request and Response Objects
Testing API
Script Context Information
HTTP Requests (pm.sendRequest)
Unsupported Features
We propose that the following Postman features will be present in the API with proper TypeScript types but will throw "Not supported by Hoppscotch" errors when called:
Migration Example
This script will work unchanged in Hoppscotch within the
hopp.onResponse()context, providing seamless migration from Postman while users can gradually adopt the more powerfulhoppAPIs when needed.TypeScript Support
We propose providing full TypeScript type definitions for all
pmAPIs, including:This compatibility layer will ensure that existing Postman users can migrate their scripts with minimal changes while providing a pathway to adopt Hoppscotch's enhanced scripting capabilities over time.
Planned Future Extensions
This RFC defines the core updated scripting system. However, we acknowledge that future iterations may expand scripting support and variable scoping to include:
hopp.collection.variablesAPI co-existing alongsidehopp.env.globalandhopp.env.activewith clear precedence rules.Status: These features are not covered in this RFC version but are acknowledged design goals. They may be proposed in separate RFCs or revisions to this document.
Final Notes
This implementation will be a gradual process that unfolds over the next few months, and some of the improvements outlined above may be postponed or omitted altogether if we face technical challenges or other constraints along the way.
As mentioned earlier, we're keen to foster an open conversation around enhancing the scripting experience. If you have any suggestions or pain points we haven’t covered, please feel free to share your thoughts in the discussion below.
All reactions