Blog

Postman - Advanced scripting

This blog post explains how you write advanced JavaScript in Postman which can validate the responses from an API using JsonSchema validation.

🌟 Intro

When using Postman for just sending requests to a REST API, you’re missing the advanced scripting functionality which Postman has to offer.
This article highlights some advanced scripting techniques which extend the validation from requests in Postman.

 

ℹ️ Postman

Postman is a popular API development and testing tool that allows developers to send HTTP requests, inspect responses, and debug APIs efficiently. It also supports features such as automated testing, environment management, API documentation, and team collaboration throughout the API development lifecycle.

Postman’s internal runtime

Postman’s internal runtime is based on Node.js (which means you can use JavaScript). You can use this to write scripts that add dynamic behavior to requests, responses, folders and collections.

Pre-request

Before a request is send to an API, it’s possible to run some extra JavaScript, this can be like setting authentication headers, special headers, or preparing some dynamic values which are used in the request.

Post-response

In addition to executing JavaScript before a request, it’s also possible to run JavaScript after the quest is done. This can be used to do response validation. Like checking the HTTP Status Code, some headers or checking if the response JSON conforms to the correct JSON-Schema.

 

🎯 Goal

The goal is to explain how to use the advanced JavaScript scripting functionality in Postman to be able to allow easy-to-use and reusable code-blocks and functions which can be used to do some generic things like logging, or JSON Response validation.

💡 Use Case: PetStore 3

I’ll use the PetStore 3 as an reference / example API which I would like to test using Postman.

Installation

The PetStore docker image can be found here: here.

Running this example API in Docker can be done with these commands:

docker pull swaggerapi/petstore3
docker run -d -e SWAGGER_HOST=http://petstore.swagger.io -e SWAGGER_URL=http://localhost:8080 -e SWAGGER_BASE_PATH=/v3 -p 8080:8080 swaggerapi/petstore3

When this container is running, you can access the OpenAPI 3.0 Swagger Specification main page, at http://localhost:8080. I’ll use this later to download the openapi.json file to be able to verify the JSON Response Schema.

Building a simple request in Postman

Follow these steps:

  1. Start Postman and login. (You can just use any edition)
  2. Create a new collection named: PetStore API
  3. Add a GET request named Get Pets and use this URL: http://localhost:8080/api/v3/pet/findByStatus?status=available
  4. Send the request

When you send the request, nothing special happens (yet), the GET is executed and the JSON response from the Pet Store 3 API is something like this:

[
    {
        "id": 1,
        "category": {
            "id": 2,
            "name": "Cats"
        },
        "name": "Cat 1",
        "photoUrls": [
            "url1",
            "url2"
        ],
        "tags": [
            {
                "id": 1,
                "name": "tag1"
            },
            {
                "id": 2,
                "name": "tag2"
            }
        ],
        "status": "available"
    }
]

⚡ Basic Scripting

Assertions using pm . *

Postman defines pm.* code which can be used to do test assertions in a Post-response.

Test for a status code

You can use the statusCode property available over pm.response to test for the correct HTTP status code of the response:

pm.test("Status code is 200", function () {
  pm.response.to.have.status(200);
});

This code uses the pm library to run the test method. The text string will appear in the test output. The function inside the test represents an assertion. In this case, the code uses a to.have chain to express the assertion. Postman includes the Chai Assertion Library by default in its JavaScript sandbox. This allows you to write clean, human-readable tests using Chai’s expect or assert syntax to validate your API responses directly in the “Tests” tab.

This test checks the response code returned by the API. If the response code is 200, the test will pass, otherwise it will fail.

Postman Statuscode
 

  1. Write here (Post-response section) the code
  2. After sending this request, you see that 1/1 test passed. Which is correct because there is only 1 test defined.
  3. This displays the details of a test, in this case, asserting if the status code is indeed 200

💎 Advanced Scripting

In this blogpost, I want to dive deeper into some advanced features in Postman with JavaScript which can be used to build reusable validation and implement code which can be used to validate the response.

Testing for valid JSON Schema

Postman supports JSON schema validation to check if the response JSON conforms to the schema.

pm.test('Schema validation', () => {
    pm.response.to.have.jsonSchema(schema);
});

The schema used here should follow the spec defined at json-schema.org.

In order to get the schema for this specific request (/pet/findByStatus), you can go to the Swagger UI page from the PetStore 3 project.
This looks like this:

 

Now open the openapi.json and use this appℹ️ to analyze this openapi.json and generate the response JSON Schema for the request (/pet/findByStatus):

const pet_findByStatus_200Schema = {
  "type": "array",
  "items": {
    "required": [
      "name",
      "photoUrls"
    ],
    "type": "object",
    "properties": {
      "id": {
        "type": "integer",
        "example": 10
      },
      "name": {
        "type": "string",
        "example": "doggie"
      },
      "category": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "name": {
            "type": "string",
            "example": "Dogs"
          }
        },
        "xml": {
          "name": "category"
        }
      },
      "photoUrls": {
        "type": "array",
        "xml": {
          "wrapped": true
        },
        "items": {
          "type": "string",
          "xml": {
            "name": "photoUrl"
          }
        }
      },
      "tags": {
        "type": "array",
        "xml": {
          "wrapped": true
        },
        "items": {
          "type": "object",
          "properties": {
            "id": {
              "type": "integer"
            },
            "name": {
              "type": "string"
            }
          },
          "xml": {
            "name": "tag"
          }
        }
      },
      "status": {
        "type": "string",
        "description": "pet status in the store",
        "enum": [
          "available",
          "pending",
          "sold"
        ]
      }
    },
    "xml": {
      "name": "pet"
    }
  }
};

ℹ️ I used GitHub Copilot to generate a simple Blazor Static WASM App which runs on Github Pages.

Create utility script

Now that I have the basic JSON Schema validation in place, I want to make this more generic.

>> I want to able to just call a function in the Post-response to validate this schema, this is because there are multiple requests which return the same schema:
/pet/findByStatus?status=available, /pet/findByStatus?status=pending and /pet/findByStatus?status=sold.

This can be done by defining a utility class in the Pre-request from the Collection like this:

function petstoreUtils() {
    const petsSchema = {
        // Schema here ...
    };

    function verifyGetPetsReponseJsonSchema() {
        pm.test('Verify that the Get Pets Response is valid', function () {
            pm.expect(pm.response.to.have.jsonSchema(petsSchema));
        });
    }

    return {
        verifyGetPetsReponseJsonSchema
    };
}

pm.collectionVariables.set('petstore-utils', petstoreUtils.toString());

This does the following:

  • Define a function named petstoreUtils, which acts like a utility class.
  • Create a function named verifyGetPetsReponseJsonSchema which uses the petsSchema for JSON Schema validation. Also make sure that this function is exported, so it can be used in the Post-response step.
  • Store the string representation from this utility class as a variable in the Collection.

Use the utility script

In order to use this script in the Post-response step, you need to define this code in that Post-response step:

const petstoreUtils = eval(`(${pm.collectionVariables.get('petstore-utils')})()`);

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

petstoreUtils.verifyGetPetsReponseJsonSchema();

This does the following:

1. Get the petstoreUtils

The most complex line in this Postman post-response script is:

const petstoreUtils = eval(`(${pm.collectionVariables.get('petstore-utils')})()`);

This line dynamically loads reusable JavaScript utility functions from a Postman collection variable, executes the stored code, and assigns the returned object to the petstoreUtils constant.

a. Retrieving the Collection Variable

The first part retrieves the value of the petstore-utils collection variable:

pm.collectionVariables.get('petstore-utils')

Postman collection variables store values as text. In this case, the variable contains JavaScript code as a string.
For example, the petstore-utils collection variable could contain something similar to:

function () {
    return {
        verifyGetPetsReponseJsonSchema: function () {
            // Response validation code
        }
    };
}

b. Using a JavaScript Template Literal
The retrieved JavaScript code is inserted into a template literal:

`(${pm.collectionVariables.get('petstore-utils')})()`

Template literals use backticks (`) instead of regular single or double quotes. The ${...} syntax allows a JavaScript expression to be evaluated and inserted into the resulting string.

After the collection variable is inserted, the generated JavaScript string effectively looks like this:

(function () {
    return {
        verifyGetPetsReponseJsonSchema: function () {
            // Response validation code
        }
    };
})()

c. Why Is the Function Wrapped in Parentheses?
The parentheses around the function cause JavaScript to interpret it as a function expression:

(function () {
    // Code
})

This is important because the function needs to be treated as a value that can immediately be executed.

d. What Does the Final () Do?
The final pair of parentheses executes the function immediately:

(function () {
    return {
        // Utility functions
    };
})()

This pattern is commonly known as an IIFE, which stands for Immediately Invoked Function Expression.

The function is defined and immediately executed. The object returned by the function becomes the result of the expression.

e. What Does eval() Do?
The eval() function takes a string containing JavaScript code and executes that string as actual JavaScript:

eval("1 + 2");

In the Postman script, eval() executes the JavaScript code retrieved from the collection variable:

eval(`(${pm.collectionVariables.get('petstore-utils')})()`);

The execution flow can be summarized as follows:

  1. Retrieve the petstore-utils collection variable.
  2. Insert the stored JavaScript code into a template literal.
  3. Wrap the function in parentheses so it is treated as a function expression.
  4. Add () to immediately execute the function.
  5. Use eval() to execute the complete JavaScript expression.
  6. Assign the returned utility object to petstoreUtils.

f. Using the Returned Utility Object
Once the utility function has been executed, its returned object is stored in the petstoreUtils constant:

const petstoreUtils = eval(`(${pm.collectionVariables.get('petstore-utils')})()`);

The methods exposed by that object can then be called elsewhere in the Postman script:

petstoreUtils.verifyGetPetsReponseJsonSchema();

In this example, verifyGetPetsReponseJsonSchema() is a reusable function responsible for validating the JSON response returned by the Get Pets API request.

Summary
In simple terms, this line acts as a mechanism for loading reusable JavaScript utilities stored in a Postman collection variable:

const petstoreUtils = eval(`(${pm.collectionVariables.get('petstore-utils')})()`);

The collection variable contains JavaScript code as text, eval() executes that code, the () immediately invokes the stored function, and the returned utility object is assigned to petstoreUtils.

⚠️ Important: eval() executes JavaScript dynamically and can introduce security risks when used with untrusted input. In this scenario, the code should only come from a trusted and controlled Postman collection variable.

2. Call the function

Now just call the function verifyGetPetsReponseJsonSchema to verify the response.

 

👉 newman / Postman CLI

Once you have build a collection using this advanced scripting, it’s also possible to run this in a CI/CD pipeline using:

newman

Newman is the original open-source command-line collection runner for Postman, built on Node.js, that executes exported Postman collections (JSON files) directly from the terminal without needing the Postman app. It’s commonly used in CI/CD pipelines for automated API testing, supports environment/globals files and various reporters (CLI, JSON, JUnit, HTML),

Postman CLI

Postman CLI (Newman-based, but the newer official CLI) lets you run and test Postman collections directly from the command line without opening the Postman app—useful for automating API tests in CI/CD pipelines. It supports running collections, environments, and monitors, and can output results in various formats (JSON, JUnit, etc.) while also integrating with the Postman API to sync test results back to your workspace.

 

📌 Conclusion

In this post I showed how Postman is much more than just a tool to send HTTP requests. Thanks to its Node.js based scripting runtime, you can add real logic to your requests, pre- and post-response.

I started with a simple example: testing the PetStore 3 API and asserting a 200 status code. From there I moved on to something more useful: validating the JSON response against a schema generated from the openapi.json spec.

The interesting part is when you have multiple requests returning the same schema (like /pet/findByStatus). Instead of copy-pasting the same JSON response validation code everywhere, I stored a reusable utility function as a Collection Variable, and loaded it back in with eval() combined with an IIFE.

Note that you are not limited by just defining JSON Schema validation code in the utility class, any complex code which is generic can be stored here.

A bit hacky maybe, but it works well and keeps your Post-response scripts clean.

>> This is exactly the kind of pattern I like: write it once, reuse it everywhere in the collection.

Once your collection and tests are in place, you can move forward by using newman and the newer Postman CLI to run your collections from the command line, which makes it straightforward to plug into a CI/CD pipeline.

So, from a single manual GET request to a fully automated, schema-validated test suite running in your pipeline, that’s the power of Postman’s scripting capabilities.

 


 

📝 Notes

Some content in this blog is created with the help of an AI. I did review and revise the content where needed.

Written by: Stef Heyenrath

Stef started writing software for the Microsoft .NET framework in 2007. Over the years, he has developed into a Microsoft specialist with experience in: backend technologies such as .NET, NETStandard, ASP.NET, Ethereum, Azure, and other cloud providers. In addition he worked with several frontend technologies such as Blazor, React, Angular, Vue.js.

He is the author from WireMock.Net.

Mission: Writing quality and structured software with passion in a scrum team for technically challenging projects.

Want to know more about our experts? Contact us!