Friday, 28 April 2017

Error handling in SPFx when using TypeScript Async/Await

This is a quick follow up post to my previous post Using TypeScript async/await to simplify your SharePoint Framework code.

Since publishing my previous post, some folks have asked how would the error handling work when using async/await in SharePoint Framework. So decided to make this short how-to post.

At first glance, it is really straightforward, you essentially have to wrap your code in a try..catch block and the typescript compiler does the rest.

But there is something interesting here. Under the hood, SharePoint Framework uses the fetch API to make http requests. MDN has some great info on the fetch API if you haven't seen it already: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API

What would be surprising for some (including me) is that the fetch API rejects the promise only when network errors occur. A "404 Not found" is considered as valid response and the Promise is resolved.

E.g. If you make a get request to a SharePoint list which does not exist, the fetch API (and the SharePoint Framework) will actually resolve your Promise.

A fetch() promise will reject with a TypeError when a network error is encountered, although this usually means permission issues or similar — a 404 does not constitute a network error, for example. An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true.

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful

There has also been some discusson on this on the SharePoint Framewok GitHub repo:
https://github.com/SharePoint/sp-dev-docs/issues/184

This is why, in addition to a try..catch block, you have to also check the Response.ok property of the returned Response. Here is the basic code you need to handle errors when using async/await:

private async getListId() {
try {
const response = await this.context.spHttpClient.get(`/_api/web/lists/GetByTitle('Does-Not-Exist')`, SPHttpClient.configurations.v1);
//Since the fetch API only throws errors on network failure, We have to explicitly check for 404's etc.
if (!response.ok) {
const responseText = await response.text();
throw new Error(responseText);
};
//If request was successful
const list: IODataList = await response.json();
console.log(list.Id);
} catch (error) {
console.log(error);
}
}

What you can also do is, since the fetch API returns promises, you can use chaining to detect if the Response.ok property is false. Have a look at this post: Handling Failed HTTP Responses With fetch()

Update 6th May 2017: Here is a slightly more complex example where I have built a service with a custom get method which also handles errors for you:

import { SPHttpClient, SPHttpClientResponse, SPHttpClientConfiguration, ODataVersion } from '@microsoft/sp-http';
import { ISPFxFetcher } from '../interfaces';
export class SPFxFetcher implements ISPFxFetcher {
private _spHttpClient: SPHttpClient;
constructor(spHttpClient: SPHttpClient) {
this._spHttpClient = spHttpClient;
}
public get(endpoint: string, config: SPHttpClientConfiguration = SPHttpClient.configurations.v1): Promise<any> {
if(endpoint.indexOf("/_api/search/") != -1){
config = config.overrideWith({ defaultODataVersion: ODataVersion.v3 });
}
return this._spHttpClient.get(endpoint, config).then(this.processResponse);
}
private processResponse(response: SPHttpClientResponse): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (response.ok) {
response.json().then((responseJSON) => {
resolve(responseJSON);
});
}
else {
response.text().then((responseText) => {
reject(responseText);
});
}
});
}
}
view raw spfxfetcher.ts hosted with ❤ by GitHub
And here is how to consume the service from your SPFx webpart:

private async runCode() {
try {
const _sPFXFetcher: SPFxFetcher = new SPFxFetcher(this.context.spHttpClient);
const currentWebUrl: string = this.context.pageContext.web.absoluteUrl;
const _list: IODataList = await _sPFXFetcher.get(`${currentWebUrl}/_api/lists/GetByTitle('Does-Not-Exist')`); //Will throw exception
console.log(_list);
const results: JSON = await _sPFXFetcher.get(`${currentWebUrl}/_api/search/query?querytext='sharepoint'`);
console.log(results);
} catch (error) {
console.log(error);
}
}
Here is the GitHub repository for the service if you are interested:
https://github.com/vman/SPFx-Fetcher


Hope you find this useful!

1 comment:

Comments moderation is turned ON. Your comment might not appear immediately after posting.