Contents ▼

Advanced Code Blocks

Code Blocks are far more powerful than ordinary HTTP steps, and they're meant for complicated API tests that require special control flow or conditional logic.

You can write your own JavaScript to make requests and parse responses, together with loops, if statements, etc. Speedway will execute your JavaScript code with each monitoring cycle.

It's important to note that this JavaScript doesn't run in a web browser; it is run in a sandboxed environment on Speedway's monitoring engines.

In code blocks, you have access to modern ECMAScript syntax like the () => {} arrow functions and const and let, as well as common parsing functionality like JSON.parse and XML.parse. You also have Speedway's own user object that is basically a user agent for making HTTP requests.

Scope

Code blocks are individually scoped to the monitor cycle. If you declare a variable, it will be set for the current step and any subsequent steps in the same monitor cycle, but will not be visible to later monitor cycles, or other monitor cycles running at the same time.

Globals

Beyond all the usual JavaScript/ECMAScript language constructs, Speedway's code blocks expose a few important objects specific to Speedway that you can use in your scripts.

User

The user object is global within the context of a monitor cycle. It represents the HTTP user agent that is executing the monitor.

This user object exposes the following methods:

// All the standard HTTP methods
user.get(url, args);
user.post(url, body, args);
user.put(url, body, args);
user.patch(url, body, args);
user.delete(url, args);
user.options(url, args);
user.trace(url, args);

// Get and set user variables
user.getVariable(name);
user.setVariable(name, value);

It's important to note that these methods are all synchronous, so there is no need to do a promise chain or callbacks or anything like that. Because the actual processing is done behind the scenes by with Speedway's monitoring engine, synchronous network programming is not the unwise practice it might seem to be if you come from a background in ordinary JavaScript development.

Console

Code blocks also expose a simple console for logging:

console.log(message);

Messages written to the console show up in the monitor logs and aid you in debugging.

JSON

When you're testing APIs, you'll often need to parse JSON responses to look at specific properties. You can use the ordinary JSON.parse for this.

var simple = JSON.parse("{a: 1}"); // parse an arbitrary JSON string
var body = JSON.parse(response.bodyAsString); // parse the response body in a validator

XML

Since XML parsing isn't a standard language feature of JavaScript, Speedway includes the open source xmldoc parser. There is additional documentation for this parser available on GitHub, but here's a quick example:

var xml = XML.parse(response.bodyAsString);
var users = xml.childrenNamed("user");

Examples

Examples are often more helpful than dry documentation, so here are a few of the things you can do in code blocks.

HTTP Requests

The special user object (representing the current v-user) has methods for all of the common HTTP methods (GET, POST, etc).

// POST to the login endpoint and store the auth token for later
user.post(
    "https://garage-example.speedway.app/account/actions/login", 
    { 
        username: "bob", 
        password: "chevy59" 
    }, 
    {
        headers: {
            "Content-Type": "application/json"
        },
        validators: [
            response => {
                let json = JSON.parse(response.bodyAsString);
                
                user.setVariable("apiToken", json.token);
                
                return json.success;
            }
        ]
    }
);

const vehicles;

// GET a list of vehicles using the newly obtained token
user.get("https://garage-example.speedway.app/vehicles?token=${apiToken}", {
    validators: {
        response => {
            let json = JSON.parse(response.bodyAsString);
            
            vehicles = json.vehicles;
            
            // Make sure there's at least one vehicle and all vehicles have VIN numbers
            return vehicles.length > 0 && !vehicles.some(v => !v.vinNumber);
        }
    }

});

// DELETE the first vehicle
user.delete("https://garage-example.speedway.app/vehicles/" + vehicles[0].id);

Request Headers

You can pass custom request headers with each request, either as an object with key-value pairs, or in an array.

// Pass request headers in an object
user.get("https://garage-example.speedway.app/vehicles", {
    headers: {
        "Accept": "application/json"
    }
);

// Pass request headers in an array
user.get("https://garage-example.speedway.app/vehicles", {
    headers: [
        { name: "Accept", value: "application/json" }
    ]
);

Response Validation

Validators, similar to the ones you can use with ordinary HTTP steps, call a JavaScript function to examine the response and return true if it's valid, or false if it's not.

Validator functions can be normal JavaScript functions or the newer ECMAScript arrow functions.

You can specify multiple validator functions for a single response, in which case all must return true for the response to be a considered valid.

// A POST with a JSON body and validator function that requires an HTTP 201 status
user.post(
    "https://api.garage-example.speedway.app/vehicles",
    {
        vinNumber: "1FAHP25NX8W134013"
        make: "Chevy",
        model: "Impala",
        year: 1959,
        color: "red",
        miles: 165538
    },
    {
        headers: [
            { name: "Content-Type", value: "application/json" }
        ],
        validators: [
            // regular function validator syntax
            function (response) {
                return response.status == 201;
            },
            // arrow function validator syntax works too!
            response => response.status === 201
        ]
    }
);

Response Capturing

Often, the API will send you some data that you need to save and use later in the same monitor cycle.

In code blocks, you can capture these from the response and store them using a validator function. Note that we use the validators for capturing too; there is no separate property for capturers as there is with ordinary HTTP steps.

Simply call user.setVariable(name, value) anywhere in your code block to set a user variable. These special user variables are available for the entire monitor cycle, unlike ordinary JavaScript variables which are scoped to the individual code block and may not be available to subsequent steps.

// Create a random numeric string between 0-999999
let random = String(Math.round(1000000 * Math.random()))

// POST to register an account with the random username and capture a user_id
user.post(
    "https://garage-example.speedway.app/registration",
    {
        username: "bob_" + random,
        password: "chevy_" + random
    },
    {
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        validators: [
            response => {
                var body = JSON.parse(response.bodyAsString);

                if (body.profile.user_id) {
                    user.setVariable("user_id", name); // save the user_id for later

                    return true; // valid response! treat as a success
                } else {
                    return false; // invalid response! treat as a failure
                }
            }
        ]
    }
);

Loops & Conditionals

Looping and conditionals can be done with all the usual JavaScript language constructs. This contrived example shows looping with a for loop, an if statement, and setting and getting special user variables with a random value.

console.log("Let's demonstrate random numbers, conditionals, and user vars.");

const colors = ["red", "yellow", "green", "purple"];

// Loop and make 20 requests, searching for vehicles with a random color
for (var i = 0; i < 20; i++) {
    let randomColor = colors[Math.floor(Math.random() * colors.length))];
    
    user.setVariable("color", randomColor);

    user.get("https://garage-example.speedway.app/vehicles?color=${color}", {
        validators: [
            response => {
                let vehicles = JSON.parse(response.bodyAsString);
                
                if (vehicles && vehicles.length > 0) {
                    console.log("There are " + vehicles.length + " " + randomColor + " cars!");
                } else {
                    console.log("There are no " + randomColor + " cars.");
                }
            }
        ]
    });
}

Getting Help

If you get stuck, we're happy to help! Please contact support@speedway.app and tell us all about it. We are always super interested in how you're using code blocks in your API monitoring.