Transform camelCase to snake_case in Axios
This article explains how to build an automated middleware pipeline
in Axios using request and response interceptors to transform JavaScript
camelCase objects into backend-friendly
snake_case payloads, and automatically convert incoming
snake_case responses back to camelCase.
1. Create the Key Transformation Helpers
To handle nested objects and arrays reliably, create recursive helper functions that convert object keys between casing conventions.
function camelToSnake(str) {
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
}
function snakeToCamel(str) {
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
export function keysToSnake(input) {
if (Array.isArray(input)) {
return input.map((item) => keysToSnake(item));
}
if (input !== null && typeof input === 'object' && !(input instanceof FormData) && !(input instanceof Date)) {
return Object.keys(input).reduce((acc, key) => {
const snakeKey = camelToSnake(key);
acc[snakeKey] = keysToSnake(input[key]);
return acc;
}, {});
}
return input;
}
export function keysToCamel(input) {
if (Array.isArray(input)) {
return input.map((item) => keysToCamel(item));
}
if (input !== null && typeof input === 'object' && !(input instanceof Date)) {
return Object.keys(input).reduce((acc, key) => {
const camelKey = snakeToCamel(key);
acc[camelKey] = keysToCamel(input[key]);
return acc;
}, {});
}
return input;
}2. Configure Axios Interceptors
Axios interceptors function as middleware that process requests before they are sent and responses before they reach application logic.
Create a dedicated Axios instance and attach the transformation logic to the request and response interceptor pipelines:
import axios from 'axios';
import { keysToSnake, keysToCamel } from './caseTransformers';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
headers: {
'Content-Type': 'application/json',
},
});
// Request Interceptor: Transform camelCase to snake_case
apiClient.interceptors.request.use(
(config) => {
// Transform JSON request payload body
if (config.data && !(config.data instanceof FormData)) {
config.data = keysToSnake(config.data);
}
// Transform URL query parameters
if (config.params) {
config.params = keysToSnake(config.params);
}
return config;
},
(error) => Promise.reject(error)
);
// Response Interceptor: Transform snake_case back to camelCase
apiClient.interceptors.response.use(
(response) => {
if (response.data && typeof response.data === 'object') {
response.data = keysToCamel(response.data);
}
return response;
},
(error) => {
if (error.response && error.response.data && typeof error.response.data === 'object') {
error.response.data = keysToCamel(error.response.data);
}
return Promise.reject(error);
}
);
export default apiClient;3. Usage Example
With the middleware pipeline active, you can send and consume
standard JavaScript camelCase properties without manual
mapping:
import apiClient from './apiClient';
async function createUser() {
// Input using camelCase
const payload = {
firstName: 'Jane',
lastName: 'Doe',
contactDetails: {
emailAddress: 'jane.doe@example.com',
postalCode: '10001'
}
};
// Sent over the wire as:
// { "first_name": "Jane", "last_name": "Doe", "contact_details": { "email_address": "...", "postal_code": "..." } }
const response = await apiClient.post('/users', payload, {
params: { sendWelcomeEmail: true } // Query parameter becomes: ?send_welcome_email=true
});
// Response with backend snake_case keys is parsed back to camelCase
console.log(response.data.userId);
}