Your Postman collection calls the token endpoint once per request in a run. How do you fix that?
answer
- Find the declaration before changing anything
- Three places an event can hang
- Narrow the scope, or make repeats cheap
- Subtract a margin from the stored expiry
- Hand-writing the header is not a fix
basics
~20 sFind the prerequest event that fetches the token; on the collection it is gathered for every item. Move it down to the folder that holds the authenticated requests, or guard it with a cached token and a stored expiry.
solid answer
~40 sLocate the `event` first. A per-request token call is almost always a `listen: "prerequest"` entry on the collection, because that is the placement whose listeners are gathered for every item in the run. Then pick a fix. **Relocate**: move the entry to the folder that actually holds the authenticated requests — declarative, with no code that can be wrong. **Guard**: keep it on the collection and cache the token plus an expiry in a scope, skipping the call while the value is still fresh, with a margin subtracted so nothing expires between the check and the send. What you must not do is hand-write the `Authorization` header instead: the `oauth2` handler's `sign` removes that header, ignoring case, before writing its own.
code
javascript · 11 linesconst token = pm.collectionVariables.get('access_token'),
expiry = Number(pm.collectionVariables.get('access_token_expiry'));
if (!token || Date.now() >= expiry - 30000) {
pm.sendRequest({ url: pm.environment.get('token_url'), method: 'POST' }, function (err, res) {
if (err) { return; }
const body = res.json();
pm.collectionVariables.set('access_token', body.access_token);
pm.collectionVariables.set('access_token_expiry', Date.now() + body.expires_in * 1000);
});
}go deeper
Recall that a pre-request script's placement decides which requests it runs for, and that the collection is the widest of the placements available.
Walk through locating the event on the collection, a folder or the request, and explain why an ancestor's listener means one fetch per item rather than one per run.
Choose between relocating and guarding for the collection in front of you, write the expiry margin correctly, and say how you would verify the fix from the run's own request history.
Set the convention: which suites get a folder-scoped fetch, which get a guarded collection-level one, and how a shared identity service is protected from suites growing past it.
## Confirm where the fetch is declared Before changing anything, find the `event`. A token call fired once per request is nearly always a `prerequest` entry on the **collection**, because that is the only placement whose listeners are gathered for every item in the run. Check, in order: 1. The collection document's own `event` array for a `listen: "prerequest"` entry. 2. Each folder above the affected requests, since `item-group` declares an `event` array too. 3. The requests themselves, which can declare their own. If the call is on the collection, every item's pre-request work includes it, and the count you are seeing is exactly the number of requests in the run. ## Why relocating is the first move | Approach | What it changes | Cost | |---|---|---| | move the `event` to a folder | the script runs only for requests inside that folder | none — declarative, no code to get wrong | | guard the script in place | the script still runs, but calls out only when the cached value is stale | a cache key and an expiry to maintain | | set the header per request | removes the auth block from the picture entirely | loses `addTokenTo`, and the handler deletes the header anyway | That third row is a trap worth naming: the `oauth2` handler's `sign` calls `request.removeHeader('Authorization', { ignoreCase: true })` before it adds its own, so hand-writing the header *while an auth block is still declared* is not a fix — the handler discards it. ## Fix one: move the event down If only part of the collection is authenticated, move the `prerequest` entry from the collection to the folder that holds those requests. Nothing else changes: the auth block still declares `accessToken` as `{{access_token}}`, and the runtime still resolves that into the auth before the handler signs. You have narrowed *which* items trigger the fetch, not *how* the fetch is consumed. ## Fix two: guard the fetch When the collection really is uniformly authenticated, leave the script where it is and make repeat executions cheap. Cache the token and its expiry in the same scope, and skip the call while it is still valid: ```javascript const token = pm.collectionVariables.get('access_token'), expiry = Number(pm.collectionVariables.get('access_token_expiry')); if (!token || Date.now() >= expiry - 30000) { pm.sendRequest({ url: pm.environment.get('token_url'), method: 'POST' }, function (err, res) { if (err) { return; } const body = res.json(); pm.collectionVariables.set('access_token', body.access_token); pm.collectionVariables.set('access_token_expiry', Date.now() + body.expires_in * 1000); }); } ``` Three details make this hold up: - **Subtract a margin from the expiry.** A token that expires between the check and the send fails the request it was fetched for. - **Write into a scope the run still holds at request time.** The value has to survive the script's own execution to be there when the runtime resolves the auth. - **Use the same key the auth block names.** The handler bails only on a falsy `accessToken`; an unsubstituted placeholder is a non-empty string, so a key typo produces a signed-but-refused request rather than an obvious error. ## What to verify afterwards - The run's request history shows the token endpoint once, not once per request. - A request early in the run and a request late in the run both succeed, which proves the cached value is still being read and not merely written once and lost. - A deliberately short expiry still triggers a refetch mid-run, which proves the guard is a guard and not a permanent skip. ## The part that stays constant Neither fix touches the join. The script's contract is "put a usable value under this name"; the auth block's contract is "sign with whatever is under this name". The runtime resolves variables into the auth immediately before signing, so as long as the name matches and the value is fresh, where the script hangs and how often it calls out are pure operational choices. Treat them as such: relocation when the authenticated area is a subtree, a guard when it is the whole collection, and both when it is a large subtree.
- Why subtract a margin from the stored expiry rather than comparing against it directly?Because the check and the send are not simultaneous. Variables are resolved into the auth and the request is then dispatched, so a token that was valid at the moment of the check can be expired by the time the server evaluates it. Subtracting a margin makes the guard refetch slightly early, which costs one extra call and removes a whole class of flaky failures.
- After the fix, how do you prove the guard is a guard and not a permanent skip?Set a deliberately short expiry and confirm a refetch happens partway through the run, then confirm requests early and late in the run both succeed. The first proves the stale branch still fires; the second proves the cached value is being read on every request and not written once and lost.
saying these in an interview costs you the question
- Hand-writes the Authorization header while an auth block is still declared
- Caches the token with no expiry, so a long run fails midway
- Compares against the expiry with no safety margin
- Changes the auth block when the problem is the script's placement
- Fixes it by disabling the event, so nothing gets signed