Files
apidash/doc/user_guide/scripting_user_guide.md
2025-06-24 23:53:05 +05:30

22 KiB

APIDash Scripting: Using the ad Object

APIDash allows you to write JavaScript code that runs either before a request is sent (pre-request script) or after a response is received (post-response script). These scripts give you powerful control over the request/response lifecycle and allow for automation, dynamic data manipulation, and basic testing.

The primary way to interact with APIDash data and functionality within these scripts is through the global ad object. This object provides structured access to request data, response data, environment variables, and logging utilities.

ad.request (Available in Pre-request Scripts Only)

Use ad.request to inspect and modify the request before it is sent.

ad.request.headers

Manage request headers. Headers are stored internally as an array of objects: [{name: "Header-Name", value: "HeaderValue"}, ...]. Note that header name comparisons in these helper methods are case-sensitive.

  • ad.request.headers.set(key: string, value: string)

    • Adds a new header or updates the value of the first existing header with the exact same key. Ensures the value is converted to a string.
    • Example:
      // Set an Authorization header
      let token = ad.environment.get("authToken");
      if (token) {
        ad.request.headers.set("Authorization", "Bearer " + token);
      }
      
      // Update User-Agent
      ad.request.headers.set("User-Agent", "APIDash-Script/1.0");
      
  • ad.request.headers.get(key: string): string | undefined

    • Retrieves the value of the first header matching the key. Returns undefined if not found.
    • Example:
      let contentType = ad.request.headers.get("Content-Type");
      if (!contentType) {
        ad.console.log("Content-Type header is not set yet.");
      }
      
  • ad.request.headers.remove(key: string)

    • Removes all headers matching the exact key.
    • Example:
      // Remove default Accept header if it exists
      ad.request.headers.remove("Accept");
      
  • ad.request.headers.has(key: string): boolean

    • Checks if at least one header with the exact key exists.
    • Example:
      if (!ad.request.headers.has("X-Request-ID")) {
        ad.request.headers.set("X-Request-ID", Date.now()); // Simple example ID
      }
      
  • ad.request.headers.clear()

    • Removes all request headers.
    • Example:
      // Start with a clean slate for headers (use with caution)
      // ad.request.headers.clear();
      // ad.request.headers.set("Authorization", "..."); // Add back essential ones
      

ad.request.params

Manage URL query parameters. Parameters are stored internally as an array of objects: [{name: "paramName", value: "paramValue"}, ...]. Parameter name comparisons are case-sensitive.

  • ad.request.params.set(key: string, value: string)

    • Adds a new query parameter or updates the value of the first existing parameter with the exact same key. Ensures the value is converted to a string.
    • Note: HTTP allows duplicate query parameter keys. This set method replaces the first match or adds a new one. If you need duplicates, you might need to manipulate the underlying request.params array directly (use with care) or call set multiple times if the backend handles updates correctly.
    • Example:
      // Add or update a timestamp parameter
      ad.request.params.set("ts", Date.now().toString());
      
      // Set a user ID from the environment
      let userId = ad.environment.get("activeUserId");
      if (userId) {
          ad.request.params.set("userId", userId);
      }
      
  • ad.request.params.get(key: string): string | undefined

    • Retrieves the value of the first parameter matching the key. Returns undefined if not found.
    • Example:
      let existingFilter = ad.request.params.get("filter");
      ad.console.log("Current filter:", existingFilter);
      
  • ad.request.params.remove(key: string)

    • Removes all parameters matching the exact key.
    • Example:
      // Remove any debug flags before sending
      ad.request.params.remove("debug");
      
  • ad.request.params.has(key: string): boolean

    • Checks if at least one parameter with the exact key exists.
    • Example:
      if (!ad.request.params.has("page")) {
        ad.request.params.set("page", "1");
      }
      
  • ad.request.params.clear()

    • Removes all query parameters.
    • Example:
      // Clear existing params if rebuilding the query string
      // ad.request.params.clear();
      // ad.request.params.set("newParam", "value");
      

ad.request.url

Access or modify the entire request URL.

  • ad.request.url.get(): string

    • Returns the current request URL string.
    • Example:
      let currentUrl = ad.request.url.get();
      ad.console.log("Request URL before modification:", currentUrl);
      
  • ad.request.url.set(newUrl: string)

    • Sets the request URL to the provided newUrl string.
    • Example:
      let baseUrl = ad.environment.get("apiBaseUrl");
      let resourcePath = "/users/me";
      if (baseUrl) {
        ad.request.url.set(baseUrl + resourcePath);
      }
      

ad.request.body

Access or modify the request body.

  • ad.request.body.get(): string | null | undefined

    • Returns the current request body as a string. For multipart/form-data, this might return an empty or non-representative string; use request.formData (accessed directly for now, potential future ad.request.formData helpers) for structured form data.
    • Example:
      let bodyContent = ad.request.body.get();
      ad.console.log("Current body:", bodyContent);
      
  • ad.request.body.set(newBody: string | object, contentType?: string)

    • Sets the request body.
    • If newBody is an object, it's automatically JSON.stringify-ed.
    • If newBody is not an object, it's converted to a string.
    • Content-Type Handling:
      • If you provide the optional contentType argument (e.g., 'application/xml'), that value is used.
      • If contentType is not provided:
        • Defaults to application/json if newBody was an object.
        • Defaults to text/plain otherwise.
      • The calculated Content-Type is automatically added as a request header unless a Content-Type header already exists (case-insensitive check).
    • Side Effect: Setting the body via this method will clear any existing request.formData entries, as they are mutually exclusive with a raw string/JSON body in the APIDash model. It also updates the internal request.bodyContentType.
    • Example:
      // Set a JSON body
      let userData = { name: "Test User", email: "test@example.com" };
      ad.request.body.set(userData); // Automatically sets Content-Type: application/json if not already set
      
      // Set a plain text body
      ad.request.body.set("This is plain text content.", "text/plain");
      
      // Set an XML body (Content-Type needed)
      let xmlString = "<user><name>Test</name></user>";
      ad.request.body.set(xmlString, "application/xml");
      

ad.request.query

  • Access or modify the GraphQL query string. This applies specifically to GraphQL requests, where the body typically includes a query, variables, and optionally operationName.

  • ad.request.query.get(): string

    • Returns the current GraphQL query string (query, mutation, or subscription). If not set, returns an empty string.

    • Example:

      let gqlQuery = ad.request.query.get();
      ad.console.log("Current GraphQL query:", gqlQuery);
      
  • ad.request.query.set(newQuery: string)

    • Sets the GraphQL query string.

    • Automatically sets the Content-Type header to application/json unless it's already set, ensuring correct handling by GraphQL servers.

    • Does not set the entire request body, it is up to the user to include the full GraphQL payload inside the query. (e.g., { query, variables, operationName }).

    • Example:

      let newQuery = `
        query {
           user(id: 1) {
              id
              username
              email
              address {
                geo {
                  lat
                  lng
                }
              } 
          }
      }`;
      ad.request.query.set(newQuery);
      
  • ad.request.query.clear()

    • Clears the current GraphQL query string by setting it to an empty string.

    • Example:

      ad.request.query.clear();
      

ad.request.method

Access or modify the HTTP request method (verb).

  • ad.request.method.get(): string

    • Returns the current request method (e.g., "get", "post"). Usually lowercase.
    • Example:
      let method = ad.request.method.get();
      ad.console.log("Request method:", method);
      
  • ad.request.method.set(newMethod: string)

    • Sets the request method. The provided newMethod string will be converted to lowercase (e.g., "PUT" becomes "put").
    • Example:
      // Change method based on environment setting
      let usePatch = ad.environment.get("usePatchForUpdates");
      if (ad.request.method.get() === "put" && usePatch) {
        ad.request.method.set("PATCH");
      }
      

ad.response (Available in Post-response Scripts Only)

Use ad.response to access details of the response received from the server. This object is read-only.

  • ad.response.status: number | undefined

    • The HTTP status code (e.g., 200, 404).
    • Example:
      if (ad.response.status === 201) {
        ad.console.log("Resource created successfully!");
      } else if (ad.response.status >= 400) {
        ad.console.error("Request failed with status:", ad.response.status);
      }
      
  • ad.response.body: string | undefined

    • The response body as a string. For binary responses, this might be decoded text or potentially garbled data depending on the Content-Type and encoding. Use bodyBytes for raw data.
    • Example:
      let responseText = ad.response.body;
      if (responseText && responseText.includes("error")) {
        ad.console.warn("Response body might contain an error message.");
      }
      
  • ad.response.formattedBody: string | undefined

    • The response body pre-formatted by APIDash (e.g., pretty-printed JSON). Useful for logging structured data clearly.
    • Example:
      ad.console.log("Formatted Response Body:\n", ad.response.formattedBody);
      
  • ad.response.bodyBytes: number[] | undefined

    • The raw response body as an array of byte values (integers 0-255). Useful for binary data, but be mindful of potential performance/memory impact for very large responses. Depends on Dart correctly serializing Uint8List to List<int>.
    • Example:
      let bytes = ad.response.bodyBytes;
      if (bytes) {
        ad.console.log(`Received ${bytes.length} bytes.`);
        // You might perform checks on byte sequences, e.g., magic numbers for file types
        // if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) {
        //    ad.console.log("Looks like a PNG file.");
        // }
      }
      
  • ad.response.time: number | undefined

    • The approximate time taken for the request-response cycle, in milliseconds.
    • Example:
      let duration = ad.response.time;
      if (duration !== undefined) {
        ad.console.log(`Request took ${duration.toFixed(2)} ms.`);
        if (duration > 1000) {
          ad.console.warn("Request took longer than 1 second.");
        }
      }
      
  • ad.response.headers: object | undefined

    • An object containing the response headers. Header names are typically lowercase (due to processing by the underlying HTTP client). Values are strings.
    • Example:
      let headers = ad.response.headers;
      if (headers) {
        ad.console.log("Response Content-Type:", headers['content-type']); // Access using lowercase key
        ad.console.log("Response Date Header:", headers.date);
      }
      
  • ad.response.requestHeaders: object | undefined

    • An object containing the headers that were actually sent with the request. Useful for debugging or verification. Header names are typically lowercase.
    • Example:
      let sentHeaders = ad.response.requestHeaders;
      if (sentHeaders) {
          ad.console.log("Sent User-Agent:", sentHeaders['user-agent']);
      }
      
  • ad.response.json(): object | undefined

    • Attempts to parse the ad.response.body as JSON.
    • Returns the parsed JavaScript object/array if successful.
    • Returns undefined if the body is empty or parsing fails (an error message is automatically logged to the APIDash console via ad.console.error in case of failure).
    • Example:
      let jsonData = ad.response.json();
      if (jsonData && jsonData.data && jsonData.data.token) {
        ad.console.log("Found token in response.");
        ad.environment.set("sessionToken", jsonData.data.token);
      } else if (jsonData) {
        ad.console.log("Parsed JSON, but expected structure not found.");
      }
      
  • ad.response.getHeader(key: string): string | undefined

    • Retrieves a specific response header's value. The lookup is case-insensitive.
    • Example:
      // These are equivalent because of case-insensitivity:
      let contentType = ad.response.getHeader("Content-Type");
      let contentTypeLower = ad.response.getHeader("content-type");
      ad.console.log("Response Content-Type (case-insensitive get):", contentType);
      
      let correlationId = ad.response.getHeader("X-Correlation-ID");
      if (correlationId) {
          ad.environment.set("lastCorrelationId", correlationId);
      }
      

ad.environment (Available in Pre & Post-response Scripts)

Use ad.environment to get, set, or remove variables within the currently active APIDash environment. Changes made here persist after the script runs and can be used by subsequent requests or other scripts using the same environment.

  • ad.environment.get(key: string): any

    • Retrieves the value of the environment variable named key. Returns undefined if not found.
    • Example:
      let apiUrl = ad.environment.get("baseUrl");
      let apiKey = ad.environment.get("apiKey");
      ad.console.log("Using API URL:", apiUrl);
      
  • ad.environment.set(key: string, value: any)

    • Sets an environment variable named key to the given value. The value should be JSON-serializable (string, number, boolean, object, array, null).
    • Example:
      // In a post-response script after login:
      let responseData = ad.response.json();
      if (responseData && responseData.access_token) {
        ad.environment.set("oauthToken", responseData.access_token);
        ad.environment.set("tokenExpiry", Date.now() + (responseData.expires_in * 1000));
        ad.console.log("OAuth token saved to environment.");
      }
      
      // Store complex object
      ad.environment.set("userProfile", { id: 123, name: "Default User"});
      
  • ad.environment.unset(key: string)

    • Removes the environment variable named key.
    • Example:
      // Clear temporary token after use or logout
      ad.environment.unset("sessionToken");
      ad.console.log("Session token cleared.");
      
  • ad.environment.has(key: string): boolean

    • Checks if an environment variable named key exists.
    • Example:
      if (!ad.environment.has("userId")) {
        ad.console.warn("Environment variable 'userId' is not set.");
        // Maybe set a default or fetch it?
        // ad.environment.set("userId", "default-001");
      }
      
  • ad.environment.clear()

    • Removes all variables from the active environment. Use with extreme caution!
    • Example:
      // Usually used for resetting state during testing, careful!
      // ad.environment.clear();
      // ad.console.warn("Cleared all variables in the active environment!");
      

ad.console (Available in Pre & Post-response Scripts)

Use ad.console to log messages to the APIDash console tab for the corresponding request. This is essential for debugging your scripts.

  • ad.console.log(...args: any[])

    • Logs informational messages. Accepts multiple arguments, which will be JSON-stringified and displayed in the console.
    • Example:
      ad.console.log("Starting pre-request script...");
      let user = ad.environment.get("currentUser");
      ad.console.log("Current user from environment:", user);
      ad.console.log("Request URL is:", ad.request.url.get(), "Method:", ad.request.method.get());
      
  • ad.console.warn(...args: any[])

    • Logs warning messages. Typically displayed differently (e.g., yellow background) in the console.
    • Example:
      if (!ad.environment.has("apiKey")) {
        ad.console.warn("API Key environment variable is missing!");
      }
      let responseTime = ad.response.time;
      if (responseTime && responseTime > 2000) {
          ad.console.warn("Request took over 2 seconds:", responseTime, "ms");
      }
      
  • ad.console.error(...args: any[])

    • Logs error messages. Typically displayed prominently (e.g., red background) in the console. Also used internally by methods like ad.response.json() on failure.
    • Example:
      if (ad.response.status >= 500) {
        ad.console.error("Server error detected!", "Status:", ad.response.status);
        ad.console.error("Response Body:", ad.response.body);
      }
      try {
        // Some operation that might fail
        let criticalValue = ad.response.json().mustExist;
      } catch (e) {
        ad.console.error("Script failed to process response:", e.toString(), e.stack);
      }
      

Key Notes:

  1. Pre/Post Script Context

    • Request manipulation only works in pre-request scripts
    • Response access only works in post-response scripts
  2. Environment Variables

    • Use {{VAR_NAME}} syntax in values for automatic substitution
    • Changes to environment variables persist for subsequent requests
  3. Data Types

    • All values are converted to strings when set in headers/params
    • Use ad.request.body.set() with objects for proper JSON handling
  4. Error Handling

    try {
      // Potentially error-prone operations
    } catch(e) {
      ad.console.error('Operation failed:', e.message)
      // Optionally re-throw to abort request
      throw e
    }
    
  5. Best Practices

    • Always check for existence before accessing values
    • Use environment variables for sensitive data
    • Clean up temporary variables with ad.environment.unset()
    • Use logging strategically to track script execution

Example Workflow

Example 1: Post-request - Extract Data and Check Status

// Post-response Script

// Set URL: https://api.apidash.dev/auth/login
// 1. Check response status
if (ad.response.status < 200 || ad.response.status >= 300) {
  ad.console.error(`Request failed with status ${ad.response.status}.`);
  ad.console.error("Response:", ad.response.body);
  // Optional: Could clear a related environment variable on failure
  // ad.environment.unset("last_successful_item_id");
  return; // Stop processing if status indicates failure
}

ad.console.log(`Request successful with status ${ad.response.status}.`);
ad.console.log(`Took ${ad.response.time} ms.`);

// 2. Try to parse JSON response
const data = ad.response.json();

// 3. Extract and store data if available
if (data && data.access_token) {
  ad.environment.set("current_session_id", data.access_token);
  ad.console.log("Session ID saved to environment.");
} else {
  ad.console.warn("Could not find 'access_token' in the response JSON.");
}

Example 2: Pre-request - Set Auth Header

// Pre-request Script

// Set URL : https://api.apidash.dev/profile
// 1. Get Auth token from environment
const token = ad.environment.get("access_token");
if (token) {
  ad.request.headers.set("Authorization", `Bearer ${token}`);
  ad.console.log("Authorization header set.");
} else {
  ad.console.warn("API token not found in environment!");
}

// 2. Try to parse JSON response
const data = ad.response.json();

// 3. Extract and store final User if available
if (data && data.user && data.user.id) {
    ad.environment.set("logged_in_user_id", data.user.id);
    ad.console.log(`User ID ${data.user.id} saved to environment.`);
}