or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cancellation.mddata-transformation.mderror-handling.mdheaders.mdhttp-client.mdindex.mdinstance-management.mdinterceptors.md
tile.json

data-transformation.mddocs/

Data Transformation

Convert between different data formats including FormData, JSON, and URL-encoded data with extensive serialization options, configuration merging, and adapter selection.

Capabilities

Form Data Conversion

Convert JavaScript objects to FormData with advanced serialization options for file uploads and form submissions.

/**
 * Convert object to FormData with serialization options
 * @param sourceObj - Object to convert to FormData
 * @param targetFormData - Existing FormData to append to (optional)
 * @param options - Serialization configuration options
 * @returns FormData instance with serialized object data
 */
axios.toFormData(
  sourceObj: object, 
  targetFormData?: GenericFormData, 
  options?: FormSerializerOptions
): GenericFormData;

interface FormSerializerOptions extends SerializerOptions {
  /** Custom visitor function for object traversal */
  visitor?: SerializerVisitor;
  /** Use dot notation for nested objects */
  dots?: boolean;
  /** Include meta tokens in keys */
  metaTokens?: boolean;
  /** Use array indexes in keys */
  indexes?: boolean | null;
}

interface SerializerVisitor {
  (
    this: GenericFormData,
    value: any,
    key: string | number,
    path: null | Array<string | number>,
    helpers: FormDataVisitorHelpers
  ): boolean;
}

interface GenericFormData {
  append(name: string, value: any, options?: any): any;
}

Usage Examples:

import axios from "axios";

// Basic object to FormData conversion
const userData = {
  name: "John Doe",
  email: "john@example.com",
  age: 30,
  avatar: fileInput.files[0] // File object
};

const formData = axios.toFormData(userData);

// Send as multipart/form-data
await axios.post("/api/users", formData, {
  headers: { "Content-Type": "multipart/form-data" }
});

// Nested object conversion with dots notation
const complexData = {
  user: {
    profile: {
      name: "Alice",
      settings: {
        theme: "dark",
        notifications: true
      }
    }
  },
  files: [file1, file2]
};

const formDataWithDots = axios.toFormData(complexData, undefined, {
  dots: true,
  indexes: true
});

// Results in FormData with keys like:
// user.profile.name = "Alice"
// user.profile.settings.theme = "dark"
// files[0] = file1
// files[1] = file2

// Custom visitor for advanced serialization
const customFormData = axios.toFormData(data, undefined, {
  visitor: function(value, key, path, helpers) {
    if (value instanceof Date) {
      this.append(key, value.toISOString());
      return false; // Don't continue default processing
    }
    
    if (typeof value === "boolean") {
      this.append(key, value ? "1" : "0");
      return false;
    }
    
    return helpers.defaultVisitor.call(this, value, key, path, helpers);
  }
});

JSON to Form Data Conversion

Convert FormData or HTML form elements back to JSON objects.

/**
 * Convert FormData or HTMLFormElement to JSON object
 * @param form - FormData instance or HTML form element
 * @returns Plain JavaScript object with form data
 */
axios.formToJSON(form: GenericFormData | GenericHTMLFormElement): object;

interface GenericHTMLFormElement {
  name: string;
  method: string;
  submit(): void;
}

Usage Examples:

// Convert FormData to JSON
const formData = new FormData();
formData.append("name", "John");
formData.append("email", "john@example.com");
formData.append("age", "30");

const jsonData = axios.formToJSON(formData);
console.log(jsonData); // { name: "John", email: "john@example.com", age: "30" }

// Convert HTML form to JSON
const formElement = document.getElementById("user-form");
const formJson = axios.formToJSON(formElement);

// Use in request
await axios.post("/api/users", formJson);

// Handle complex form structures
const complexForm = new FormData();
complexForm.append("user[name]", "Alice");
complexForm.append("user[email]", "alice@example.com");
complexForm.append("preferences[theme]", "dark");
complexForm.append("tags[]", "admin");
complexForm.append("tags[]", "user");

const parsedData = axios.formToJSON(complexForm);
// Results in nested object structure based on form field names

Configuration Merging

Merge multiple axios configurations with intelligent precedence handling.

/**
 * Merge two axios configurations intelligently
 * @param config1 - Base configuration
 * @param config2 - Override configuration
 * @returns Merged configuration with proper precedence
 */
axios.mergeConfig<D = any>(
  config1: AxiosRequestConfig<D>, 
  config2: AxiosRequestConfig<D>
): AxiosRequestConfig<D>;

Usage Examples:

// Base configuration
const baseConfig = {
  baseURL: "https://api.example.com",
  timeout: 5000,
  headers: {
    "Accept": "application/json",
    "User-Agent": "MyApp/1.0"
  },
  params: {
    version: "v1"
  }
};

// Override configuration
const requestConfig = {
  timeout: 10000, // Override timeout
  headers: {
    "Authorization": "Bearer token123", // Add new header
    "Accept": "application/json, text/plain" // Override accept
  },
  params: {
    limit: 10 // Add new parameter
  }
};

const merged = axios.mergeConfig(baseConfig, requestConfig);
console.log(merged);
/* Result:
{
  baseURL: "https://api.example.com",
  timeout: 10000, // Overridden
  headers: {
    "Accept": "application/json, text/plain", // Overridden
    "User-Agent": "MyApp/1.0", // Preserved
    "Authorization": "Bearer token123" // Added
  },
  params: {
    version: "v1", // Preserved
    limit: 10 // Added
  }
}
*/

// Use in instance creation
const apiClient = axios.create(baseConfig);

// Merge for specific requests
const response = await apiClient.request(
  axios.mergeConfig(requestConfig, {
    method: "get",
    url: "/users"
  })
);

Adapter Selection

Select appropriate request adapter based on environment and requirements.

/**
 * Get appropriate adapter from configuration
 * @param adapters - Adapter configuration (string, function, or array)
 * @returns Resolved adapter function
 */
axios.getAdapter(adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined): AxiosAdapter;

type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;
type AxiosAdapterName = 'fetch' | 'xhr' | 'http' | (string & {});

interface AxiosAdapter {
  (config: InternalAxiosRequestConfig): AxiosPromise;
}

Usage Examples:

// Get default adapter
const defaultAdapter = axios.getAdapter();

// Specify preferred adapters in order
const adapter = axios.getAdapter(['fetch', 'xhr', 'http']);

// Use specific adapter
const fetchAdapter = axios.getAdapter('fetch');
const xhrAdapter = axios.getAdapter('xhr');
const httpAdapter = axios.getAdapter('http'); // Node.js only

// Use in configuration
const config = {
  url: "/api/data",
  adapter: axios.getAdapter(['fetch', 'xhr'])
};

// Custom adapter selection logic
function selectAdapter(environment) {
  if (environment === 'node') {
    return axios.getAdapter('http');
  } else if (typeof fetch !== 'undefined') {
    return axios.getAdapter('fetch');
  } else {
    return axios.getAdapter('xhr');
  }
}

const customAdapter = selectAdapter(process.env.NODE_ENV);

URL Parameter Serialization

Advanced URL parameter serialization with custom encoders and formats.

interface ParamsSerializerOptions extends SerializerOptions {
  /** Custom parameter encoder function */
  encode?: ParamEncoder;
  /** Custom serialization function */
  serialize?: CustomParamsSerializer;
}

interface ParamEncoder {
  (value: any, defaultEncoder: (value: any) => any): any;
}

interface CustomParamsSerializer {
  (params: Record<string, any>, options?: ParamsSerializerOptions): string;
}

Usage Examples:

// Custom parameter serialization
const config = {
  url: "/api/search",
  params: {
    query: "hello world",
    filters: {
      category: "tech",
      date: new Date("2023-01-01")
    },
    tags: ["javascript", "api"]
  },
  paramsSerializer: {
    serialize: (params, options) => {
      const searchParams = new URLSearchParams();
      
      Object.entries(params).forEach(([key, value]) => {
        if (Array.isArray(value)) {
          value.forEach(item => searchParams.append(`${key}[]`, item));
        } else if (typeof value === "object" && value !== null) {
          Object.entries(value).forEach(([nestedKey, nestedValue]) => {
            searchParams.append(`${key}[${nestedKey}]`, nestedValue);
          });
        } else {
          searchParams.append(key, value);
        }
      });
      
      return searchParams.toString();
    }
  }
};

// Results in URL: /api/search?query=hello+world&filters[category]=tech&filters[date]=2023-01-01T00:00:00.000Z&tags[]=javascript&tags[]=api

// Custom encoder for special characters
const customEncoderConfig = {
  params: { special: "hello+world" },
  paramsSerializer: {
    encode: (value, defaultEncoder) => {
      if (typeof value === "string") {
        return encodeURIComponent(value).replace(/%20/g, "+");
      }
      return defaultEncoder(value);
    }
  }
};

Request and Response Transformers

Transform request and response data automatically.

interface AxiosRequestTransformer {
  (this: InternalAxiosRequestConfig, data: any, headers: AxiosRequestHeaders): any;
}

interface AxiosResponseTransformer {
  (this: InternalAxiosRequestConfig, data: any, headers: AxiosResponseHeaders, status?: number): any;
}

Usage Examples:

// Custom request transformer
const requestTransformer = function(data, headers) {
  if (data && typeof data === "object") {
    // Convert camelCase to snake_case for API
    const transformed = {};
    Object.entries(data).forEach(([key, value]) => {
      const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
      transformed[snakeKey] = value;
    });
    return JSON.stringify(transformed);
  }
  return data;
};

// Custom response transformer
const responseTransformer = function(data, headers, status) {
  if (typeof data === "string") {
    try {
      data = JSON.parse(data);
    } catch (e) {
      return data;
    }
  }
  
  if (data && typeof data === "object") {
    // Convert snake_case to camelCase
    const transformed = {};
    Object.entries(data).forEach(([key, value]) => {
      const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
      transformed[camelKey] = value;
    });
    return transformed;
  }
  
  return data;
};

// Use transformers in configuration
const apiClient = axios.create({
  transformRequest: [requestTransformer, ...axios.defaults.transformRequest],
  transformResponse: [...axios.defaults.transformResponse, responseTransformer]
});

// Multiple transformers
const multiTransformConfig = {
  transformRequest: [
    // First transformer: handle dates
    (data, headers) => {
      if (data && typeof data === "object") {
        Object.entries(data).forEach(([key, value]) => {
          if (value instanceof Date) {
            data[key] = value.toISOString();
          }
        });
      }
      return data;
    },
    // Second transformer: JSON stringify
    (data, headers) => {
      if (data && typeof data === "object") {
        headers.setContentType("application/json");
        return JSON.stringify(data);
      }
      return data;
    }
  ]
};

Advanced Configuration Examples

Real-world examples of complex data transformation and configuration scenarios.

Usage Examples:

// Complex file upload with progress and validation
const uploadFile = async (file, metadata) => {
  const formData = axios.toFormData({
    file: file,
    metadata: JSON.stringify(metadata),
    timestamp: new Date().toISOString()
  }, undefined, {
    dots: true,
    indexes: true,
    visitor: function(value, key, path, helpers) {
      // Custom handling for different data types
      if (value instanceof Date) {
        this.append(key, value.toISOString());
        return false; // Don't use default handling
      }
      if (typeof value === 'object' && value.constructor.name === 'File') {
        this.append(key, value, value.name);
        return false;
      }
      // Use default handling for other types
      return true;
    }
  });

  return axios.post('/api/upload', formData, {
    headers: { 'Content-Type': 'multipart/form-data' },
    onUploadProgress: (progressEvent) => {
      const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
      console.log(`Upload progress: ${percentCompleted}%`);
    },
    timeout: 30000, // 30 second timeout for file uploads
    maxBodyLength: 50 * 1024 * 1024 // 50MB max file size
  });
};

// API client with automatic data transformation
const createAPIClient = (baseURL) => {
  const client = axios.create({ baseURL });
  
  // Transform snake_case API responses to camelCase
  client.defaults.transformResponse = [
    ...client.defaults.transformResponse,
    (data) => {
      if (typeof data === 'object' && data !== null) {
        return transformKeys(data, camelCase);
      }
      return data;
    }
  ];
  
  // Transform camelCase requests to snake_case
  client.defaults.transformRequest = [
    (data) => {
      if (typeof data === 'object' && data !== null) {
        return transformKeys(data, snakeCase);
      }
      return data;
    },
    ...client.defaults.transformRequest
  ];
  
  return client;
};

// Helper function for key transformation
function transformKeys(obj, transformer) {
  if (Array.isArray(obj)) {
    return obj.map(item => transformKeys(item, transformer));
  }
  if (obj && typeof obj === 'object') {
    return Object.keys(obj).reduce((result, key) => {
      const transformedKey = transformer(key);
      result[transformedKey] = transformKeys(obj[key], transformer);
      return result;
    }, {});
  }
  return obj;
}

// Modern approach (preferred)
const responses = await Promise.all([
  axios.get("/api/users"),
  axios.get("/api/posts")
]);

const [usersResponse, postsResponse] = responses;

// Legacy all usage (use Promise.all instead)
axios.all([
  axios.get("/api/users"),
  axios.get("/api/posts")
]).then(axios.spread((usersRes, postsRes) => {
  console.log("Users:", usersRes.data);
  console.log("Posts:", postsRes.data);
}));

// Modern approach (preferred)
Promise.all([
  axios.get("/api/users"),
  axios.get("/api/posts")
]).then(([usersRes, postsRes]) => {
  console.log("Users:", usersRes.data);
  console.log("Posts:", postsRes.data);
});

Advanced Data Transformation Patterns

Complex transformation patterns for real-world applications.

Usage Examples:

// Data normalization pipeline
class DataNormalizer {
  static request(data) {
    return Object.entries(data || {}).reduce((acc, [key, value]) => {
      // Convert dates to ISO strings
      if (value instanceof Date) {
        acc[key] = value.toISOString();
      }
      // Convert boolean to string for some APIs
      else if (typeof value === "boolean") {
        acc[key] = value.toString();
      }
      // Convert camelCase to snake_case
      else {
        const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
        acc[snakeKey] = value;
      }
      return acc;
    }, {});
  }
  
  static response(data) {
    if (!data || typeof data !== "object") return data;
    
    return Object.entries(data).reduce((acc, [key, value]) => {
      // Convert snake_case to camelCase
      const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
      
      // Parse ISO date strings back to Date objects
      if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
        acc[camelKey] = new Date(value);
      } else {
        acc[camelKey] = value;
      }
      
      return acc;
    }, {});
  }
}

// Use in axios configuration
const normalizedClient = axios.create({
  transformRequest: [
    DataNormalizer.request,
    ...axios.defaults.transformRequest
  ],
  transformResponse: [
    ...axios.defaults.transformResponse,
    DataNormalizer.response
  ]
});

// Conditional transformation based on content type
const smartTransformer = function(data, headers) {
  const contentType = headers.getContentType();
  
  if (contentType === "application/json") {
    return JSON.stringify(data);
  } else if (contentType === "application/x-www-form-urlencoded") {
    return new URLSearchParams(data).toString();
  } else if (contentType === "multipart/form-data") {
    return axios.toFormData(data);
  }
  
  return data;
};