Free Ebook cover Postman for API Testing: Collections, Environments, and Automated Checks

Postman for API Testing: Collections, Environments, and Automated Checks

New course

10 pages

Environments in Postman: Testing Across Dev, Staging, and Production Safely

Capítulo 4

Estimated reading time: 9 minutes

+ Exercise

1) Environment creation and variable mapping

An environment in Postman is a named set of variable values that represent a specific deployment (for example, dev, staging, production). The goal is to run the same requests and the same collection logic against different systems by changing only the environment selection, not the request definitions.

Create an environment for each deployment

  • Create one environment named Dev and another named Staging (you can add Production later, but keep it locked down).
  • In each environment, define the same variable names so requests can be identical across deployments.

Recommended variable map

Use a consistent naming scheme across environments. Typical variables include:

  • baseUrl: root URL for the API (differs per environment).
  • username, password: placeholders for credentials (values differ; often secrets).
  • token: bearer token or session token (usually set at runtime).
  • tenantId or accountId: if the API is multi-tenant (may differ).
  • safeMode: a guard flag (e.g., true/false) used to block risky calls in production.

Map variables into requests

In requests, reference environment variables instead of hardcoding. Example patterns:

  • URL: {{baseUrl}}/v1/users
  • Authorization header: Authorization: Bearer {{token}}
  • Login body: {"username":"{{username}}","password":"{{password}}"}

Keep the variable names identical across environments; only the values change per environment.

2) Switching environments and validating which values are active

Switching environments changes which values resolve when Postman renders {{...}} variables. The most common failure mode is thinking you are on one environment while actually running another, so build quick checks into your workflow.

Continue in our app.

You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.

Or continue reading below...
Download App

Download the app

Switch environments intentionally

  • Select the active environment from the environment dropdown before running a request or collection.
  • When using the Collection Runner, confirm the selected environment in the runner settings.

Validate active values inside requests

Add lightweight checks that confirm the request is hitting the expected deployment.

  • Check the resolved URL in the Postman console (useful when debugging).
  • Assert on a response field that differs by environment (e.g., environment, build, version), if your API provides it.

Example test that validates you are not accidentally hitting production (adapt to your API):

pm.test("Not calling production", function () {  const baseUrl = pm.environment.get("baseUrl") || "";  pm.expect(baseUrl.toLowerCase()).to.not.include("prod");});

If your API has a health endpoint that returns metadata, add a request like GET {{baseUrl}}/health and assert on environment markers:

pm.test("Health indicates expected environment", function () {  const json = pm.response.json();  pm.expect(json).to.have.property("env");  // Example: env is "dev" or "staging"  pm.expect(["dev","staging"]).to.include(json.env);});

3) Separating secrets from non-secrets and reducing accidental production calls

Environment variables often mix non-sensitive configuration (like baseUrl) with sensitive values (like passwords or API keys). Treat them differently to reduce risk and prevent accidental production-impacting calls.

Separate secrets from non-secrets

  • Keep non-secrets in the environment: baseUrl, tenantId, feature flags, and safe guard variables.
  • Keep secrets out of shared environments whenever possible: passwords, long-lived API keys, private tokens.
  • Prefer short-lived tokens generated at runtime (login request sets {{token}}) instead of storing long-lived secrets.

Use guardrails to reduce accidental production calls

Even with correct variables, accidents happen (wrong environment selected, copied URL, or a runner configured incorrectly). Add explicit safety checks for requests that can mutate data (POST/PUT/PATCH/DELETE).

Example pre-request script guard: block destructive calls if the environment is production or if a safety flag is not enabled.

const baseUrl = (pm.environment.get("baseUrl") || "").toLowerCase();const safeMode = pm.environment.get("safeMode");const method = pm.request.method;const isWrite = ["POST","PUT","PATCH","DELETE"].includes(method);if (isWrite) {  // Block if baseUrl looks like production or safeMode is not explicitly true  const looksProd = baseUrl.includes("prod") || baseUrl.includes("production");  if (looksProd || safeMode !== true) {    throw new Error("Write request blocked: enable safeMode=true and avoid production baseUrl.");  }}

Operationally, keep production environments read-only for most users: only include baseUrl and allow GET requests, or require a deliberate opt-in variable like allowWrites that defaults to false.

4) Using initial vs current values conceptually for safe sharing

Postman environment variables have two value slots: Initial and Current. Conceptually:

  • Initial value: the shareable baseline configuration. Think “what teammates can safely see and sync.”
  • Current value: your local, private runtime value. Think “what changes during runs (tokens) or what I don’t want to share (secrets).”

Safe sharing pattern

  • Put non-sensitive defaults in Initial (e.g., baseUrl for dev/staging, tenantId, safeMode).
  • Leave secrets blank in Initial and set them only in Current (e.g., password, apiKey).
  • Store short-lived tokens in Current only, and refresh them automatically when needed.

Example: a login request sets the token into the environment (current value) after a successful authentication:

// In Tests tab of the login requestconst json = pm.response.json();pm.test("Login succeeded", function () {  pm.expect(pm.response.code).to.be.oneOf([200,201]);  pm.expect(json).to.have.property("token");});pm.environment.set("token", json.token);

This approach keeps the environment shareable without leaking credentials, while still enabling automation.

5) Troubleshooting environment-related failures (wrong URL, outdated tokens)

When a request fails after switching environments, the cause is often not the API itself but a mismatch in environment values. Use a consistent checklist to isolate the issue quickly.

Failure: wrong URL or wrong deployment

Symptoms: DNS errors, 404 on known endpoints, unexpected response schema, or hitting a different version of the API.

Checks:

  • Verify the active environment selection (dropdown and runner settings).
  • Inspect the resolved URL: confirm {{baseUrl}} is correct for that environment.
  • Confirm the base path and version segment (e.g., /v1) matches that deployment.
  • If the API exposes a metadata endpoint, call it and assert on env/version.

Fix: update baseUrl in the correct environment; ensure both environments have the same variable names (missing variables often resolve to blank and create malformed URLs).

Failure: outdated or invalid token

Symptoms: 401/403 responses after switching environments, or after some time has passed.

Checks:

  • Confirm the token was generated for the same environment you are calling (dev token won’t work on staging).
  • Check token expiry behavior; some systems invalidate tokens quickly.
  • Verify the Authorization header is using {{token}} and that token exists in the active environment.

Fix: re-run the login/auth request for the active environment and overwrite token in that environment’s current value. If you have multiple auth flows, ensure the correct one runs per environment.

Failure: credentials placeholders not set

Symptoms: login request fails (400/401), or token is never set.

Checks:

  • Confirm {{username}} and {{password}} are populated in the active environment’s current values.
  • Ensure you did not accidentally sync secrets into initial values; keep them local.

Fix: set current values locally; keep initial values blank for secrets.

Failure: environment variable drift

Symptoms: teammates get different results, or your run differs from yesterday’s run.

Checks:

  • Compare variable names across environments; ensure no typos (baseURL vs baseUrl).
  • Look for stale current values (old tokens, old tenant IDs).
  • Reset current values for non-secrets if needed to match initial values.

Fix: standardize variable names, and adopt a routine: non-secrets in initial values, runtime values in current values, and refresh tokens via auth requests.

Guided lab: Run the same requests against Dev and Staging

This lab configures two environments and proves that identical requests behave differently based on environment values, while keeping the request definitions unchanged.

Lab setup: create two environments

  • Create environment Dev with variables: baseUrl, username, password, token, safeMode.
  • Set baseUrl initial value to your dev API root (example: https://dev.api.example.com).
  • Set safeMode initial value to true.
  • Leave token initial value blank.
  • Set username/password in current values only.
  • Create environment Staging with the same variable names.
  • Set baseUrl initial value to your staging API root (example: https://staging.api.example.com).
  • Set safeMode initial value to true.
  • Leave token initial value blank.
  • Set username/password in current values only (staging credentials may differ).

Lab requests: build an identical mini-flow

Create (or reuse) a small set of requests inside a collection. Do not hardcode any environment-specific values.

Request A: Health/Info check

Request: GET {{baseUrl}}/health (or your API’s equivalent).

Tests: verify the endpoint responds and capture an environment marker if available.

pm.test("Health is reachable", function () {  pm.expect(pm.response.code).to.equal(200);});let json = {};try { json = pm.response.json(); } catch (e) {}if (json && json.env) {  pm.environment.set("reportedEnv", json.env);}

Request B: Login/Auth to obtain token

Request: POST {{baseUrl}}/auth/login (adjust path/body to your API). Body uses placeholders:

{  "username": "{{username}}",  "password": "{{password}}"}

Tests: store token in the active environment.

pm.test("Auth succeeded", function () {  pm.expect(pm.response.code).to.be.oneOf([200,201]);});const json = pm.response.json();pm.environment.set("token", json.token);

Request C: Authenticated read request

Request: GET {{baseUrl}}/v1/me (or any endpoint that returns user/account data).

Header: Authorization: Bearer {{token}}

Tests: verify the call is authorized and capture a field that should differ between dev and staging (for example, tenant ID, user ID range, or a banner field).

pm.test("Authorized", function () {  pm.expect(pm.response.code).to.equal(200);});const json = pm.response.json();if (json && json.tenantId) {  pm.environment.set("observedTenantId", json.tenantId);}

Request D (optional): A guarded write request

Request: POST {{baseUrl}}/v1/items with a simple payload. Add the guard pre-request script shown earlier to prevent unsafe execution if safeMode is not enabled or if the base URL looks like production.

Purpose: practice safety constraints and confirm your guardrail works.

Run the lab against Dev

  • Select the Dev environment.
  • Run Request A, then B, then C (and D if included).
  • Confirm {{token}} is set in Dev current values after login.
  • Record any environment marker from the response (for example, reportedEnv or observedTenantId).

Run the same lab against Staging (no request changes)

  • Switch to the Staging environment.
  • Run the exact same requests in the same order.
  • Confirm a new token is stored in Staging current values (separate from Dev).
  • Compare the captured markers (for example, reportedEnv should differ, or returned IDs/data should reflect staging).

Verification checklist

  • The request URLs remain identical except for {{baseUrl}} resolution.
  • Dev and Staging tokens are different and stored in their respective environments.
  • At least one response attribute differs between environments (env marker, dataset differences, or version/build).
  • Guarded write requests are blocked unless safeMode is explicitly enabled, and they never run against a production-looking base URL.

Now answer the exercise about the content:

When you want to run the same Postman collection against Dev and Staging without changing any request definitions, which approach best achieves this safely?

You are right! Congratulations, now go to the next page

You missed! Try again.

Using separate environments with identical variable names lets the same requests run unchanged while only the selected environment’s values (like baseUrl and token) change. Switching environments intentionally reduces mistakes and supports safer testing across deployments.

Next chapter

Authentication Patterns in Postman: API Keys, Bearer Tokens, and OAuth Basics

Arrow Right Icon
Download the app to earn free Certification and listen to the courses in the background, even with the screen off.