0
# AJAX Operations
1
2
HTTP request capabilities with full observable integration, response streaming, and comprehensive error handling for web API interactions.
3
4
## Capabilities
5
6
### ajax Function
7
8
Core function for creating HTTP request observables.
9
10
```typescript { .api }
11
/**
12
* Create observable HTTP requests with comprehensive configuration
13
* @param request - URL string or detailed configuration object
14
* @returns Observable emitting AjaxResponse
15
*/
16
function ajax<T>(request: string | AjaxConfig): Observable<AjaxResponse<T>>;
17
18
/**
19
* Create GET request observable returning JSON response
20
* @param url - URL to request
21
* @param headers - Optional request headers
22
* @returns Observable emitting parsed JSON response
23
*/
24
function ajax.getJSON<T>(url: string, headers?: Record<string, any>): Observable<T>;
25
26
/**
27
* Create GET request observable
28
* @param url - URL to request
29
* @param headers - Optional request headers
30
* @returns Observable emitting AjaxResponse
31
*/
32
function ajax.get(url: string, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
33
34
/**
35
* Create POST request observable
36
* @param url - URL to request
37
* @param body - Request body
38
* @param headers - Optional request headers
39
* @returns Observable emitting AjaxResponse
40
*/
41
function ajax.post(url: string, body?: any, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
42
43
/**
44
* Create PUT request observable
45
* @param url - URL to request
46
* @param body - Request body
47
* @param headers - Optional request headers
48
* @returns Observable emitting AjaxResponse
49
*/
50
function ajax.put(url: string, body?: any, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
51
52
/**
53
* Create PATCH request observable
54
* @param url - URL to request
55
* @param body - Request body
56
* @param headers - Optional request headers
57
* @returns Observable emitting AjaxResponse
58
*/
59
function ajax.patch(url: string, body?: any, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
60
61
/**
62
* Create DELETE request observable
63
* @param url - URL to request
64
* @param headers - Optional request headers
65
* @returns Observable emitting AjaxResponse
66
*/
67
function ajax.delete(url: string, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
68
```
69
70
**Usage Examples:**
71
72
```typescript
73
import { ajax } from "rxjs/ajax";
74
import { map, catchError } from "rxjs/operators";
75
import { of } from "rxjs";
76
77
// Simple GET request
78
ajax.getJSON<User>('/api/users/123').subscribe(
79
user => console.log('User:', user),
80
err => console.error('Error:', err)
81
);
82
83
// GET with headers
84
ajax.get('/api/data', {
85
'Authorization': 'Bearer token123',
86
'Content-Type': 'application/json'
87
}).subscribe(response => {
88
console.log('Status:', response.status);
89
console.log('Data:', response.response);
90
});
91
92
// POST request
93
ajax.post('/api/users', {
94
name: 'John Doe',
95
email: 'john@example.com'
96
}, {
97
'Content-Type': 'application/json'
98
}).subscribe(
99
response => console.log('Created user:', response.response),
100
err => console.error('Creation failed:', err)
101
);
102
103
// Advanced configuration
104
ajax({
105
url: '/api/upload',
106
method: 'POST',
107
body: formData,
108
timeout: 30000,
109
responseType: 'json',
110
withCredentials: true
111
}).subscribe(
112
response => console.log('Upload successful:', response),
113
err => console.error('Upload failed:', err)
114
);
115
```
116
117
### AjaxConfig Interface
118
119
Comprehensive configuration for HTTP requests.
120
121
```typescript { .api }
122
/**
123
* Configuration object for AJAX requests
124
*/
125
interface AjaxConfig {
126
/** Request URL */
127
url?: string;
128
129
/** HTTP method (GET, POST, PUT, DELETE, etc.) */
130
method?: string;
131
132
/** Request headers object */
133
headers?: Record<string, any>;
134
135
/** Request body (any serializable data) */
136
body?: any;
137
138
/** Include credentials in cross-origin requests */
139
withCredentials?: boolean;
140
141
/** Username for HTTP authentication */
142
user?: string;
143
144
/** Password for HTTP authentication */
145
password?: string;
146
147
/** Request timeout in milliseconds */
148
timeout?: number;
149
150
/** Response type expected */
151
responseType?: XMLHttpRequestResponseType;
152
153
/** Custom XMLHttpRequest creation function */
154
createXHR?: () => XMLHttpRequest;
155
156
/** Progress event handler */
157
progressSubscriber?: Subscriber<any>;
158
159
/** Include response headers in result */
160
includeDownloadProgress?: boolean;
161
162
/** Include upload progress events */
163
includeUploadProgress?: boolean;
164
165
/** Custom result selector function */
166
resultSelector?: (response: AjaxResponse<any>) => any;
167
168
/** Cross-domain request handling */
169
crossDomain?: boolean;
170
}
171
172
/**
173
* Response type values for XMLHttpRequest
174
*/
175
type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "json" | "text";
176
```
177
178
### AjaxResponse Class
179
180
Wrapper for HTTP responses with metadata.
181
182
```typescript { .api }
183
/**
184
* Wrapper for HTTP response data and metadata
185
*/
186
class AjaxResponse<T> {
187
/** Parsed response data */
188
readonly response: T;
189
190
/** HTTP status code */
191
readonly status: number;
192
193
/** HTTP status text */
194
readonly responseText: string;
195
196
/** Response headers */
197
readonly responseHeaders: Record<string, string>;
198
199
/** Original request configuration */
200
readonly request: AjaxConfig;
201
202
/** Response type */
203
readonly responseType: XMLHttpRequestResponseType;
204
205
/** Total bytes loaded */
206
readonly loaded: number;
207
208
/** Total bytes to load */
209
readonly total: number;
210
211
/** Original XMLHttpRequest object */
212
readonly originalEvent: ProgressEvent;
213
214
/** Original XMLHttpRequest instance */
215
readonly xhr: XMLHttpRequest;
216
217
constructor(
218
originalEvent: Event,
219
xhr: XMLHttpRequest,
220
request: AjaxConfig
221
);
222
}
223
```
224
225
### Error Classes
226
227
Specialized error types for HTTP request failures.
228
229
```typescript { .api }
230
/**
231
* Error for general AJAX request failures
232
*/
233
class AjaxError extends Error {
234
/** Error name */
235
readonly name: "AjaxError";
236
237
/** HTTP status code */
238
readonly status: number;
239
240
/** Response text */
241
readonly responseText: string;
242
243
/** Original XMLHttpRequest */
244
readonly xhr: XMLHttpRequest;
245
246
/** Original request configuration */
247
readonly request: AjaxConfig;
248
249
constructor(message: string, xhr: XMLHttpRequest, request: AjaxConfig);
250
}
251
252
/**
253
* Error for AJAX request timeouts
254
*/
255
class AjaxTimeoutError extends AjaxError {
256
/** Error name */
257
readonly name: "AjaxTimeoutError";
258
259
constructor(xhr: XMLHttpRequest, request: AjaxConfig);
260
}
261
```
262
263
## Advanced AJAX Patterns
264
265
### Request Interceptors
266
267
```typescript
268
import { ajax } from "rxjs/ajax";
269
import { tap, catchError, retryWhen, delay, take } from "rxjs/operators";
270
271
// Create HTTP client with interceptors
272
class HttpClient {
273
private baseURL = 'https://api.example.com';
274
private authToken = '';
275
276
setAuthToken(token: string) {
277
this.authToken = token;
278
}
279
280
private createRequest(config: AjaxConfig): Observable<AjaxResponse<any>> {
281
const fullConfig: AjaxConfig = {
282
...config,
283
url: `${this.baseURL}${config.url}`,
284
headers: {
285
'Content-Type': 'application/json',
286
...(this.authToken && { 'Authorization': `Bearer ${this.authToken}` }),
287
...config.headers
288
},
289
timeout: 10000
290
};
291
292
return ajax(fullConfig).pipe(
293
tap(response => {
294
console.log(`${config.method} ${config.url}:`, response.status);
295
}),
296
retryWhen(errors =>
297
errors.pipe(
298
delay(1000),
299
take(3) // Retry up to 3 times
300
)
301
),
302
catchError(err => {
303
if (err instanceof AjaxTimeoutError) {
304
console.error('Request timeout:', config.url);
305
} else if (err instanceof AjaxError) {
306
console.error(`HTTP Error ${err.status}:`, err.responseText);
307
}
308
throw err;
309
})
310
);
311
}
312
313
get<T>(url: string, headers?: Record<string, any>): Observable<T> {
314
return this.createRequest({ url, method: 'GET', headers }).pipe(
315
map(response => response.response)
316
);
317
}
318
319
post<T>(url: string, body: any, headers?: Record<string, any>): Observable<T> {
320
return this.createRequest({
321
url,
322
method: 'POST',
323
body: JSON.stringify(body),
324
headers
325
}).pipe(
326
map(response => response.response)
327
);
328
}
329
}
330
331
const httpClient = new HttpClient();
332
httpClient.setAuthToken('your-token-here');
333
334
// Usage
335
httpClient.get<User>('/users/123').subscribe(user => {
336
console.log('User loaded:', user);
337
});
338
```
339
340
### File Upload with Progress
341
342
```typescript
343
import { ajax } from "rxjs/ajax";
344
import { Subject } from "rxjs";
345
346
function uploadFile(file: File, url: string): Observable<AjaxResponse<any>> {
347
const formData = new FormData();
348
formData.append('file', file);
349
350
const progressSubject = new Subject<number>();
351
352
const upload$ = ajax({
353
url,
354
method: 'POST',
355
body: formData,
356
progressSubscriber: progressSubject,
357
includeUploadProgress: true
358
});
359
360
// Monitor progress
361
progressSubject.subscribe(progress => {
362
const percentage = (progress.loaded / progress.total) * 100;
363
console.log(`Upload progress: ${percentage.toFixed(1)}%`);
364
updateProgressBar(percentage);
365
});
366
367
return upload$;
368
}
369
370
// Usage
371
const fileInput = document.getElementById('file') as HTMLInputElement;
372
fileInput.addEventListener('change', (event) => {
373
const file = event.target.files[0];
374
if (file) {
375
uploadFile(file, '/api/upload').subscribe(
376
response => console.log('Upload complete:', response.response),
377
err => console.error('Upload failed:', err)
378
);
379
}
380
});
381
```
382
383
### Response Caching
384
385
```typescript
386
import { ajax } from "rxjs/ajax";
387
import { shareReplay, tap } from "rxjs/operators";
388
389
class CachedHttpClient {
390
private cache = new Map<string, Observable<any>>();
391
392
get<T>(url: string, cacheKey?: string): Observable<T> {
393
const key = cacheKey || url;
394
395
if (!this.cache.has(key)) {
396
const request$ = ajax.getJSON<T>(url).pipe(
397
tap(data => console.log(`Cached: ${key}`)),
398
shareReplay(1) // Share and replay last emission
399
);
400
401
this.cache.set(key, request$);
402
403
// Auto-expire cache after 5 minutes
404
setTimeout(() => {
405
this.cache.delete(key);
406
console.log(`Cache expired: ${key}`);
407
}, 5 * 60 * 1000);
408
}
409
410
return this.cache.get(key)!;
411
}
412
413
clearCache(key?: string) {
414
if (key) {
415
this.cache.delete(key);
416
} else {
417
this.cache.clear();
418
}
419
}
420
}
421
422
const cachedClient = new CachedHttpClient();
423
424
// First call - makes HTTP request
425
cachedClient.get<User[]>('/api/users').subscribe(users => {
426
console.log('Users (from server):', users);
427
});
428
429
// Second call - returns cached data
430
cachedClient.get<User[]>('/api/users').subscribe(users => {
431
console.log('Users (from cache):', users);
432
});
433
```
434
435
### Parallel Requests
436
437
```typescript
438
import { ajax } from "rxjs/ajax";
439
import { forkJoin, combineLatest } from "rxjs";
440
import { map } from "rxjs/operators";
441
442
// Load multiple resources in parallel
443
const userRequest$ = ajax.getJSON<User>('/api/user/123');
444
const postsRequest$ = ajax.getJSON<Post[]>('/api/user/123/posts');
445
const commentsRequest$ = ajax.getJSON<Comment[]>('/api/user/123/comments');
446
447
// Wait for all to complete
448
forkJoin({
449
user: userRequest$,
450
posts: postsRequest$,
451
comments: commentsRequest$
452
}).subscribe(({ user, posts, comments }) => {
453
console.log('All data loaded:', { user, posts, comments });
454
});
455
456
// Combine latest values as they arrive
457
combineLatest([
458
userRequest$,
459
postsRequest$,
460
commentsRequest$
461
]).pipe(
462
map(([user, posts, comments]) => ({ user, posts, comments }))
463
).subscribe(data => {
464
console.log('Combined data:', data);
465
});
466
```
467
468
## Types
469
470
```typescript { .api }
471
interface AjaxRequest extends AjaxConfig {
472
url: string;
473
method: string;
474
}
475
476
type AjaxDirection = "upload" | "download";
477
478
interface User {
479
id: number;
480
name: string;
481
email: string;
482
}
483
484
interface Post {
485
id: number;
486
title: string;
487
content: string;
488
userId: number;
489
}
490
491
interface Comment {
492
id: number;
493
text: string;
494
postId: number;
495
userId: number;
496
}
497
```