npm-axios

Description
Promise based HTTP client for the browser and node.js
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/npm-axios@1.11.0

data-transformation.md docs/

1
# Data Transformation
2
3
Convert between different data formats including FormData, JSON, and URL-encoded data with extensive serialization options, configuration merging, and adapter selection.
4
5
## Capabilities
6
7
### Form Data Conversion
8
9
Convert JavaScript objects to FormData with advanced serialization options for file uploads and form submissions.
10
11
```javascript { .api }
12
/**
13
* Convert object to FormData with serialization options
14
* @param sourceObj - Object to convert to FormData
15
* @param targetFormData - Existing FormData to append to (optional)
16
* @param options - Serialization configuration options
17
* @returns FormData instance with serialized object data
18
*/
19
axios.toFormData(
20
sourceObj: object,
21
targetFormData?: GenericFormData,
22
options?: FormSerializerOptions
23
): GenericFormData;
24
25
interface FormSerializerOptions extends SerializerOptions {
26
/** Custom visitor function for object traversal */
27
visitor?: SerializerVisitor;
28
/** Use dot notation for nested objects */
29
dots?: boolean;
30
/** Include meta tokens in keys */
31
metaTokens?: boolean;
32
/** Use array indexes in keys */
33
indexes?: boolean | null;
34
}
35
36
interface SerializerVisitor {
37
(
38
this: GenericFormData,
39
value: any,
40
key: string | number,
41
path: null | Array<string | number>,
42
helpers: FormDataVisitorHelpers
43
): boolean;
44
}
45
46
interface GenericFormData {
47
append(name: string, value: any, options?: any): any;
48
}
49
```
50
51
**Usage Examples:**
52
53
```javascript
54
import axios from "axios";
55
56
// Basic object to FormData conversion
57
const userData = {
58
name: "John Doe",
59
email: "john@example.com",
60
age: 30,
61
avatar: fileInput.files[0] // File object
62
};
63
64
const formData = axios.toFormData(userData);
65
66
// Send as multipart/form-data
67
await axios.post("/api/users", formData, {
68
headers: { "Content-Type": "multipart/form-data" }
69
});
70
71
// Nested object conversion with dots notation
72
const complexData = {
73
user: {
74
profile: {
75
name: "Alice",
76
settings: {
77
theme: "dark",
78
notifications: true
79
}
80
}
81
},
82
files: [file1, file2]
83
};
84
85
const formDataWithDots = axios.toFormData(complexData, undefined, {
86
dots: true,
87
indexes: true
88
});
89
90
// Results in FormData with keys like:
91
// user.profile.name = "Alice"
92
// user.profile.settings.theme = "dark"
93
// files[0] = file1
94
// files[1] = file2
95
96
// Custom visitor for advanced serialization
97
const customFormData = axios.toFormData(data, undefined, {
98
visitor: function(value, key, path, helpers) {
99
if (value instanceof Date) {
100
this.append(key, value.toISOString());
101
return false; // Don't continue default processing
102
}
103
104
if (typeof value === "boolean") {
105
this.append(key, value ? "1" : "0");
106
return false;
107
}
108
109
return helpers.defaultVisitor.call(this, value, key, path, helpers);
110
}
111
});
112
```
113
114
### JSON to Form Data Conversion
115
116
Convert FormData or HTML form elements back to JSON objects.
117
118
```javascript { .api }
119
/**
120
* Convert FormData or HTMLFormElement to JSON object
121
* @param form - FormData instance or HTML form element
122
* @returns Plain JavaScript object with form data
123
*/
124
axios.formToJSON(form: GenericFormData | GenericHTMLFormElement): object;
125
126
interface GenericHTMLFormElement {
127
name: string;
128
method: string;
129
submit(): void;
130
}
131
```
132
133
**Usage Examples:**
134
135
```javascript
136
// Convert FormData to JSON
137
const formData = new FormData();
138
formData.append("name", "John");
139
formData.append("email", "john@example.com");
140
formData.append("age", "30");
141
142
const jsonData = axios.formToJSON(formData);
143
console.log(jsonData); // { name: "John", email: "john@example.com", age: "30" }
144
145
// Convert HTML form to JSON
146
const formElement = document.getElementById("user-form");
147
const formJson = axios.formToJSON(formElement);
148
149
// Use in request
150
await axios.post("/api/users", formJson);
151
152
// Handle complex form structures
153
const complexForm = new FormData();
154
complexForm.append("user[name]", "Alice");
155
complexForm.append("user[email]", "alice@example.com");
156
complexForm.append("preferences[theme]", "dark");
157
complexForm.append("tags[]", "admin");
158
complexForm.append("tags[]", "user");
159
160
const parsedData = axios.formToJSON(complexForm);
161
// Results in nested object structure based on form field names
162
```
163
164
### Configuration Merging
165
166
Merge multiple axios configurations with intelligent precedence handling.
167
168
```javascript { .api }
169
/**
170
* Merge two axios configurations intelligently
171
* @param config1 - Base configuration
172
* @param config2 - Override configuration
173
* @returns Merged configuration with proper precedence
174
*/
175
axios.mergeConfig<D = any>(
176
config1: AxiosRequestConfig<D>,
177
config2: AxiosRequestConfig<D>
178
): AxiosRequestConfig<D>;
179
```
180
181
**Usage Examples:**
182
183
```javascript
184
// Base configuration
185
const baseConfig = {
186
baseURL: "https://api.example.com",
187
timeout: 5000,
188
headers: {
189
"Accept": "application/json",
190
"User-Agent": "MyApp/1.0"
191
},
192
params: {
193
version: "v1"
194
}
195
};
196
197
// Override configuration
198
const requestConfig = {
199
timeout: 10000, // Override timeout
200
headers: {
201
"Authorization": "Bearer token123", // Add new header
202
"Accept": "application/json, text/plain" // Override accept
203
},
204
params: {
205
limit: 10 // Add new parameter
206
}
207
};
208
209
const merged = axios.mergeConfig(baseConfig, requestConfig);
210
console.log(merged);
211
/* Result:
212
{
213
baseURL: "https://api.example.com",
214
timeout: 10000, // Overridden
215
headers: {
216
"Accept": "application/json, text/plain", // Overridden
217
"User-Agent": "MyApp/1.0", // Preserved
218
"Authorization": "Bearer token123" // Added
219
},
220
params: {
221
version: "v1", // Preserved
222
limit: 10 // Added
223
}
224
}
225
*/
226
227
// Use in instance creation
228
const apiClient = axios.create(baseConfig);
229
230
// Merge for specific requests
231
const response = await apiClient.request(
232
axios.mergeConfig(requestConfig, {
233
method: "get",
234
url: "/users"
235
})
236
);
237
```
238
239
### Adapter Selection
240
241
Select appropriate request adapter based on environment and requirements.
242
243
```javascript { .api }
244
/**
245
* Get appropriate adapter from configuration
246
* @param adapters - Adapter configuration (string, function, or array)
247
* @returns Resolved adapter function
248
*/
249
axios.getAdapter(adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined): AxiosAdapter;
250
251
type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;
252
type AxiosAdapterName = 'fetch' | 'xhr' | 'http' | (string & {});
253
254
interface AxiosAdapter {
255
(config: InternalAxiosRequestConfig): AxiosPromise;
256
}
257
```
258
259
**Usage Examples:**
260
261
```javascript
262
// Get default adapter
263
const defaultAdapter = axios.getAdapter();
264
265
// Specify preferred adapters in order
266
const adapter = axios.getAdapter(['fetch', 'xhr', 'http']);
267
268
// Use specific adapter
269
const fetchAdapter = axios.getAdapter('fetch');
270
const xhrAdapter = axios.getAdapter('xhr');
271
const httpAdapter = axios.getAdapter('http'); // Node.js only
272
273
// Use in configuration
274
const config = {
275
url: "/api/data",
276
adapter: axios.getAdapter(['fetch', 'xhr'])
277
};
278
279
// Custom adapter selection logic
280
function selectAdapter(environment) {
281
if (environment === 'node') {
282
return axios.getAdapter('http');
283
} else if (typeof fetch !== 'undefined') {
284
return axios.getAdapter('fetch');
285
} else {
286
return axios.getAdapter('xhr');
287
}
288
}
289
290
const customAdapter = selectAdapter(process.env.NODE_ENV);
291
```
292
293
### URL Parameter Serialization
294
295
Advanced URL parameter serialization with custom encoders and formats.
296
297
```javascript { .api }
298
interface ParamsSerializerOptions extends SerializerOptions {
299
/** Custom parameter encoder function */
300
encode?: ParamEncoder;
301
/** Custom serialization function */
302
serialize?: CustomParamsSerializer;
303
}
304
305
interface ParamEncoder {
306
(value: any, defaultEncoder: (value: any) => any): any;
307
}
308
309
interface CustomParamsSerializer {
310
(params: Record<string, any>, options?: ParamsSerializerOptions): string;
311
}
312
```
313
314
**Usage Examples:**
315
316
```javascript
317
// Custom parameter serialization
318
const config = {
319
url: "/api/search",
320
params: {
321
query: "hello world",
322
filters: {
323
category: "tech",
324
date: new Date("2023-01-01")
325
},
326
tags: ["javascript", "api"]
327
},
328
paramsSerializer: {
329
serialize: (params, options) => {
330
const searchParams = new URLSearchParams();
331
332
Object.entries(params).forEach(([key, value]) => {
333
if (Array.isArray(value)) {
334
value.forEach(item => searchParams.append(`${key}[]`, item));
335
} else if (typeof value === "object" && value !== null) {
336
Object.entries(value).forEach(([nestedKey, nestedValue]) => {
337
searchParams.append(`${key}[${nestedKey}]`, nestedValue);
338
});
339
} else {
340
searchParams.append(key, value);
341
}
342
});
343
344
return searchParams.toString();
345
}
346
}
347
};
348
349
// Results in URL: /api/search?query=hello+world&filters[category]=tech&filters[date]=2023-01-01T00:00:00.000Z&tags[]=javascript&tags[]=api
350
351
// Custom encoder for special characters
352
const customEncoderConfig = {
353
params: { special: "hello+world" },
354
paramsSerializer: {
355
encode: (value, defaultEncoder) => {
356
if (typeof value === "string") {
357
return encodeURIComponent(value).replace(/%20/g, "+");
358
}
359
return defaultEncoder(value);
360
}
361
}
362
};
363
```
364
365
### Request and Response Transformers
366
367
Transform request and response data automatically.
368
369
```javascript { .api }
370
interface AxiosRequestTransformer {
371
(this: InternalAxiosRequestConfig, data: any, headers: AxiosRequestHeaders): any;
372
}
373
374
interface AxiosResponseTransformer {
375
(this: InternalAxiosRequestConfig, data: any, headers: AxiosResponseHeaders, status?: number): any;
376
}
377
```
378
379
**Usage Examples:**
380
381
```javascript
382
// Custom request transformer
383
const requestTransformer = function(data, headers) {
384
if (data && typeof data === "object") {
385
// Convert camelCase to snake_case for API
386
const transformed = {};
387
Object.entries(data).forEach(([key, value]) => {
388
const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
389
transformed[snakeKey] = value;
390
});
391
return JSON.stringify(transformed);
392
}
393
return data;
394
};
395
396
// Custom response transformer
397
const responseTransformer = function(data, headers, status) {
398
if (typeof data === "string") {
399
try {
400
data = JSON.parse(data);
401
} catch (e) {
402
return data;
403
}
404
}
405
406
if (data && typeof data === "object") {
407
// Convert snake_case to camelCase
408
const transformed = {};
409
Object.entries(data).forEach(([key, value]) => {
410
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
411
transformed[camelKey] = value;
412
});
413
return transformed;
414
}
415
416
return data;
417
};
418
419
// Use transformers in configuration
420
const apiClient = axios.create({
421
transformRequest: [requestTransformer, ...axios.defaults.transformRequest],
422
transformResponse: [...axios.defaults.transformResponse, responseTransformer]
423
});
424
425
// Multiple transformers
426
const multiTransformConfig = {
427
transformRequest: [
428
// First transformer: handle dates
429
(data, headers) => {
430
if (data && typeof data === "object") {
431
Object.entries(data).forEach(([key, value]) => {
432
if (value instanceof Date) {
433
data[key] = value.toISOString();
434
}
435
});
436
}
437
return data;
438
},
439
// Second transformer: JSON stringify
440
(data, headers) => {
441
if (data && typeof data === "object") {
442
headers.setContentType("application/json");
443
return JSON.stringify(data);
444
}
445
return data;
446
}
447
]
448
};
449
```
450
451
### Advanced Configuration Examples
452
453
Real-world examples of complex data transformation and configuration scenarios.
454
455
**Usage Examples:**
456
457
```javascript
458
// Complex file upload with progress and validation
459
const uploadFile = async (file, metadata) => {
460
const formData = axios.toFormData({
461
file: file,
462
metadata: JSON.stringify(metadata),
463
timestamp: new Date().toISOString()
464
}, undefined, {
465
dots: true,
466
indexes: true,
467
visitor: function(value, key, path, helpers) {
468
// Custom handling for different data types
469
if (value instanceof Date) {
470
this.append(key, value.toISOString());
471
return false; // Don't use default handling
472
}
473
if (typeof value === 'object' && value.constructor.name === 'File') {
474
this.append(key, value, value.name);
475
return false;
476
}
477
// Use default handling for other types
478
return true;
479
}
480
});
481
482
return axios.post('/api/upload', formData, {
483
headers: { 'Content-Type': 'multipart/form-data' },
484
onUploadProgress: (progressEvent) => {
485
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
486
console.log(`Upload progress: ${percentCompleted}%`);
487
},
488
timeout: 30000, // 30 second timeout for file uploads
489
maxBodyLength: 50 * 1024 * 1024 // 50MB max file size
490
});
491
};
492
493
// API client with automatic data transformation
494
const createAPIClient = (baseURL) => {
495
const client = axios.create({ baseURL });
496
497
// Transform snake_case API responses to camelCase
498
client.defaults.transformResponse = [
499
...client.defaults.transformResponse,
500
(data) => {
501
if (typeof data === 'object' && data !== null) {
502
return transformKeys(data, camelCase);
503
}
504
return data;
505
}
506
];
507
508
// Transform camelCase requests to snake_case
509
client.defaults.transformRequest = [
510
(data) => {
511
if (typeof data === 'object' && data !== null) {
512
return transformKeys(data, snakeCase);
513
}
514
return data;
515
},
516
...client.defaults.transformRequest
517
];
518
519
return client;
520
};
521
522
// Helper function for key transformation
523
function transformKeys(obj, transformer) {
524
if (Array.isArray(obj)) {
525
return obj.map(item => transformKeys(item, transformer));
526
}
527
if (obj && typeof obj === 'object') {
528
return Object.keys(obj).reduce((result, key) => {
529
const transformedKey = transformer(key);
530
result[transformedKey] = transformKeys(obj[key], transformer);
531
return result;
532
}, {});
533
}
534
return obj;
535
}
536
537
// Modern approach (preferred)
538
const responses = await Promise.all([
539
axios.get("/api/users"),
540
axios.get("/api/posts")
541
]);
542
543
const [usersResponse, postsResponse] = responses;
544
545
// Legacy all usage (use Promise.all instead)
546
axios.all([
547
axios.get("/api/users"),
548
axios.get("/api/posts")
549
]).then(axios.spread((usersRes, postsRes) => {
550
console.log("Users:", usersRes.data);
551
console.log("Posts:", postsRes.data);
552
}));
553
554
// Modern approach (preferred)
555
Promise.all([
556
axios.get("/api/users"),
557
axios.get("/api/posts")
558
]).then(([usersRes, postsRes]) => {
559
console.log("Users:", usersRes.data);
560
console.log("Posts:", postsRes.data);
561
});
562
```
563
564
### Advanced Data Transformation Patterns
565
566
Complex transformation patterns for real-world applications.
567
568
**Usage Examples:**
569
570
```javascript
571
// Data normalization pipeline
572
class DataNormalizer {
573
static request(data) {
574
return Object.entries(data || {}).reduce((acc, [key, value]) => {
575
// Convert dates to ISO strings
576
if (value instanceof Date) {
577
acc[key] = value.toISOString();
578
}
579
// Convert boolean to string for some APIs
580
else if (typeof value === "boolean") {
581
acc[key] = value.toString();
582
}
583
// Convert camelCase to snake_case
584
else {
585
const snakeKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
586
acc[snakeKey] = value;
587
}
588
return acc;
589
}, {});
590
}
591
592
static response(data) {
593
if (!data || typeof data !== "object") return data;
594
595
return Object.entries(data).reduce((acc, [key, value]) => {
596
// Convert snake_case to camelCase
597
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
598
599
// Parse ISO date strings back to Date objects
600
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
601
acc[camelKey] = new Date(value);
602
} else {
603
acc[camelKey] = value;
604
}
605
606
return acc;
607
}, {});
608
}
609
}
610
611
// Use in axios configuration
612
const normalizedClient = axios.create({
613
transformRequest: [
614
DataNormalizer.request,
615
...axios.defaults.transformRequest
616
],
617
transformResponse: [
618
...axios.defaults.transformResponse,
619
DataNormalizer.response
620
]
621
});
622
623
// Conditional transformation based on content type
624
const smartTransformer = function(data, headers) {
625
const contentType = headers.getContentType();
626
627
if (contentType === "application/json") {
628
return JSON.stringify(data);
629
} else if (contentType === "application/x-www-form-urlencoded") {
630
return new URLSearchParams(data).toString();
631
} else if (contentType === "multipart/form-data") {
632
return axios.toFormData(data);
633
}
634
635
return data;
636
};
637
```