Reactive Extensions for modern JavaScript providing comprehensive reactive programming with observable sequences
—
HTTP request capabilities with full observable integration, response streaming, and comprehensive error handling for web API interactions.
Core function for creating HTTP request observables.
/**
* Create observable HTTP requests with comprehensive configuration
* @param request - URL string or detailed configuration object
* @returns Observable emitting AjaxResponse
*/
function ajax<T>(request: string | AjaxConfig): Observable<AjaxResponse<T>>;
/**
* Create GET request observable returning JSON response
* @param url - URL to request
* @param headers - Optional request headers
* @returns Observable emitting parsed JSON response
*/
function ajax.getJSON<T>(url: string, headers?: Record<string, any>): Observable<T>;
/**
* Create GET request observable
* @param url - URL to request
* @param headers - Optional request headers
* @returns Observable emitting AjaxResponse
*/
function ajax.get(url: string, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
/**
* Create POST request observable
* @param url - URL to request
* @param body - Request body
* @param headers - Optional request headers
* @returns Observable emitting AjaxResponse
*/
function ajax.post(url: string, body?: any, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
/**
* Create PUT request observable
* @param url - URL to request
* @param body - Request body
* @param headers - Optional request headers
* @returns Observable emitting AjaxResponse
*/
function ajax.put(url: string, body?: any, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
/**
* Create PATCH request observable
* @param url - URL to request
* @param body - Request body
* @param headers - Optional request headers
* @returns Observable emitting AjaxResponse
*/
function ajax.patch(url: string, body?: any, headers?: Record<string, any>): Observable<AjaxResponse<any>>;
/**
* Create DELETE request observable
* @param url - URL to request
* @param headers - Optional request headers
* @returns Observable emitting AjaxResponse
*/
function ajax.delete(url: string, headers?: Record<string, any>): Observable<AjaxResponse<any>>;Usage Examples:
import { ajax } from "rxjs/ajax";
import { map, catchError } from "rxjs/operators";
import { of } from "rxjs";
// Simple GET request
ajax.getJSON<User>('/api/users/123').subscribe(
user => console.log('User:', user),
err => console.error('Error:', err)
);
// GET with headers
ajax.get('/api/data', {
'Authorization': 'Bearer token123',
'Content-Type': 'application/json'
}).subscribe(response => {
console.log('Status:', response.status);
console.log('Data:', response.response);
});
// POST request
ajax.post('/api/users', {
name: 'John Doe',
email: 'john@example.com'
}, {
'Content-Type': 'application/json'
}).subscribe(
response => console.log('Created user:', response.response),
err => console.error('Creation failed:', err)
);
// Advanced configuration
ajax({
url: '/api/upload',
method: 'POST',
body: formData,
timeout: 30000,
responseType: 'json',
withCredentials: true
}).subscribe(
response => console.log('Upload successful:', response),
err => console.error('Upload failed:', err)
);Comprehensive configuration for HTTP requests.
/**
* Configuration object for AJAX requests
*/
interface AjaxConfig {
/** Request URL */
url?: string;
/** HTTP method (GET, POST, PUT, DELETE, etc.) */
method?: string;
/** Request headers object */
headers?: Record<string, any>;
/** Request body (any serializable data) */
body?: any;
/** Include credentials in cross-origin requests */
withCredentials?: boolean;
/** Username for HTTP authentication */
user?: string;
/** Password for HTTP authentication */
password?: string;
/** Request timeout in milliseconds */
timeout?: number;
/** Response type expected */
responseType?: XMLHttpRequestResponseType;
/** Custom XMLHttpRequest creation function */
createXHR?: () => XMLHttpRequest;
/** Progress event handler */
progressSubscriber?: Subscriber<any>;
/** Include response headers in result */
includeDownloadProgress?: boolean;
/** Include upload progress events */
includeUploadProgress?: boolean;
/** Custom result selector function */
resultSelector?: (response: AjaxResponse<any>) => any;
/** Cross-domain request handling */
crossDomain?: boolean;
}
/**
* Response type values for XMLHttpRequest
*/
type XMLHttpRequestResponseType = "" | "arraybuffer" | "blob" | "document" | "json" | "text";Wrapper for HTTP responses with metadata.
/**
* Wrapper for HTTP response data and metadata
*/
class AjaxResponse<T> {
/** Parsed response data */
readonly response: T;
/** HTTP status code */
readonly status: number;
/** HTTP status text */
readonly responseText: string;
/** Response headers */
readonly responseHeaders: Record<string, string>;
/** Original request configuration */
readonly request: AjaxConfig;
/** Response type */
readonly responseType: XMLHttpRequestResponseType;
/** Total bytes loaded */
readonly loaded: number;
/** Total bytes to load */
readonly total: number;
/** Original XMLHttpRequest object */
readonly originalEvent: ProgressEvent;
/** Original XMLHttpRequest instance */
readonly xhr: XMLHttpRequest;
constructor(
originalEvent: Event,
xhr: XMLHttpRequest,
request: AjaxConfig
);
}Specialized error types for HTTP request failures.
/**
* Error for general AJAX request failures
*/
class AjaxError extends Error {
/** Error name */
readonly name: "AjaxError";
/** HTTP status code */
readonly status: number;
/** Response text */
readonly responseText: string;
/** Original XMLHttpRequest */
readonly xhr: XMLHttpRequest;
/** Original request configuration */
readonly request: AjaxConfig;
constructor(message: string, xhr: XMLHttpRequest, request: AjaxConfig);
}
/**
* Error for AJAX request timeouts
*/
class AjaxTimeoutError extends AjaxError {
/** Error name */
readonly name: "AjaxTimeoutError";
constructor(xhr: XMLHttpRequest, request: AjaxConfig);
}import { ajax } from "rxjs/ajax";
import { tap, catchError, retryWhen, delay, take } from "rxjs/operators";
// Create HTTP client with interceptors
class HttpClient {
private baseURL = 'https://api.example.com';
private authToken = '';
setAuthToken(token: string) {
this.authToken = token;
}
private createRequest(config: AjaxConfig): Observable<AjaxResponse<any>> {
const fullConfig: AjaxConfig = {
...config,
url: `${this.baseURL}${config.url}`,
headers: {
'Content-Type': 'application/json',
...(this.authToken && { 'Authorization': `Bearer ${this.authToken}` }),
...config.headers
},
timeout: 10000
};
return ajax(fullConfig).pipe(
tap(response => {
console.log(`${config.method} ${config.url}:`, response.status);
}),
retryWhen(errors =>
errors.pipe(
delay(1000),
take(3) // Retry up to 3 times
)
),
catchError(err => {
if (err instanceof AjaxTimeoutError) {
console.error('Request timeout:', config.url);
} else if (err instanceof AjaxError) {
console.error(`HTTP Error ${err.status}:`, err.responseText);
}
throw err;
})
);
}
get<T>(url: string, headers?: Record<string, any>): Observable<T> {
return this.createRequest({ url, method: 'GET', headers }).pipe(
map(response => response.response)
);
}
post<T>(url: string, body: any, headers?: Record<string, any>): Observable<T> {
return this.createRequest({
url,
method: 'POST',
body: JSON.stringify(body),
headers
}).pipe(
map(response => response.response)
);
}
}
const httpClient = new HttpClient();
httpClient.setAuthToken('your-token-here');
// Usage
httpClient.get<User>('/users/123').subscribe(user => {
console.log('User loaded:', user);
});import { ajax } from "rxjs/ajax";
import { Subject } from "rxjs";
function uploadFile(file: File, url: string): Observable<AjaxResponse<any>> {
const formData = new FormData();
formData.append('file', file);
const progressSubject = new Subject<number>();
const upload$ = ajax({
url,
method: 'POST',
body: formData,
progressSubscriber: progressSubject,
includeUploadProgress: true
});
// Monitor progress
progressSubject.subscribe(progress => {
const percentage = (progress.loaded / progress.total) * 100;
console.log(`Upload progress: ${percentage.toFixed(1)}%`);
updateProgressBar(percentage);
});
return upload$;
}
// Usage
const fileInput = document.getElementById('file') as HTMLInputElement;
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
uploadFile(file, '/api/upload').subscribe(
response => console.log('Upload complete:', response.response),
err => console.error('Upload failed:', err)
);
}
});import { ajax } from "rxjs/ajax";
import { shareReplay, tap } from "rxjs/operators";
class CachedHttpClient {
private cache = new Map<string, Observable<any>>();
get<T>(url: string, cacheKey?: string): Observable<T> {
const key = cacheKey || url;
if (!this.cache.has(key)) {
const request$ = ajax.getJSON<T>(url).pipe(
tap(data => console.log(`Cached: ${key}`)),
shareReplay(1) // Share and replay last emission
);
this.cache.set(key, request$);
// Auto-expire cache after 5 minutes
setTimeout(() => {
this.cache.delete(key);
console.log(`Cache expired: ${key}`);
}, 5 * 60 * 1000);
}
return this.cache.get(key)!;
}
clearCache(key?: string) {
if (key) {
this.cache.delete(key);
} else {
this.cache.clear();
}
}
}
const cachedClient = new CachedHttpClient();
// First call - makes HTTP request
cachedClient.get<User[]>('/api/users').subscribe(users => {
console.log('Users (from server):', users);
});
// Second call - returns cached data
cachedClient.get<User[]>('/api/users').subscribe(users => {
console.log('Users (from cache):', users);
});import { ajax } from "rxjs/ajax";
import { forkJoin, combineLatest } from "rxjs";
import { map } from "rxjs/operators";
// Load multiple resources in parallel
const userRequest$ = ajax.getJSON<User>('/api/user/123');
const postsRequest$ = ajax.getJSON<Post[]>('/api/user/123/posts');
const commentsRequest$ = ajax.getJSON<Comment[]>('/api/user/123/comments');
// Wait for all to complete
forkJoin({
user: userRequest$,
posts: postsRequest$,
comments: commentsRequest$
}).subscribe(({ user, posts, comments }) => {
console.log('All data loaded:', { user, posts, comments });
});
// Combine latest values as they arrive
combineLatest([
userRequest$,
postsRequest$,
commentsRequest$
]).pipe(
map(([user, posts, comments]) => ({ user, posts, comments }))
).subscribe(data => {
console.log('Combined data:', data);
});interface AjaxRequest extends AjaxConfig {
url: string;
method: string;
}
type AjaxDirection = "upload" | "download";
interface User {
id: number;
name: string;
email: string;
}
interface Post {
id: number;
title: string;
content: string;
userId: number;
}
interface Comment {
id: number;
text: string;
postId: number;
userId: number;
}