One source of confusion when working with AJAX requests and Aurelia is whether you should use the aurelia-http-client
or aurelia-fetch-client
both achieve similar things. However at the time of writing this no official documentation that exists for using the Fetch Client or explains how to use it.
I have been using the Fetch client since it debuted and realised others wanting to migrate to the Fetch client might find this post useful.
The Aurelia Fetch client is merely a thin wrapper around the native Fetch specification. So anything detailed in the Fetch specification can be achieved using the Aurelia Fetch client albeit the way the wrapper expects them to be done (which in most cases is the same as the spec).
Here are some basic things you might want to achieve using Aurelia Fetch client below like setting base URL’s, working with credentials, caching and more.
Polyfill alert: If you are planning on using Aurelia’s Fetch client you need to use a Fetch polyfill to plug browsers that do not support it that well. The Skeleton application uses Github’s Fetch API polyfill which is the best one out there at the moment.
Specifying a baseUrl
Before each request is made, the URL is prefixed with the supplied baseUrl.
import {HttpClient} from 'aurelia-fetch-client';
@inject(HttpClient)
export class MyClass {
constructor(http) {
http.configure(config => {
config
.withBaseUrl('someBaseUrl/');
});
this.http = http;
}
}
Working with credentials
Similar to XMLHttpRequest’s withCredentials property. Read more about the credentials property here.
import {HttpClient} from 'aurelia-fetch-client';
@inject(HttpClient)
export class MyClass {
constructor(http) {
http.configure(config => {
config
.withDefaults({
credentials: 'same-origin' // Valid values; omit, same-origin and include
});
});
this.http = http;
}
}
Specifying custom headers
Using the same withDefaults
method above, we can specify one or more custom senders to send with each request. Read more about the headers property here.
For every request
If you want some custom headers to be sent with every request, then using the withDefaults
method does this for you.
import {HttpClient} from 'aurelia-fetch-client';
@inject(HttpClient)
export class MyClass {
constructor(http) {
http.configure(config => {
config
.withDefaults({
headers: {
'Accept': 'application/json'
}
});
});
this.http = http;
}
}
Per request
Sometimes you want to send custom headers for a specific request, not every request like above. The code isn’t really that different. As you can see the fetch method has an optional parameter which allows us to provide an object of option values.
import {HttpClient} from 'aurelia-fetch-client';
@inject(HttpClient)
export class MyClass {
constructor(http) {
this.http = http;
}
getSomeJson() {
this.http.fetch('something', {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
// More options
}
})
.then(response => response.json())
.then(data => {
console.log(data);
})
}
}
Intercepting requests with interceptors
Pew, pew. Fire your lasers and intercept requests and response using the withInterceptor
method.
import {HttpClient} from 'aurelia-fetch-client';
@inject(HttpClient)
export class MyClass {
constructor(http) {
http.configure(config => {
config
.withInterceptor({
request(request) {
console.log(`Intercepted request using method: ${request.method} with URL: ${request.url}`);
return request;
},
response(response) {
console.log(`Intercepted response ${response.status} using URL: ${response.url}`);
return response;
}
});
});
this.http = http;
}
}
Requesting JSON
Probably the most common use-case is communicating with an API and expecting some sweet-sweet JSON to come back.
import {HttpClient} from 'aurelia-fetch-client';
@inject(HttpClient)
export class MyClass {
constructor(http) {
this.http = http;
}
getSomeJson() {
this.http.fetch('something')
.then(response => response.json())
.then(data => {
console.log(data);
})
}
}
Posting JSON
Sometimes you want to also give JSON, not just receive it. Your application might register a new user or create a page by sending a populated object to the server. Fetch with some Aurelia lovin’ has you covered here as well.
import {HttpClient, json} from 'aurelia-fetch-client';
@inject(HttpClient)
export class MyClass {
constructor(http) {
this.http = http;
}
postSomeJson(user) {
this.http.fetch('users', {
method: 'post',
body: json(user)
})
}
}
Conclusion
I probably forgot some things here. If there is something you think should be added here, something doesn’t work is not clear or you have a suggestion of your own, please leave a comment below.
In my opinion the aurelia documentation is not sufficient for production ready code. Just deactivate the webserver and you will see that the “.then” promise will still be executed leading to other error messages of the aurelia framework.
I thought this can be handled by adding requestError(data) and responseError(data) to the config. Both of these functions are returning an empty json instead of throwing the data. But so far I did not have luck with this approach. Perhaps you have better ideas to properly handle communication errors? At least I want to alert the user and secondly I would like to execute other code than the “.then” promise.
I think I confused the handlers requestError and responseError for something that is better achieved with a catch handler:
.then(data => {…})
.catch(error => {…})
The only problem I now have is that the error object is always empty in my tests. Thus I find it difficult to inform the user with more details about the message. For that the statuscode and exception message of the fetch communication would be helpful.
Did anyone figure out how to catch the errors from the response?
It is possible to change the Content-Type when using json()? I want to send something like “Content-Type: ‘application/vnd.api+json'”, but json() by defaults set the Content-Type to “application/json” and it ignores the defaults headers and also the by request headers
@James
I’ll be updating this post with a section on capturing errors shortly. There are definitely a couple of ways you can capture errors in your Fetch requests. Sadly, the documentation for Fetch is still limited, so I’ll try and make this post more comprehensive.
@Hector
Do you mean you want to change the content type of a GET or POST request? Like you want the content type changed when sending JSON to the server or getting it back?
Is there any way to pass request parameters?
For example aurelia-http-client has createRequest builder function, which gives helper function named withParams()
Some documentation on error handling for fetch requests would be greatly appreciated!
Thank you
It’s a shame that most of Aurelia’s best ‘documentation’ is still in the form of community blog posts like these. But kudos for the post, and I definitely second the request for more detail on properly handling errors. I would also really appreciate seeing how to implement a request timeout!
Could you specify more clearly with an example of how to connect to webapi and get data from it.I am new to webapi concept and I am not finding enough information in any site.
Thanks in advance.
@Andrew I’m quite new to Aurelia and specifically to its fetch client. What I have found is that if you want to catch errors returned through the header of the response (e.g. 401, 404 errors etc.), you can add an interceptor during the configuration of the client like this :
[code]
.withInterceptor({ response(response) { // MODIFY TO TAKE CAR OF OTHER 2XX SUCCESS CODES
if (response.status !== 200)
{
throw response;
}
else
{
return response;
}
}})
[/code]
Then when you make the request, add the “.catch” after all “.then” :
[code]
.catch(error => {
// Will log the http error status code
// Add any other actions here
console.log(error.status); });
[/code]
Hope this helps!
Great article.
Do you have a doc like this about the http-client?
Does the fetch client work for CORS POST requests? In my tests it only sends the OPTIONS post but not the following POST request. If I switch out code for jquery Ajax works fine.
Hi Dwayne, mayby you can help me?
During fetching data I’m getting error: Error:
A value is required for route parameter ‘id’ in route
The problem is because page is rendered before data are loaded and transformed by Aurelia to Promises:
this.client.fetch(‘/GetData’).then(response => response.json()).then(data => {
this.myData = data;
});
The only solution which is working for me is using if.bind=”itemFromMyData” in the repeat.for, but I don’t like because I’d like to force app to render page after all data transformations.
Do you have any idea?
What’s the difference with native fetch API, or what’s the benefits? Since I saw most of these features is implemented by fetch API.
I am using this, but want to know if I can switch to native fetch API without lose some important features.
Thanks for the article. One big unanswered for me though: How do you use responseError() and requestError() to detect, for example, when the web api is offline or unreachable?
There is a file (main.js maybe) where I can set the baseUrl for all my requests?
I’m starting in Aurelia, so sorry if is a stupid question.
Hi Dwayne, any chance of adding something on caching? I want to cache certain codes and lists, but if I manually cache the object then I open a memory leak.
How does the built-in cache work, and can I specify an expiration policy?
Thanks!