Frontend JavaScript TypeScript

Using Interceptors with aurelia-fetch-client

In today’s post, we are going to explore another feature of the Aurelia framework or more precisely aurelia-fetch-client. So, in many cases, it would be nice to perform some specific actions when doing an AJAX request. For instance, before sending a request we want to log it into browser database like PouchDb. A more common example is calling a toastr on some error like 404, 500, 401. The problem with such a scenarios is that many developers duplicate their code in every single request/callback so they break the DRY rule (which stands for don’t repeat yourself). Fortunately aurelia-fetch-client provides easy solution – Interceptors.

 

Exploring Interceptors

In order to register our own interceptor, we need to call the configure function of the HttpClient instance. Then inside that we have an access to the withInterceptor method of the HttpClientConfiguration object. If you’re confused here’s the implementation:

 


httpClient.configure(config =>
{
    config.withInterceptor(); //parms: interceptor: Interceptor
});

 

Now, as you can see the function accepts the object which implements the Interceptor interface. Let’s click F12 and navigate to the *.d.ts file:

 


export declare interface Interceptor
{
    request?: (request: Request) => Request | Response | Promise<Request | Response>;

    requestError?: (error: any) => Request | Response | Promise<Request | Response>;

    response?: (response: Response, request?: Request) => Response | Promise<Response>;

    responseError?: (error: any, request?: Request) => Response | Promise<Response>;
}

 

So basically we have four functions here. It’s worthwhile to mention that all of them are optional in the derived types (the ? operator usage). Let’s discuss each of the functions (I’ll paste the original comments removed from the code due to its simplicity):

 

  • request – Called with the request before it is sent. Request interceptors can modify and return the request, or return a new one to be sent. If desired, the interceptor may return a Response in order to short-circuit the HTTP request itself.
  • requestError – Handles errors generated by previous request interceptors. This function acts as a Promise rejection handler. It may rethrow the error to propagate the failure, or return a new Request or Response to recover.
  • response – Called with the response after it is received. Response interceptors can modify and return the Response, or create a new one to be returned to the caller.
  • responseError – Handles fetch errors and errors generated by previous interceptors. This function acts as a Promise rejection handler. It may rethrow the error to propagate the failure, or return a new Response to recover.

 

As presented, we’ve got a set of functions which should handle most of our specific scenarios related to the AJAX calls. Let’s implement some simple example, so we can check whether it’s working.

 

The implementation

So, in our simple example, we’re going to implement only the request and responseError functions. Our first step is to create a class which implements discussed interface. The implementation looks as follows:

 


export class SimpleInterceptor implements Interceptor
{
    request(request: Request)
    {
        console.log(`I am inside of the interceptor doing a new request to ${request.url}`);
        return request;
    }

    responseError(response: Response)
    {
        console.log('Some error has occured! Run!')
        return response;
    }
}

 

The code is bloody simple therefore, I’ll not comment it. Now we need to pass the new instance of our SimpleInterceptor class to the withInterceptor function:

 


httpClient.configure(config =>
{
    config.withInterceptor(new SimpleInterceptor());
});

 

Now, I’m going to make a GET request to my blog (http://foreverframe.net). Here’s the result in the console:

 

Screen Shot 2016-11-27 at 23.32.14 (2)

 

Everything works as expected 馃檪

 

Summary

The above example is trivial but I don’t see a point of doing something more complicated since in most cases the problems are „project specific”. All you should remember is the fact that Aurelia’s fetch client provides this solution which might be handy in the future 馃槈 That’s all for today. As always I encourage you to follow me on Twitter and Facebook so you can be up to date with new post 馃檪