Search code examples
javascriptreactjshttpsuperagent

Dynamically Set header for all requests ( React, super-agent )


I would like an elegant way to dynamically set a Authentication header on all requests. My current solution is using a superagent-defaults package but it dooesn't handle dynamic headers

Take the sample code below

superagentWrapper.js

import defaults from 'superagent-defaults';
import localStorage from 'localStorage';

const superagent = defaults();

superagent
  .set('Authorization', `Bearer ${localStorage.getItem('access_token')}`);

export default superagent;

api.js

  import request from '../../../utils/superagentWrapper';

  ExampleAuthenticatedCall: (x) => {
    return new Promise((resolve, reject) => {
      request
     .post(sources.exampleCall())
     .accept('json')
     .set('Content-Type', 'application/json')
     .timeout(40000)
     .send({ exampleCall: "xxx"})
     .end((error, res) => {
        res.body.error ? reject(res.body.error) : resolve(res.body);
     });
    });
  },

So the issue is that my superAgentWrapper is required by all my api files at the time of page load. This means that the example works fine as long as the access_token never changes ( Which it does do potentially multiple times before a page refresh ).

I've found a solution to this issue here https://www.crowdsync.io/blog/2017/10/16/setting-defaults-for-all-your-superagent-requests/.

Whilst this could potentially work an ideal solution would be to dynamically mirror all the methods the request library has rather than manually having to define them ( new methods may be added / removed in the future so this approach is a little brittle ).

Perhaps anyone could point me in the right direction of how this might be possible perhaps by using proxies / reflection?


Solution

  • Use the standard superagent, and instead of returning the same request, return a function that generates a new request, and gets the current token from the localStorage:

    import request from 'superagent';
    import localStorage from 'localStorage';
    
    const superagent = () => request
      .set('Authorization', `Bearer ${localStorage.getItem('access_token')}`);
    
    export default superagent;
    

    Usage (see request and promises):

    import request from '../../../utils/superagentWrapper';
    
    AuthenticatedCall: (x) => request() // generate a new request
      .get(sources.AuthenticatedCall(x)) // a request returns a promise by default
    

    If you don't won't to convert the wrapper to a function, you can use a proxy with a get handler. The proxy will produce a new request, with the current token, whenever it's called:

    import request from 'superagent';
    import localStorage from 'localStorage';
    
    const handler = {
      get: function(target, prop, receiver) {
        return request
          .set('Authorization', `Bearer ${localStorage.getItem('access_token')}`)[prop];
      }
    };
    
    const requestProxy = new Proxy(request, handler);
    
    export default requestProxy;
    

    Usage (see request and promises):

    import request from '../../../utils/superagentWrapper';
    
    AuthenticatedCall: (x) => request // generate a new request
      .get(sources.AuthenticatedCall(x)) // a request returns a promise by default