LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • 0001 to 0023
      • 0088 to 0723
      • 0901 to 0908
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page
  • Basic Usage
  • Configuring the fetch client
  • Fetch helpers
  • Error Handling and Recovery
  • Retrying failed requests

Was this helpful?

Export as PDF
  1. Aurelia Packages
  2. Fetch Client

Setup and Configuration

The Fetch Client can be used in a couple of different ways. You can create a new instance using the new keyboard or use dependency injection to create an instance.

Basic Usage

Here's a quick example of how to set up and make a GET request with the Aurelia Fetch Client in an Aurelia 2 application. The Fetch client ships with Aurelia and requires no additional installation. Import the HttpClient from the @aurelia/fetch-client package to use it.

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

const httpClient = new HttpClient();

httpClient.configure(config => {
  config
    .withDefaults({ mode: 'cors' })
    .withBaseUrl('https://api.example.com/');
});

httpClient.get('users')
  .then(response => response.json())
  .then(users => console.log(users))
  .catch(error => console.error(error));

You can inject a new instance into your component or service class by injecting the Fetch client with the newInstanceOf decorator. This will ensure our component gets a new instance of the Fetch client.


import { IHttpClient } from '@aurelia/fetch-client';
import { resolve, newInstanceOf } from '@aurelia/kernel';
import { inject } from 'aurelia';

export class MyComponent {
    http = resolve(newInstanceOf(IHttpClient))
}

You should avoid creating new instances of the Fetch client. Instead, you should create a service class or wrapper functionality that encapsulates your HTTP calls.

Configuring the fetch client

Many configuration options available to the native Fetch API are also available in the Aurelia Fetch Client. You can set default headers, create interceptors (more on that further down) and more.

import { IHttpClient } from '@aurelia/fetch-client';
import { newInstanceOf. resolve } from '@aurelia/kernel';
import { ICustomElementViewModel } from 'aurelia';

export class MyComponent implements ICustomElementViewModel {
    constructor(readonly http: IHttpClient= resolve(newInstanceOf(IHttpClient))) {
      http.configure(config =>
        config
        .withBaseUrl('api/')
        .withDefaults({
          credentials: 'same-origin',
          headers: {
            'Accept': 'application/json',
            'X-Requested-With': 'Fetch'
          }
        })
        .withInterceptor({
          request(request) {
            console.log(`Requesting ${request.method} ${request.url}`);
            return request;
          },
          response(response) {
            console.log(`Received ${response.status} ${response.url}`);
            return response;
          }
        })
      );
    }
 }
import { IHttpClient } from '@aurelia/fetch-client';
import { newInstanceOf, resolve } from '@aurelia/kernel';

export class MyComponent {
    constructor(http = resolve(newInstanceOf(IHttpClient))) {
        this.http = http

        this.http.configure(config =>
          config
          .withBaseUrl('api/')
          .withDefaults({
            credentials: 'same-origin',
            headers: {
              'Accept': 'application/json',
              'X-Requested-With': 'Fetch'
            }
          })
          .withInterceptor({
            request(request) {
              console.log(`Requesting ${request.method} ${request.url}`);
              return request;
            },
            response(response) {
              console.log(`Received ${response.status} ${response.url}`);
              return response;
            }
          })
        );
    }
 }
  • requestError acts as a Promise rejection handler during Request creation and request interceptor execution. It will receive the rejection reason and can either re-throw or recover by returning a valid Request.

  • responseError is similar to requestError and acts as a Promise rejection handler for response rejections.

These methods on the interceptor object can also return a Promisefor their respective return values.

Fetch helpers

There are some caveats with the default Fetch implementation around error handling that Aurelia conveniently provides helper methods to work with.

  • config.rejectErrorResponses() will add a response interceptor that causes responses with unsuccessful status codes to result in a rejected Promise.

  • config.useStandardConfiguration() will apply rejectErrorResponses(), and also configure credentials: 'same-origin' as a default on all requests.

  • The Fetch API has no convenient way of sending JSON in the body of a request. Objects must be manually serialized to JSON, and the Content-Type header must be set appropriately. the Fetch package includes a helper called json for this.

Posting JSON

import { IHttpClient, json } from '@aurelia/fetch-client';
import { newInstanceOf, resolve } from '@aurelia/kernel';
import { ICustomElementViewModel } from 'aurelia';

export class MyComponent implements ICustomElementViewModel {
    constructor(readonly http: IHttpClient = resolve(newInstanceOf(IHttpClient)) {

    }

    createComment() {
        let comment = {
          title: 'Awesome!',
          content: 'This Fetch client is pretty rad.'
        };

        this.http.fetch('comments', {
          method: 'post',
          body: json(comment)
        });
    }
 }
import { IHttpClient, json } from '@aurelia/fetch-client';
import { newInstanceOf, resolve } from '@aurelia/kernel';

export class MyComponent {
    constructor(http = resolve(newInstanceOf(IHttpClient))) {
        this.http = http
    }

    createComment() {
        let comment = {
          title: 'Awesome!',
          content: 'This Fetch client is pretty rad.'
        };

        this.http.fetch('comments', {
          method: 'post',
          body: json(comment)
        });
    }
 }

For the example above, if you prefer .get/.post/etc.. style, you can also use corresponding method on the Fetch client

  ...
  this.http.post('comments', { body: JSON(comment) })

Error Handling and Recovery

Robust error handling is crucial for any application making HTTP requests. It involves not only catching and responding to errors but also implementing strategies for error recovery and user notification.

http.configure(config => {
    config.withInterceptor({
        response(response) {
            if (!response.ok) {
                handleError(response);
            }
            return response;
        },
        responseError(error) {
            handleError(error);
            throw error; // Rethrow error after handling
        }
    });
});

function handleError(error) {
    // Centralized error handling logic
    console.error('Fetch Error:', error);
    // Additional logic like logging, user notification, etc.
}

Retrying failed requests

The Fetch client comes with a retry implementation that can be configured like the following example

http.configure(config => config.withRetry(retryOptions))

There are several options can be specified, per the following type:

export interface IRetryConfiguration {
  maxRetries: number;
  interval?: number;
  strategy?: number | ((retryCount: number) => number);
  minRandomInterval?: number;
  maxRandomInterval?: number;
  counter?: number;
  requestClone?: Request;
  doRetry?(response: Response, request: Request): boolean | Promise<boolean>;
  beforeRetry?(request: Request, client: HttpClient): Request | Promise<Request>;
}

Note that for option strategy, there are 4 default strategies provided via the export RetryStrategy from the @aurelia/fetch-client package:

export const RetryStrategy: {
  fixed: 0;
  incremental: 1;
  exponential: 2;
  random: 3;
}

Per the names suggest, the interval which a request will be attempted again will be calcuated accordingly for each strategy. If you want to supply your own strategy, the strategy option can take a callback to be invoked with the number of the retry and the return value is treated as the time to wait until the next fetch attempt.

PreviousFetch ClientNextResponse types

Last updated 1 year ago

Was this helpful?

In the example above, withBaseUrl() is used to specify a base URL that all fetches will be relative to. The withDefaults() method allows passing an object that can include any properties described in the optional init parameter to the and will be merged into the new before it is passed to the first request interceptor.

withInterceptor() enables passing an object which can provide any of these four optional methods: request, requestError, response, and responseError. Here's an explanation of how each of these methods works:request takes the that will be passed to window.fetch() after interceptors run. It should return the same Request or create a new one. It can also return a to short-circuit the call to fetch() and complete the request immediately. Interceptors will handle errors thrown in request interceptors.

response will be run after fetch() completes, and will receive the resulting . As with request, it can either pass the Response along, return a modified response, or throw.

Request constructor
Request
Request
Response
Response