Search code examples
javascriptaxiosinterceptor

Axios interceptor to retry sending FormData


I'm trying to create a "retry" functionality for a request containing a FormData as data. The retry happen when the JWT token is expired, my second request (the retry one) doesn't contain any data.

The idea:

const axiosInstance = axios.create();

  /**
   * Before each request, we'll add a possible retry if the
   * request need a refreshed token
   */
  axiosInstance.interceptors.request.use((request) => {
    if (typeof _.get(request, ['retried']) === 'undefined') {
      request.retried = false;
    }

    return request;
  });

/**
   * After a request with an error, we'll check if the error is a token not refreshed.
   * If we didn't already tried to refresh, we'll just fallback to the errorHandler function
   */
  axiosInstance.interceptors.response.use(null, async (error) => {
    const statusCode = _.get(error, ['response', 'data', 'statusCode']);
    const message = _.get(error, ['response', 'data', 'message']);
    const { config: request = {} } = error;

    if (statusCode === 401 && !request.retried) {
      try {
        const newTokens = await refreshTokens();
        request.retried = true;
        _.set(request, ['headers', 'authorization'], newTokens.accessToken);
        return axiosInstance.request(request);
      } catch (e) {
        return error;
      }
    }
    return error;
  });

The request:

  const formData = new FormData();
  formData.append('name', name);
  formData.append('file', fs.createReadStream(file.path));

  axiosInstance.post(
      'http://localhost/upload',
      formData,
      {
        headers: {
          ...formData.getHeaders(),
          authorization: tokens.accessToken
        }
      }
    );

If my token expired, the request will fail, the interceptor will then refresh my tokens, and retry the exact same request with just the new header Authorization. Yet, on the server side, the received payload is always null.


Solution

  • As far as I know, the formData needs to be added again on the retry (I think it is due to the data being added as a stream and consumed during the request, so a new retry cannot consume the already consumed stream).

    You can take a look at request.data to see what I mean.