WakaSync HTTP Integration

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.

The Basics

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>

HTTP Methods

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');

Request Cancellation

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>

Configuration Options

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'

Request Options

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

Advanced Features

File Uploads

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);
}

Automatic Retry Logic

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');
}

Response Type Control

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'
});

Request and Response Interceptors

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 Handling

Error Types

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)

Error Handling Pattern

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 provided
  • HTTP_404, HTTP_500, etc. - HTTP status code errors
  • PARSE_ERROR - Failed to parse response body
  • CANCEL_TIMEOUT - Request timed out
  • CANCEL_CANCELLED - Manually cancelled
  • CANCEL_SUPERSEDED - Replaced by newer request
  • INTERCEPTOR_ERROR - Request or response interceptor failed

Best Practices

  • Use underscore prefix: Store as _http to avoid reactivity overhead on the HTTP client itself
  • Initialize in init(): Create WakaSync instances in the init() lifecycle method
  • Use groupKey for searches: Always cancel previous search requests to prevent race conditions and stale results
  • Handle loading states: Show loading indicators and disable interactive elements during requests
  • Always catch errors: Use try-catch with async/await and handle both cancellation and HTTP errors appropriately
  • Set reasonable timeouts: Default 30s is fine for most APIs, but adjust based on expected response times
  • Check for cancellation: Don't show error messages for AbortError or CancellationError
  • Clean up on destroy: Call this._http.cancelAll() in cleanup if component can be destroyed mid-request
Performance Tip: WakaSync automatically parses JSON responses based on Content-Type headers. Use responseType: 'text' or responseType: 'blob' only when needed to avoid unnecessary parsing overhead.