API Automation with Nova Scripts: A Complete Guide to RESTK's Scripting Framework
Learn how to automate API testing workflows with RESTK's Nova scripting framework. Write pre-request scripts, test assertions, chain requests, and build complex automation flows.
API automation testing saves time, catches regressions, and gives you confidence that your services behave correctly under real-world conditions. But automation is only as good as the scripting framework behind it. If the syntax is clunky, the APIs are limited, or the tooling fights you at every step, the automation ends up being more work than the manual testing it was supposed to replace.
That is why we built Nova -- RESTK's scripting framework designed specifically for API automation.
Nova provides a clean, modern, type-safe scripting API through the nova.* namespace that handles
everything from simple assertions to complex multi-step workflows. This guide covers the entire
framework from first principles through advanced patterns, with code examples you can use as
reference in your own work.
What Is Nova Scripting?
Nova is RESTK's proprietary scripting framework for automating API workflows. Every script you write
in RESTK -- whether it runs before a request, after a response, or at the collection level -- uses
the nova.* namespace to interact with requests, responses, variables, and test assertions.
If you have used Postman's pm.* scripting API, the concept is familiar. The difference is that Nova
was designed from scratch with modern JavaScript conventions, a consistent API surface, and built-in
type safety. There is no legacy baggage, no inconsistent method signatures, and no hidden gotchas.
Nova scripts run in a local JavaScript environment inside RESTK. They execute locally on your machine -- no data is sent to external servers during script execution. This means your auth tokens, request bodies, and test logic remain private.
Getting Started
Where to Write Scripts
Every request in RESTK has a Scripts tab with two sections:
- Pre-script: Runs before the request is sent. Use it to modify headers, set dynamic values, generate timestamps, or conditionally alter the request.
- Post-script: Runs after the response is received. Use it to validate the response, extract data, set variables for subsequent requests, or run test assertions.
To open the script editor, select any request in your collection and click the Scripts tab. The
editor includes syntax highlighting, autocomplete for the nova.* API, and inline error reporting.
Script Execution Order
Scripts run at multiple levels in a specific order:
1. Collection pre-script
2. Folder pre-script
3. Request pre-script
4. --- HTTP Request Sent ---
5. Request post-script
6. Folder post-script
7. Collection post-script
This hierarchy lets you define shared setup logic at the collection or folder level (such as authentication) and override or extend it at the request level.
The nova.* Namespace API
The Nova API is organized into clear, logical groups. Here is the complete reference.
Variables: nova.variable
Variables are how you pass data between requests, store tokens, and parameterize your workflows.
// Set a variable (accessible across all requests)
nova.variable.set("user_id", "12345");
// Get a variable
const userId = nova.variable.get("user_id");
// Check if a variable exists
if (nova.variable.has("access_token")) {
// token is available
}
// Remove a variable
nova.variable.unset("temp_value");
Variables resolve in priority order: request variables > folder variables > collection variables > environment variables. You can also target a specific scope:
// Environment variables
nova.environment.set("base_url", "https://api.staging.example.com");
nova.environment.get("base_url");
// Collection variables (shared across all requests in the collection)
nova.collectionVariables.set("api_version", "v2");
nova.collectionVariables.get("api_version");
// Folder variables
nova.folderVariables.set("folder_token", "abc123");
// Request variables
nova.requestVariables.set("request_specific", "value");
Reference variables in the UI using double curly braces: {{base_url}}/users/{{user_id}}.
Testing: nova.test() and nova.expect()
The testing API lets you write assertions that appear in RESTK's test results panel.
nova.test("Test name", function() {
// assertions go here
});
The nova.expect() method provides a rich set of matchers:
nova.expect(value).toBe(expected); // Strict equality
nova.expect(value).toBeDefined(); // Not undefined
nova.expect(value).toBeTruthy(); // Boolean true
nova.expect(value).toBeGreaterThan(10); // Numeric comparison
nova.expect(value).toBeLessThan(100); // Numeric comparison
nova.expect(array).toContain(item); // Array contains
nova.expect(object).toHaveProperty("key"); // Object has property
nova.expect(string).toMatch(/pattern/); // Regex match
Response: nova.response
Available in post-scripts, this object gives you full access to the HTTP response.
nova.response.status // HTTP status code (e.g., 200)
nova.response.body // Raw response body
nova.response.responseTime // Response time in milliseconds
// Parse body as JSON
const data = nova.response.json();
// Get body as text
const text = nova.response.text();
// Access response headers
const contentType = nova.response.headers.get("Content-Type");
Request: nova.request
Available in pre-scripts, this object lets you modify the request before it is sent.
// Read request properties
nova.request.url // Full URL string
nova.request.method // "GET", "POST", etc.
nova.request.body // Request body
// Add headers
nova.request.headers.add({
key: "X-Request-ID",
value: nova.uuid()
});
// Set JSON body
nova.request.setJsonBody({
username: "john",
timestamp: Date.now()
});
// Set authentication
nova.request.setBearerToken(nova.variable.get("access_token"));
nova.request.setBasicAuth("username", "password");
Environment: nova.environment
Direct access to environment-scoped variables.
nova.environment.get("api_url");
nova.environment.set("api_url", "https://api.production.example.com");
nova.environment.has("api_key");
nova.environment.unset("deprecated_var");
Built-in Utilities
Nova includes helper functions you will use constantly:
nova.uuid() // Generate a UUID v4
nova.timestamp() // Unix timestamp in seconds
nova.timestampMs() // Unix timestamp in milliseconds
nova.isoDate() // ISO 8601 date string
nova.randomInt(1, 100) // Random integer in range
nova.log("message") // Log to the console
Crypto API
For signing requests, generating hashes, and encoding data:
nova.crypto.md5("string to hash");
nova.crypto.sha256("string to hash");
nova.crypto.hmacSha256("message", "secret-key");
nova.crypto.base64Encode("string to encode");
nova.crypto.base64Decode("encoded-string");
Pre-Request Scripts
Pre-scripts run before the request is sent. They are your opportunity to dynamically configure the request based on runtime conditions.
Dynamic Timestamps
Many APIs require a timestamp header or parameter for request signing:
// Add a timestamp header
nova.request.headers.add({
key: "X-Timestamp",
value: String(nova.timestamp())
});
// Add an ISO date to the request body
const body = JSON.parse(nova.request.body || "{}");
body.created_at = nova.isoDate();
nova.request.setJsonBody(body);
Auth Token Generation
Generate or refresh tokens before each request:
// Check if token has expired
const expiresAt = parseInt(nova.variable.get("token_expires") || "0");
const now = Date.now();
if (now > expiresAt) {
// Token is expired -- set a flag for manual refresh
nova.log("Token expired. Please re-authenticate.");
}
// Always attach the current token
const token = nova.variable.get("access_token");
if (token) {
nova.request.setBearerToken(token);
}
HMAC Signature Generation
APIs like AWS, Stripe webhooks, and payment gateways often require HMAC signatures:
// Generate HMAC signature for request signing
const secret = nova.variable.get("api_secret");
const timestamp = String(nova.timestamp());
const method = nova.request.method;
const path = "/api/v1/orders";
// Create the signing string
const signingString = [timestamp, method, path].join("\n");
// Generate HMAC-SHA256 signature
const signature = nova.crypto.hmacSha256(signingString, secret);
// Add signature headers
nova.request.headers.add({ key: "X-Timestamp", value: timestamp });
nova.request.headers.add({ key: "X-Signature", value: signature });
Conditional Logic
Modify the request based on environment or context:
const env = nova.environment.get("ENV");
if (env === "production") {
nova.request.headers.add({
key: "X-Rate-Limit-Bypass",
value: nova.variable.get("prod_bypass_key")
});
} else {
nova.request.headers.add({
key: "X-Debug",
value: "true"
});
}
Post-Request Scripts and Test Assertions
Post-scripts run after the response is received. This is where you validate behavior and extract data.
Status Code Validation
The most fundamental assertion -- did the API respond with the expected status:
nova.test("Status is 200 OK", function() {
nova.expect(nova.response.status).toBe(200);
});
// For creation endpoints
nova.test("Status is 201 Created", function() {
nova.expect(nova.response.status).toBe(201);
});
// For delete endpoints
nova.test("Status is 204 No Content", function() {
nova.expect(nova.response.status).toBe(204);
});
Body Validation
Check that the response body contains the expected structure and values:
nova.test("Response contains user data", function() {
const data = nova.response.json();
// Check top-level properties
nova.expect(data).toHaveProperty("id");
nova.expect(data).toHaveProperty("email");
nova.expect(data).toHaveProperty("name");
// Check specific values
nova.expect(data.email).toMatch(/@/);
nova.expect(data.name).toBeDefined();
});
// Validate nested objects
nova.test("User has address", function() {
const user = nova.response.json();
nova.expect(user).toHaveProperty("address");
nova.expect(user.address).toHaveProperty("city");
nova.expect(user.address).toHaveProperty("country");
});
// Validate arrays
nova.test("Returns a list of users", function() {
const data = nova.response.json();
nova.expect(data.users.length).toBeGreaterThan(0);
nova.expect(data.users[0]).toHaveProperty("id");
});
Response Time Checks
Ensure your API responds within acceptable latency thresholds:
nova.test("Response time is under 500ms", function() {
nova.expect(nova.response.responseTime).toBeLessThan(500);
});
nova.test("Response time is under 2 seconds", function() {
nova.expect(nova.response.responseTime).toBeLessThan(2000);
});
Schema Validation
Validate that every item in a collection response has the required fields:
nova.test("All items have required fields", function() {
const items = nova.response.json().data;
items.forEach(function(item) {
nova.expect(item).toHaveProperty("id");
nova.expect(item).toHaveProperty("name");
nova.expect(item).toHaveProperty("created_at");
nova.expect(item).toHaveProperty("status");
});
});
nova.test("Status values are valid", function() {
const items = nova.response.json().data;
const validStatuses = ["active", "inactive", "pending"];
items.forEach(function(item) {
nova.expect(validStatuses).toContain(item.status);
});
});
Header Validation
Check that the response includes expected headers:
nova.test("Response has correct content type", function() {
const contentType = nova.response.headers.get("Content-Type");
nova.expect(contentType).toMatch(/application\/json/);
});
nova.test("Response includes pagination headers", function() {
nova.expect(nova.response.headers.get("X-Total-Count")).toBeDefined();
nova.expect(nova.response.headers.get("X-Page")).toBeDefined();
});
Request Chaining
One of the most powerful patterns in API automation testing is request chaining -- using the output of one request as the input for the next.
Example: Login and Use Token
Step 1: Create a "Login" request to POST /auth/login. In its post-script:
nova.test("Login successful", function() {
nova.expect(nova.response.status).toBe(200);
});
const data = nova.response.json();
if (data && data.access_token) {
nova.variable.set("access_token", data.access_token);
nova.variable.set("refresh_token", data.refresh_token);
// Store expiry for later checks
const expiresAt = Date.now() + (data.expires_in * 1000);
nova.variable.set("token_expires", String(expiresAt));
nova.log("Tokens stored successfully");
}
Step 2: In all subsequent requests, reference the token. In the URL bar or headers, use
{{access_token}}. Or set it programmatically in a pre-script:
nova.request.setBearerToken(nova.variable.get("access_token"));
Step 3: Create a "Get User Profile" request to GET /users/me. In its post-script, extract more
data:
const user = nova.response.json();
nova.variable.set("user_id", String(user.id));
nova.variable.set("user_email", user.email);
if (user.organization) {
nova.variable.set("org_id", String(user.organization.id));
}
Step 4: Use {{user_id}} and {{org_id}} in downstream requests like
GET /organizations/{{org_id}}/members.
When you run these requests in sequence using RESTK's collection runner, the variables chain automatically from one request to the next.
Collection-Level and Folder-Level Scripts
Scripts are not limited to individual requests. You can attach pre-scripts and post-scripts to collections and folders, and they execute in the hierarchical order described earlier.
Collection Pre-Script
Attach a pre-script to the collection itself to run shared setup logic before every request in that collection:
// Collection pre-script: runs before EVERY request
// Attach the auth token to all requests automatically
const token = nova.variable.get("access_token");
if (token) {
nova.request.setBearerToken(token);
}
// Add a correlation ID for tracing
nova.request.headers.add({
key: "X-Correlation-ID",
value: nova.uuid()
});
Folder Pre-Script
Use folder-level scripts to apply logic to a subset of requests. For example, a folder called "Admin Endpoints" might require an admin token:
// Folder pre-script for "Admin Endpoints"
const adminToken = nova.variable.get("admin_token");
if (adminToken) {
nova.request.setBearerToken(adminToken);
} else {
nova.log("WARNING: admin_token not set. Admin requests will fail.");
}
Folder Post-Script
Run common validation after every request in a folder:
// Folder post-script: validate all responses in this folder
nova.test("Response is JSON", function() {
const contentType = nova.response.headers.get("Content-Type");
nova.expect(contentType).toMatch(/application\/json/);
});
nova.test("No server errors", function() {
nova.expect(nova.response.status).toBeLessThan(500);
});
Skipping Parent Scripts
If a specific request should not inherit collection or folder scripts, check the Skip Parent Scripts option on that request. This is useful for requests like health checks or public endpoints that do not need authentication.
Advanced Patterns
Conditional Request Execution
Use pre-scripts to skip requests based on conditions:
// Pre-script: only run if we have a user to delete
const userId = nova.variable.get("user_id_to_delete");
if (!userId) {
nova.log("No user to delete. Skipping this request.");
// Set a flag that the post-script can check
nova.variable.set("skipped", "true");
}
Data-Driven Testing with Dynamic Variables
Generate dynamic test data for each request:
// Pre-script: generate unique test data
const uniqueId = nova.uuid().substring(0, 8);
nova.request.setJsonBody({
name: "Test User " + uniqueId,
email: "test-" + uniqueId + "@example.com",
external_id: uniqueId,
created_at: nova.isoDate()
});
Error Handling in Scripts
Write resilient scripts that handle unexpected responses gracefully:
nova.test("Response is parseable", function() {
let data;
try {
data = nova.response.json();
} catch (e) {
nova.log("Failed to parse response as JSON: " + e.message);
nova.expect(true).toBe(false); // Force test failure
return;
}
nova.expect(data).toBeDefined();
});
// Handle different status codes
if (nova.response.status === 200) {
const data = nova.response.json();
nova.variable.set("result_id", String(data.id));
} else if (nova.response.status === 429) {
nova.log("Rate limited. Retry after: " +
nova.response.headers.get("Retry-After"));
} else if (nova.response.status >= 500) {
nova.log("Server error: " + nova.response.status);
const error = nova.response.json();
nova.log("Error details: " + JSON.stringify(error));
}
Extracting and Transforming Data
Process response data before storing it:
// Extract the latest order from a list
const orders = nova.response.json().data;
const latestOrder = orders.sort(function(a, b) {
return new Date(b.created_at) - new Date(a.created_at);
})[0];
nova.variable.set("latest_order_id", String(latestOrder.id));
nova.variable.set("latest_order_status", latestOrder.status);
nova.variable.set("latest_order_total", String(latestOrder.total));
Nova vs Postman Scripts: A Comparison
If you are migrating from Postman or evaluating alternatives, here is how the two scripting frameworks compare:
| Feature | Nova (nova.*) | Postman (pm.*) |
|---|---|---|
| Variable get/set | nova.variable.get() / .set() | pm.variables.get() / .set() |
| Environment access | nova.environment.get() / .set() | pm.environment.get() / .set() |
| Test assertions | nova.test() + nova.expect() | pm.test() + pm.expect() |
| Response access | nova.response.json() | pm.response.json() |
| Response status | nova.response.status | pm.response.code |
| Response time | nova.response.responseTime | pm.response.responseTime |
| Request modification | nova.request.headers.add() | pm.request.headers.add() |
| Set bearer token | nova.request.setBearerToken() | Manual header manipulation |
| Set JSON body | nova.request.setJsonBody() | Manual body string manipulation |
| UUID generation | nova.uuid() | Requires external library or $guid |
| Crypto/HMAC | nova.crypto.hmacSha256() | Requires CryptoJS import |
| Timestamp | nova.timestamp() | Date.now() / 1000 |
| Collection auth | nova.collectionAuth.setBearerToken() | Not available |
| Folder auth | nova.folderAuth.setBearerToken() | Not available |
| Script execution | Local | Cloud-connected runtime |
| Privacy | All data stays local | Data may sync to cloud |
Key differences in practice:
- Nova has convenience methods like
setBearerToken(),setJsonBody(), andsetBasicAuth()that reduce boilerplate. In Postman, you manually construct header strings. - Nova includes built-in crypto without needing to import external libraries. Postman requires importing CryptoJS and managing library dependencies.
- Nova supports parent auth manipulation. You can set authentication on the collection or folder
from within a script using
nova.collectionAuthandnova.folderAuth. This is useful for OAuth token refresh flows. - Nova scripts run entirely offline. No cloud connection is required for script execution, variable resolution, or test assertions.
Migrating Postman Scripts
If you import a Postman collection into RESTK, pm.* scripts are automatically preserved and remain
functional. You do not need to rewrite them immediately. Over time, you can migrate to nova.*
syntax to take advantage of the additional convenience methods and built-in capabilities.
How MCP Can Generate Nova Scripts for You
RESTK's local MCP integration adds another dimension to Nova scripting: AI-assisted script generation. Instead of writing every assertion by hand, you can describe what you want in natural language and let your local AI model generate the Nova script.
For example, after receiving a response, you can ask:
"Write tests that verify the response is a paginated list, each item has an id and email, the total count matches the array length, and the response time is under 500ms."
The AI, running locally through MCP, generates:
nova.test("Status is 200", function() {
nova.expect(nova.response.status).toBe(200);
});
nova.test("Response is paginated", function() {
const data = nova.response.json();
nova.expect(data).toHaveProperty("data");
nova.expect(data).toHaveProperty("total");
nova.expect(data).toHaveProperty("page");
});
nova.test("All items have required fields", function() {
const items = nova.response.json().data;
items.forEach(function(item) {
nova.expect(item).toHaveProperty("id");
nova.expect(item).toHaveProperty("email");
});
});
nova.test("Total matches array length", function() {
const data = nova.response.json();
nova.expect(data.total).toBe(data.data.length);
});
nova.test("Response time under 500ms", function() {
nova.expect(nova.response.responseTime).toBeLessThan(500);
});
Because MCP runs locally, the AI sees your actual response data and generates assertions tailored to the real payload structure -- not generic boilerplate. And none of your data leaves your machine during this process.
This is particularly valuable when onboarding to a new API. Import the OpenAPI spec, run a few requests, and let MCP generate a comprehensive test suite based on the actual responses. You can then refine and extend the generated scripts as needed.
Putting It All Together
Here is a complete example of an automated workflow that demonstrates the patterns covered in this guide. This collection tests a user management API:
Collection pre-script (runs before every request):
// Attach auth token to all requests
const token = nova.variable.get("access_token");
if (token) {
nova.request.setBearerToken(token);
}
// Add correlation ID for distributed tracing
nova.request.headers.add({
key: "X-Correlation-ID",
value: nova.uuid()
});
Request 1: Login (POST /auth/login) -- post-script:
nova.test("Login successful", function() {
nova.expect(nova.response.status).toBe(200);
nova.expect(nova.response.json()).toHaveProperty("access_token");
});
const auth = nova.response.json();
nova.variable.set("access_token", auth.access_token);
Request 2: Create User (POST /users) -- pre-script:
const uniqueId = nova.uuid().substring(0, 8);
nova.request.setJsonBody({
name: "Test User " + uniqueId,
email: "test-" + uniqueId + "@example.com"
});
nova.variable.set("test_email", "test-" + uniqueId + "@example.com");
Request 2: Create User -- post-script:
nova.test("User created", function() {
nova.expect(nova.response.status).toBe(201);
});
const user = nova.response.json();
nova.variable.set("created_user_id", String(user.id));
Request 3: Get User (GET /users/{{created_user_id}}) -- post-script:
nova.test("Returns correct user", function() {
const user = nova.response.json();
nova.expect(user.email).toBe(nova.variable.get("test_email"));
});
Request 4: Delete User (DELETE /users/{{created_user_id}}) -- post-script:
nova.test("User deleted", function() {
nova.expect(nova.response.status).toBe(204);
});
Run this entire collection using RESTK's Collection Runner, and all four requests execute in sequence with variables chaining automatically between them. You get a clear test results summary showing which assertions passed and which failed.
Next Steps
This guide covers the core of what Nova scripting offers for API automation testing. To go deeper:
- Read the Scripting Reference for the complete API documentation
- Explore Environments to manage variables across dev, staging, and production
- Browse all RESTK features to see how scripting fits into the full platform
- Learn about the Collection Runner for running automated test suites
- Set up MCP integration for AI-assisted script generation
Download RESTK and start automating your API tests today.
Related reading:
- MCP for API Testing: How AI-Powered Workflows Change Everything
- Environment Variables Best Practices for API Development
Questions about Nova scripting? Join our Discord community or email [email protected] and we will help you get set up.