Custom Axios Response Data Transformation Pipelines

This guide explains how to configure custom response data transformation pipelines in the Axios HTTP client. You will learn how to leverage the transformResponse configuration option and Axios interceptors to sanitize, normalize, and manipulate incoming server payloads before they reach your application's business logic.

Using the transformResponse Property

Axios provides a built-in transformResponse option that accepts an array of functions. Each function receives the response data and headers, passes its output to the next function in the pipeline, and returns the final transformed data.

By default, Axios includes a single transformer that parses JSON strings into JavaScript objects:

axios.defaults.transformResponse = [
  function (data) {
    return JSON.parse(data);
  }
];

Building a Multi-Step Transformation Pipeline

When defining custom pipelines, you can chain multiple functions to handle tasks like string decoding, property renaming, or date parsing.

import axios from 'axios';

// 1. Transformer to parse ISO date strings into Date objects
const parseDates = (data) => {
  if (typeof data !== 'object' || data === null) return data;

  for (const key of Object.keys(data)) {
    const value = data[key];
    const isoDateFormat = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
    
    if (typeof value === 'string' && isoDateFormat.test(value)) {
      data[key] = new Date(value);
    } else if (typeof value === 'object') {
      parseDates(value);
    }
  }
  return data;
};

// 2. Transformer to standardize API response wrappers
const unwrapData = (data) => {
  return data && data.payload ? data.payload : data;
};

// Create an Axios instance with the transformation pipeline
const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  transformResponse: [
    // Retain default JSON parsing first
    ...axios.defaults.transformResponse,
    // Add custom transformers sequentially
    unwrapData,
    parseDates,
  ],
});

export default apiClient;

Configuring Transformations at Different Scopes

You can apply response transformation pipelines at the global level, instance level, or per-request level.

1. Per-Request Configuration

Override or append transformations for specific API calls:

apiClient.get('/users', {
  transformResponse: [
    ...axios.defaults.transformResponse,
    (data) => data.map(user => ({ ...user, fullName: `${user.firstName} ${user.lastName}` }))
  ]
});

2. Global Defaults

Apply a pipeline across all Axios calls in your application:

axios.defaults.transformResponse = [
  ...axios.defaults.transformResponse,
  (data) => {
    // Custom global transformation
    return data;
  }
];

transformResponse vs. Response Interceptors

While transformResponse is ideal for synchronous data manipulation on successful responses, Axios interceptors provide broader control.

  • transformResponse: Executes only on raw payload data before the promise resolves. It is strictly meant for data parsing and modification.
  • Response Interceptors: Access the entire response object (including HTTP status codes, headers, and config) and support asynchronous execution or error handling.

Asynchronous Data Transformations with Interceptors

If your transformation pipeline requires asynchronous operations (such as decrypting data or making secondary lookup requests), use an interceptor instead:

apiClient.interceptors.response.use(async (response) => {
  if (response.data?.encrypted) {
    response.data = await decryptPayload(response.data.payload);
  }
  return response;
}, (error) => {
  return Promise.reject(error);
});

Best Practices

  • Preserve Default Parsing: Always include ...axios.defaults.transformResponse at the beginning of your pipeline unless you intend to manually parse raw JSON strings.
  • Handle Nullish Data: Ensure transformers defensively check for null, undefined, or unexpected non-object types to avoid unhandled runtime errors.
  • Keep Transformers Synchronous: Keep functions in transformResponse pure and synchronous. Use interceptors whenever async/await is required.