Build a polling request implementation with the Aurelia 2 Fetch Client

Sometimes you have a request that doesn’t immediately return a response. For example, the user uploads some audio files and you process those and return them.

Using the Aurelia 2 Fetch Client, we are going to build an implementation that allows us to poll a specific endpoint and use callback functions to configure our response.

import { HttpClient } from '@aurelia/fetch-client';

export interface PollingOptions {
  pollingIntervalMs?: number;
  maxPollingAttempts?: number;
  isSuccess?: (response: Response) => boolean;
  parse?: (response: Response) => Promise;
}

export class PollingHttpClient {
  private readonly httpClient: HttpClient;
  private readonly defaultPollingIntervalMs = 1000;
  private readonly defaultMaxPollingAttempts = 10;

  constructor() {
	this.httpClient = new HttpClient();
  }

  async pollUntilSuccess(
    url: string,
    options?: RequestInit,
    pollingOptions?: PollingOptions
  ): Promise {
    const pollingIntervalMs = pollingOptions?.pollingIntervalMs ?? this.defaultPollingIntervalMs;
    const maxPollingAttempts = pollingOptions?.maxPollingAttempts ?? this.defaultMaxPollingAttempts;
    const isSuccess = pollingOptions?.isSuccess ?? ((response) => response.ok);
    const parse = pollingOptions?.parse ?? ((response) => response.json() as Promise);

    let attempts = 0;
    while (attempts < maxPollingAttempts) {
      try {
        const response = await this.httpClient.fetch(url, options);
        if (isSuccess(response)) {
          return await parse(response);
        }
      } catch (error) {
        console.error('Fetch error:', error);
      }
      attempts++;
      await new Promise(resolve => setTimeout(resolve, pollingIntervalMs));
    }
    throw new Error(\`Polling failed after ${maxPollingAttempts} attempts.\`);
  }
}

To use it, simply instantiate it like you would the ordinary Fetch client:

const pollingClient = new PollingHttpClient();

async function fetchData() {
  try {
    const data = await pollingClient.pollUntilSuccess('https://api.example.com/data', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      }
    }, {
      pollingIntervalMs: 2000, // 2 seconds
      maxPollingAttempts: 5,
      isSuccess: (response) => response.status === 200, // Customize success condition
      parse: async (response) => {
        // Custom parsing logic, if needed
        const result = await response.json();
        // Further processing of result if necessary
        return result;
      }
    });

    console.log('Successfully fetched data:', data);
  } catch (error) {
    console.error('Failed to fetch data:', error);
  }
}

fetchData();

In our example, a successful response is when the server returns a 200 status code to indicate a successful response.