A cleaner way to prepare requests

Oops, I’ve messed up something in the repository and changes described in the previous post disappeared… Fortunately there wasn’t that much to restore and code cited in the previous post helped, so I’ll add something else. I wanted to define a request timeout to catch situations when a database is down. The default timeout is probably 30 seconds long and it’s too much for me to wait. I didn’t want to bother about setting timeouts everywhere so I’ve extracted a method for executing requests to the API. There are 2 common parts of executing a request: converting it to a promise and defining its “catch” block. So the common method gets content of a “then” block as a parameter and performs those two things:

import { Injectable } from "@angular/core";
import { Response as HttpResponse } from '@angular/http';

import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/timeout';
import { Observable } from "rxjs/Observable";
import { TimeoutError } from "rxjs/util/TimeoutError";

import { LogService } from "./log.service";

@Injectable()
export class RequestHelper {
    constructor(private logService: LogService) { }

    getPromiseFromRequest<TResult>(request: Observable<HttpResponse>, thenCallback: (response: HttpResponse) => TResult): Promise<TResult> {
        return request.timeout(3000).toPromise()
            .then(response =>
                thenCallback(response)
            ).catch(reason => {
                if (reason instanceof TimeoutError) {
                    this.logService.log(reason.stack);
                    throw new Error();
                }
                else {
                    this.logService.log(reason.json());
                }
            });
    }
}

The purpose of throwing a new error is only to activate a “catch” block in a consumer of a service – in order to display a message. There is no need to have the original stack trace.

There was a little confusing error:

Types of parameters ‘response’ and ‘response’ are incompatible. Type ‘Response’ is not assignable to type ‘Response’. Two different types with this name exist, but they are unrelated.

because there are 2 predefined types called Response – one is a class in the @angular/http module, the other is an interface in typescript. That’s why I had to import it with an alias:

import { Response as HttpResponse } from '@angular/http';

Could be better if the error message already contained a hint from where I can import…

And a usage is:


...
export class CategoryService {
    constructor(private http: Http, private requestHelper: RequestHelper) { }

    private getCategoriesCallback(response: HttpResponse) {
        let categories: any[] = response.json();
        let result: Category[] = categories.map(category => new Category(category.Id, category.Name));
        return result;
    }

    getCategories(): Promise<Category[]> {
        let url = Constants.urlBase + `categories`;
        return this.requestHelper.getPromiseFromRequest(this.http.get(url), this.getCategoriesCallback);
    }
};

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s