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:
Pre-request (onRequest)
Executes before the request is sent. Modify headers, body, URL, or abort the request entirely.
Post-response (onResponse)
Executes after receiving the response. Extract data, update environment variables, or chain to another request.
Tests (onTest)
Executes for test automation. Validate responses using assertions and expectations.
// 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
Dynamic authentication
Request signing
Dynamic body generation
Conditional execution
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 ());
}
export function onRequest ( req ) {
// AWS-style request signing
const accessKey = req . env . getSecret ( 'awsAccessKey' );
const secretKey = req . env . getSecret ( 'awsSecretKey' );
const stringToSign = ` ${ req . method } \n ${ req . url } \n ${ req . body || '' } ` ;
const signature = hmacSHA256 ( stringToSign , secretKey );
req . headers . set ( 'Authorization' , `AWS ${ accessKey } : ${ signature } ` );
req . headers . set ( 'X-Amz-Date' , new Date (). toISOString ());
}
export function onRequest ( req ) {
// Generate dynamic test data
const payload = {
id: crypto . randomUUID (),
timestamp: Date . now (),
nonce: Math . random (). toString ( 36 ). substring ( 7 ),
data: req . env . getVariable ( 'testData' ) || '{}' ,
};
req . body = JSON . stringify ( payload );
req . headers . set ( 'Content-Type' , 'application/json' );
}
export function onRequest ( req ) {
// Skip request in certain conditions
const environment = req . env . getVariable ( 'environment' );
if ( environment === 'production' ) {
// Return a mock response instead of sending request
return new YasumuResponse ( JSON . stringify ({ mock: true }), {
status: 200 ,
statusText: 'OK' ,
headers: { 'Content-Type' : 'application/json' },
});
}
// Proceed with actual request
}
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
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.