WakaSync is an advanced HTTP client designed to work seamlessly with WakaPAC. It provides request cancellation, automatic retry logic, timeout handling, and intelligent response parsing.
Create a WakaSync instance in your component's init() method and use it to make HTTP requests:
<div id="app">
<div data-pac-bind="visible: loading">Loading...</div>
<div data-pac-bind="if: user">
<h2>{{user.name}}</h2>
<p>{{user.email}}</p>
</div>
<div data-pac-bind="visible: error">Error: {{error}}</div>
</div>
<script>
wakaPAC('#app', {
user: null,
loading: false,
error: null,
_http: null,
init() {
// Create HTTP client instance
this._http = new WakaSync({
timeout: 10000,
retries: 1
});
this.loadUser();
},
async loadUser() {
this.loading = true;
this.error = null;
try {
this.user = await this._http.get('/api/user');
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
}
});
</script>
WakaSync provides convenience methods for all standard HTTP verbs:
// GET request
const users = await http.get('/api/users');
// POST with data
const newUser = await http.post('/api/users', {
name: 'Alice',
email: 'alice@example.com'
});
// PUT - full replacement
await http.put('/api/users/123', {
name: 'Alice Smith',
email: 'alice@example.com'
});
// PATCH - partial update
await http.patch('/api/users/123', {
lastLogin: new Date().toISOString()
});
// DELETE request
await http.delete('/api/users/123');
// HEAD request (no body returned)
await http.head('/api/users/123');
Use groupKey to cancel previous requests and prevent race conditions in search interfaces:
<div id="app">
<input data-pac-bind="value: searchQuery" placeholder="Search...">
<div data-pac-bind="visible: searching">Searching...</div>
<ul data-pac-bind="foreach: results" data-pac-item="result">
<li>{{result.title}}</li>
</ul>
</div>
<script>
wakaPAC('#app', {
searchQuery: '',
results: [],
searching: false,
_http: null,
init() {
this._http = new WakaSync();
},
watch: {
async searchQuery(query) {
if (!query) {
this.results = [];
return;
}
// Cancel previous search requests before starting new one
this._http.cancelGroup('search');
this.searching = true;
try {
this.results = await this._http.get('/api/search', {
params: { q: query },
groupKey: 'search' // Tag for cancellation
});
} catch (err) {
// Cancelled requests throw CancellationError/AbortError
if (err.name !== 'AbortError' && err.name !== 'CancellationError') {
console.error('Search failed:', err);
}
} finally {
this.searching = false;
}
}
}
});
</script>
Configure WakaSync globally when creating an instance:
| Option | Default | Description |
|---|---|---|
timeout |
30000 | Request timeout in milliseconds (0 = no timeout) |
retries |
0 | Number of retry attempts on network failure |
retryDelay |
1000 | Delay between retries in milliseconds |
headers |
{} | Default headers applied to all requests |
responseType |
'auto' | Response parsing: 'json', 'text', 'blob', 'response', 'auto' |
Override configuration per-request using the options parameter:
| Option | Description |
|---|---|
groupKey |
Group identifier for batch cancellation |
latestOnly |
Auto-cancel previous requests to the same URL |
params |
Object of query parameters to append to URL |
headers |
Request-specific headers (merged with defaults) |
onSuccess |
Callback function invoked on successful response |
onError |
Callback function invoked on error |
ignoreAbort |
Return undefined instead of throwing on cancellation |
WakaSync automatically detects FormData and skips setting Content-Type header, allowing the browser to set the correct multipart boundary. No special configuration needed.
async uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('description', 'My file');
// Browser automatically sets Content-Type with boundary
const result = await this._http.post('/api/upload', formData);
console.log('Uploaded:', result);
}
WakaSync retries requests that fail due to network errors, 5xx server errors, or 429 (rate limiting). It does NOT retry 4xx client errors (like 404, 403) since these indicate the request itself is wrong. Retries are aborted if the request is cancelled during the delay.
init() {
this._http = new WakaSync({
retries: 3, // Retry up to 3 times
retryDelay: 2000 // Wait 2 seconds between retries
});
}
async loadWithRetry() {
// Will retry on: network failures, 500/502/503, 429
// Will NOT retry on: 404, 403, 401, etc.
const data = await this._http.get('/api/unstable-endpoint');
}
By default, WakaSync auto-detects response type from Content-Type header. Override this when you need specific handling:
// Download binary file (images, PDFs, etc.)
const blob = await this._http.get('/api/download', {
responseType: 'blob'
});
const url = URL.createObjectURL(blob);
// Get raw Response object for streaming or custom parsing
const response = await this._http.get('/api/data', {
responseType: 'response'
});
const reader = response.body.getReader();
// Force text parsing even if server sends wrong Content-Type
const html = await this._http.get('/api/page', {
responseType: 'text'
});
Interceptors allow you to transform requests before they're sent and responses after they're received. This is useful for adding authentication, logging, error tracking, or data transformation globally.
wakaPAC('#app', {
init() {
this._http = new WakaSync();
// Add authentication token to all requests
this._http.interceptors.request.push(function(config) {
config.headers.set('Authorization', `Bearer ${getToken()}`);
return config;
});
// Log all requests
this._http.interceptors.request.push(function(config) {
console.log(`[HTTP] ${config.method} ${config.url}`);
return config;
});
// Add timestamp to all responses
this._http.interceptors.response.push(function(data) {
return {
...data,
_receivedAt: Date.now()
};
});
// Global error tracking
this._http.interceptors.response.push(function(data, config) {
if (data && data.error) {
trackError(data.error, config.url);
}
return data;
});
}
});
Important: Interceptors must return the modified config or data. If you modify in-place without returning, the fallback will use the original value.
| Error Name | Cause |
|---|---|
AbortError |
Request cancelled via cancelGroup() or timeout |
CancellationError |
Request cancelled internally (superseded, etc.) |
Error (HTTP status) |
HTTP error response (4xx, 5xx status codes) |
try {
const data = await this._http.get('/api/data');
} catch (err) {
// Check if cancelled
if (err.name === 'AbortError' || err.name === 'CancellationError') {
console.log('Request was cancelled');
return;
}
// HTTP error - includes err.code like 'HTTP_404', 'HTTP_500'
if (err.response) {
console.error(`HTTP ${err.response.status}:`, err.message);
console.error('Error code:', err.code);
} else {
// Network error or parse error
console.error('Error:', err.message, err.code);
}
}
All errors include an error.code property for programmatic handling:
INVALID_URL - Malformed URL providedHTTP_404, HTTP_500, etc. - HTTP status code errorsPARSE_ERROR - Failed to parse response bodyCANCEL_TIMEOUT - Request timed outCANCEL_CANCELLED - Manually cancelledCANCEL_SUPERSEDED - Replaced by newer requestINTERCEPTOR_ERROR - Request or response interceptor failed_http to avoid reactivity overhead on the HTTP client itselfinit() lifecycle methodthis._http.cancelAll() in cleanup if component can be destroyed mid-requestresponseType: 'text' or responseType: 'blob' only when needed to avoid unnecessary parsing overhead.