Collection variables are a powerful feature in Postman for managing dynamic data in your API requests, but their scoping and override behavior can be a bit tricky.
Let’s see how they work in practice. Imagine you have a Postman collection for a simple user management API.
{
"info": {
"_postman_id": "...",
"name": "User API",
"description": "API for managing users",
"schema": "..."
},
"item": [
{
"_postman_id": "...",
"name": "Get Users",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/users",
"host": [
"{{baseUrl}}"
],
"path": [
"users"
]
}
},
"response": []
},
{
"_postman_id": "...",
"name": "Create User",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"username\": \"{{newUsername}}\",\n \"email\": \"{{userEmail}}\"\n}"
},
"url": {
"raw": "{{baseUrl}}/users",
"host": [
"{{baseUrl}}"
],
"path": [
"users"
]
}
},
"response": []
}
],
"variable": [
{
"key": "baseUrl",
"value": "http://localhost:3000",
"type": "string"
},
{
"key": "userEmail",
"value": "test@example.com",
"type": "string"
}
]
}
In this collection, we have two variables defined at the collection level: baseUrl and userEmail. The Get Users and Create User requests both use {{baseUrl}}. The Create User request also uses {{userEmail}} in its request body.
Now, let’s consider how Postman resolves these variables. Postman has a hierarchy for variable resolution, often referred to as "scope." The order of precedence, from highest to lowest, is:
- Global Variables: Defined in Postman’s Global scope.
- Environment Variables: Defined within a specific Postman Environment.
- Data Variables: Used in collection runs with external data files.
- Collection Variables: Defined directly on the collection.
- Folder Variables: Defined on a folder within a collection.
- Local Variables: Defined within a specific request (e.g., using
pm.variables.set()in a pre-request script).
When Postman encounters a variable like {{baseUrl}}, it searches for its value starting from the highest scope and moving down. The first place it finds a matching key, it uses that value.
What’s really interesting is how this hierarchy allows for powerful overrides. You can set a baseUrl at the collection level, say http://localhost:3000. Then, if you create an environment (e.g., "Staging") and define baseUrl as https://staging.api.example.com within that environment, Postman will use the Staging environment’s baseUrl when that environment is active. The collection variable is effectively overridden.
This is crucial for testing across different environments (development, staging, production) without modifying your collection. You can have one collection and multiple environments, each with its own set of variable values for things like hostnames, API keys, or user credentials.
Here’s a real-world scenario: You’re developing an API and want to test it locally. Your collection has baseUrl: http://localhost:8080. You then deploy to a staging server. You create a "Staging" environment in Postman with baseUrl: https://staging.example.com. When you select the "Staging" environment, any request in your collection that uses {{baseUrl}} will automatically point to your staging server.
The userEmail variable in our example could also be overridden. If you set a global variable userEmail to admin@example.com, that global value would be used instead of the collection’s test@example.com unless you had a more specific override (like an environment variable for userEmail).
You can also define variables dynamically within your scripts. For instance, in a pre-request script for the Create User request, you could set a local variable:
// Pre-request Script for Create User
let uniqueId = Math.random().toString(36).substring(7);
pm.variables.set("newUsername", "user_" + uniqueId);
This newUsername variable, defined locally within the request’s pre-request script, would have the highest precedence for that specific request’s execution. It would override any newUsername variable defined at the collection, environment, or global level for that single request run.
The one nuance that trips many people up is that when you’re viewing variables in the Postman UI, you see the currently resolved value. If you’re in an active environment that overrides a collection variable, the collection variable editor will still show the original value defined in the collection, but the request will use the environment’s value. To see the effective values, you often need to inspect the variable values during a request run, or look at the active environment’s settings.
Understanding this scope and override hierarchy is key to effectively managing your API testing workflows and ensuring your requests target the correct endpoints with the right configurations. The next logical step is to explore how these variables interact with Postman’s "Runner" feature and data files for large-scale, parameterized testing.