Postman and Pre-Request scripts for Token-based APIs

One of the major issues I have had with using something like Postman to test APIs is the need for an API key before being able to send requests to an API.  In the past, this has usually involved logging in to the API using some sort of “get token” endpoint, copying the token from the response body, and pasting it in to the Authorization section of all the Postman requests I wanted to use.  Slow, annoying, and often caused issues when I accidentally used a development token on a production endpoint, and vice versa.

I decided to do a bit of a deep dive into really setting up Postman to make it quick and painless to hit an endpoint for testing/debugging/general dev, instead of avoiding using Postman at all because of the number of steps needed to perform a simple request.

To begin with, Postman allows us to define Workspaces, Collections, Environments, as well as variables for each scope.  Postman also allows us to run scripts before and after requests, allowing us to hook into the request lifecycle and perform tasks – like getting a valid token, perhaps?

To start, I wanted to be able to switch environments (URLs, credentials, etc.) but use the same set of endpoints.  To accomplish this, I created a new Workspace (you’ll have to log in to do this) for each project I was working on.  You can set it to individual or team depending on your needs.

Within the newly created workspace, I created my first (and likely only) collection – a collection is just that, a collection of requests.  For most cases, this seems an almost unnecessary level of organization, but if perhaps you have a large project with multiple APIs or other services, you can organizes them – e.g., API, web routes, caching server, Redis, etc.

Once we have defined a collection, we need to create some environment variables.  We can use environment variables anywhere within Postman to inject the variable value.  Because we define them on the environment level, we can also switch environments using the environment drop-down and automatically have all those values changed to the new environment’s settings.

I’ll create two environment, Dev and Production, and define a set of variables for each one.  For my uses, I need a base URL, a username, a password, a token, and a token expiration.  I’ll go ahead and set initial values for the base URL, username, and password, since these are going to remain persistent.  I’ll leave token and expiry blank, since we will write a script to automatically set those later.

Once we have the two environments set up, we need to make our first request.  When creating a request, we can substitute our variables for any parameters or properties of the request.  In my case, I will use {{baseUrl}} instead of hard coding a URL (this allows me to swap dev and prod environments without needing separate requests for each).  I’ll also go into Authorization, select ‘Bearer Token’, and then use the {{token}} variable here.

Once my request is set up, it’s time for the last step – right now our token is empty.  We are going to create a pre-request script that will check the current value of {{token}} and whether or not {{tokenExpiry}} has passed, and if so, will hit the token endpoint of this API and save the token and expiry.  Once done, the request will proceed as normal and should be authenticated.

Pre-request scripts are written in Javascript (this is an Electron App, after all).  Postman provides an API for accessing variables and performing some basic tasks, like HTTP requests.  Check out the documentation for more.

Here’s the script I used to accomplish this:

const echoPostRequest = {
  url: pm.variables.get('baseUrl') + '/Token',
  method: 'POST',
  header: 'Content-Type:application/json',
  body: {
    mode: 'urlencoded',
    urlencoded: [
        { key: 'username', value: pm.variables.get('username')},
        { key: 'password', value: pm.variables.get('password')},
        { key: 'grant_type', value: 'password'}
    ]
  }
};

console.log(echoPostRequest);

var getToken = true;

if (!pm.environment.get('tokenExpiry') || 
    !pm.environment.get('token')) {
    console.log('Token or expiry date are missing')
} else if (pm.environment.get('tokenExpiry') <= (new Date()).getTime()) {
    console.log('Token is expired')
} else {
    getToken = false;
    console.log('Token and expiry date are all good');
}

if (getToken === true) {
    console.log(echoPostRequest);
    pm.sendRequest(echoPostRequest, function (err, res) {
    console.log(err ? err : res.json());
        if (err === null) {
            console.log('Saving the token and expiry date')
            var responseJson = res.json();
            pm.environment.set('token', responseJson.access_token)
    
            var expiryDate = new Date();
            expiryDate.setSeconds(expiryDate.getSeconds() + responseJson.expires_in);
            pm.environment.set('tokenExpiry', expiryDate.getTime());
        }
    });
}

It boils down to checking the value of token, then checking it’s expiration.  Postman allows us to access variables of different scopes using the `pm` object – variable for local scope, environment for environment scope, and global for global scope.

If we need to get a token, we use the request object constructed at the top to perform the request, then, depending on how the response payload is formatted, we parse and extract the token and expiration and save it.

To use the script, go to your newly created collection and edit it – on the edit screen, there’s a tab for Pre-request Scripts which has a space to write/paste in code.

After the pre-request script runs, the actual request will run, substituting values for all those variable placeholders.  if everything went well, your request should be authorized and go through.  To debug, you can open up the Postman console and view the log output (you can use console.log() in your pre-request script to show output here).

Make sure the correct environment is set and run the request, and you should see your pre-request script running, setting the token, and then your request going through successfully authorized.