Skip to main content
Yasumu’s scripting system allows you to execute custom JavaScript/TypeScript code at different points in the request lifecycle. Scripts run in an embedded Deno-powered runtime with access to request/response data, environment variables, and a rich API for automation.

Script execution lifecycle

Scripts can be executed at three different stages:
1

Pre-request (onRequest)

Executes before the request is sent. Modify headers, body, URL, or abort the request entirely.
2

Post-response (onResponse)

Executes after receiving the response. Extract data, update environment variables, or chain to another request.
3

Tests (onTest)

Executes for test automation. Validate responses using assertions and expectations.
rest.ts:97-115
// Pre-request script execution
public async executeScript(
  entityId: string,
  script: YasumuEmbeddedScript,
  context: RestScriptContext,
) {
  // Determines invocation target based on context
  invocationTarget: !!context.response ? 'onResponse' : 'onRequest'
}

Script runtime

Scripts execute in an isolated JavaScript runtime powered by Deno:
script-runtime.service.ts:22-61
public async executeScript<Context, Entity extends ExecutableScript<Context>>(
  workspaceId: string,
  entity: Entity,
  contextType: string,
): Promise<ScriptExecutionResult<Context>> {
  const worker = getGlobalScriptWorker();
  
  // Register and execute the script
  const moduleKey = worker.registerModule(
    `${workspaceId}/${entity.entityId}`,
    entity.script.code,
  );
  
  const response = await worker.execute<Context>(
    moduleKey,
    entity.invocationTarget,
    contextType,
    entity.context,
  );
}
Scripts run in a sandboxed environment with controlled access to system resources, ensuring security while providing powerful capabilities.

Pre-request scripts (onRequest)

Pre-request scripts execute before sending the HTTP request, allowing you to:
  • Generate dynamic values (timestamps, UUIDs, signatures)
  • Modify request headers, body, or URL
  • Read and set environment variables
  • Implement custom authentication logic
  • Conditionally abort requests

Script API

Pre-request scripts receive a YasumuRequest object:
export function onRequest(req, res) {
  // Request properties
  req.method        // HTTP method (GET, POST, etc.)
  req.url          // Request URL
  req.headers      // Headers map
  req.body         // Request body (string or null)
  req.env          // Environment variables
  
  // Modify the request
  req.headers.set('X-Request-Time', new Date().toISOString());
  req.headers.set('X-Request-ID', crypto.randomUUID());
  
  // Update environment
  req.env.setVariable('lastRequestTime', Date.now().toString());
  req.env.setSecret('sessionToken', generateToken());
}

Common use cases

export function onRequest(req) {
  // Generate JWT token
  const token = req.env.getSecret('apiKey');
  const signature = generateSignature(token, req.url);
  
  req.headers.set('Authorization', `Bearer ${signature}`);
  req.headers.set('X-Timestamp', Date.now().toString());
}

Post-response scripts (onResponse)

Post-response scripts execute after receiving the HTTP response, allowing you to:
  • Extract data from responses
  • Update environment variables with response data
  • Chain requests by triggering other endpoints
  • Log response data for debugging
  • Transform response data

Script API

Post-response scripts receive both request and response objects:
export function onResponse(req, res) {
  // Response properties
  res.status        // HTTP status code
  res.statusText    // Status text (e.g., "OK")
  res.headers       // Response headers
  res.body          // Response body (string)
  
  // Convenience methods
  res.json()        // Parse body as JSON
  res.text()        // Get body as text
  
  // Request context still available
  req.url
  req.method
  req.env
}

Common use cases

export function onResponse(req, res) {
  // Save access token from login response
  if (res.status === 200) {
    const data = res.json();
    
    req.env.setSecret('accessToken', data.access_token);
    req.env.setSecret('refreshToken', data.refresh_token);
    req.env.setVariable('tokenExpiry', data.expires_in.toString());
    
    console.log('✓ Tokens saved to environment');
  }
}

Environment variable manipulation

Both pre-request and post-response scripts can read and modify environment variables:
export function onRequest(req) {
  // Read variables
  const apiUrl = req.env.getVariable('apiUrl');
  const apiVersion = req.env.getVariable('apiVersion');
  const apiKey = req.env.getSecret('apiKey');
  
  // Set variables
  req.env.setVariable('requestCount', 
    (parseInt(req.env.getVariable('requestCount') || '0') + 1).toString()
  );
  
  // Set secrets (sensitive data)
  req.env.setSecret('temporaryToken', generateTemporaryToken());
}

export function onResponse(req, res) {
  // Extract and save from response
  const data = res.json();
  
  req.env.setVariable('lastResponseTime', Date.now().toString());
  req.env.setVariable('recordCount', data.total.toString());
  
  // Update secrets
  if (data.new_token) {
    req.env.setSecret('apiToken', data.new_token);
  }
}
Changes to environment variables persist within the workspace and are immediately available to subsequent requests.

Available globals and APIs

Scripts have access to a curated set of global APIs:

Web APIs

fetch, URL, URLSearchParams, Headers, crypto

Console

console.log, console.error, console.warn, console.info

Encoding

btoa, atob, TextEncoder, TextDecoder

Timers

setTimeout, setInterval, clearTimeout, clearInterval

Crypto API

export function onRequest(req) {
  // Generate UUIDs
  const requestId = crypto.randomUUID();
  
  // Generate random values
  const nonce = crypto.getRandomValues(new Uint8Array(16));
  
  // Hash data (subtle crypto)
  const encoder = new TextEncoder();
  const data = encoder.encode('data to hash');
  crypto.subtle.digest('SHA-256', data).then(hash => {
    // Use hash
  });
}

Fetch API

export async function onRequest(req) {
  // Make external requests
  const response = await fetch('https://api.external.com/data');
  const data = await response.json();
  
  // Use fetched data in request
  req.env.setVariable('externalData', JSON.stringify(data));
}
Be cautious with fetch in scripts as it can introduce dependencies on external services and slow down request execution.

Error handling

Script errors are captured and reported without failing the request:
script-runtime.service.ts:52-59
try {
  const response = await worker.execute<Context>(
    moduleKey,
    entity.invocationTarget,
    contextType,
    entity.context,
  );
  return { context: response.context, result: response };
} catch (error) {
  return {
    context: entity.context,
    result: {
      success: false,
      error: error instanceof Error ? error.message : String(error),
    },
  };
}
Handle errors gracefully in your scripts:
export function onRequest(req) {
  try {
    // Risky operation
    const data = JSON.parse(req.env.getVariable('complexData'));
    req.body = JSON.stringify(transformData(data));
  } catch (error) {
    console.error('Failed to process data:', error.message);
    // Use fallback
    req.body = '{}';
  }
}

export function onResponse(req, res) {
  try {
    const data = res.json();
    req.env.setVariable('responseData', JSON.stringify(data));
  } catch (error) {
    console.error('Failed to parse response:', error.message);
    // Response might not be JSON
    req.env.setVariable('responseText', res.text());
  }
}

Script examples

OAuth 2.0 token refresh

export async function onRequest(req) {
  const accessToken = req.env.getSecret('accessToken');
  const tokenExpiry = parseInt(req.env.getVariable('tokenExpiry') || '0');
  
  // Check if token is expired
  if (Date.now() > tokenExpiry) {
    console.log('Token expired, refreshing...');
    
    const refreshToken = req.env.getSecret('refreshToken');
    const response = await fetch('https://auth.example.com/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        grant_type: 'refresh_token',
        refresh_token: refreshToken,
      }),
    });
    
    const tokens = await response.json();
    req.env.setSecret('accessToken', tokens.access_token);
    req.env.setVariable('tokenExpiry', 
      (Date.now() + tokens.expires_in * 1000).toString()
    );
  }
  
  // Use current token
  req.headers.set('Authorization', `Bearer ${req.env.getSecret('accessToken')}`);
}

HMAC request signing

export async function onRequest(req) {
  const apiKey = req.env.getSecret('apiKey');
  const apiSecret = req.env.getSecret('apiSecret');
  const timestamp = Date.now().toString();
  const nonce = crypto.randomUUID();
  
  // Create signature base string
  const signatureBase = [
    req.method,
    req.url,
    timestamp,
    nonce,
    req.body || '',
  ].join('\n');
  
  // Generate HMAC signature
  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    'raw',
    encoder.encode(apiSecret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );
  
  const signature = await crypto.subtle.sign(
    'HMAC',
    key,
    encoder.encode(signatureBase)
  );
  
  const signatureHex = Array.from(new Uint8Array(signature))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
  
  // Add signature headers
  req.headers.set('X-API-Key', apiKey);
  req.headers.set('X-Timestamp', timestamp);
  req.headers.set('X-Nonce', nonce);
  req.headers.set('X-Signature', signatureHex);
}

Multi-step workflow

// Step 1: Login request (post-response)
export function onResponse(req, res) {
  if (res.status === 200) {
    const data = res.json();
    req.env.setSecret('sessionToken', data.token);
    req.env.setVariable('userId', data.user.id);
    console.log('✓ Logged in successfully');
  }
}

// Step 2: Fetch user data (pre-request)
export function onRequest(req) {
  const token = req.env.getSecret('sessionToken');
  req.headers.set('Authorization', `Bearer ${token}`);
  
  // Replace userId in URL
  const userId = req.env.getVariable('userId');
  req.url = req.url.replace(':userId', userId);
}

// Step 2: Fetch user data (post-response)
export function onResponse(req, res) {
  const user = res.json();
  req.env.setVariable('userEmail', user.email);
  req.env.setVariable('userName', user.name);
  console.log(`✓ Fetched profile for ${user.name}`);
}

Best practices

Keep scripts focused

Each script should do one thing well. Split complex logic into multiple requests.

Handle errors gracefully

Use try-catch blocks and provide fallbacks for critical operations.

Log important events

Use console.log to track script execution and debug issues.

Minimize external calls

Avoid unnecessary fetch calls in scripts to keep requests fast.

Use environment variables

Store reusable values in environment variables instead of hardcoding.

Document complex logic

Add comments to explain non-obvious script behavior.

Build docs developers (and LLMs) love