In the near past, when I just started to learn Ionic and Angular I had a big headache because I needed quickly to create mobile app with technologies that I didn't know well.

One of the problem was to do simple API requests to back-end server. There was a lot of solutions like creating custom function or wrappers but at that moment (November 2016) no one tried simply to extend Angular HTTP class and so here we are - today I want to share my solution to this problem.

So the main idea is to use token-based authentication: grab HttpClient and extend it by adding custom auth header with our token to each request it does and check this token on server.

Creating service provider

The first thing we will need to do is create a new service provider by executing the command ionic generate provider APIRequest.

We also will add there Storage in order to get the access token which was saved before.

import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http';

import { Storage } from '@ionic/storage';

import { Observable } from 'rxjs/Observable';

import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/observable/fromPromise';

export class APIRequest extends Http {

	private API_SERVER = 'localhost';

	constructor (
		backend: XHRBackend, 
		options: RequestOptions,

		private storage: Storage,
	) {
		super(backend, options)
	}

	request(url: string|Request, options?: RequestOptionsArgs): Observable <any> {
		// Convert the Promise to an Observable
		let storage_observable = Observable.fromPromise(this.storage.get('access_token'));

		// rxjs/add/operator/mergeMap
		return storage_observable.flatMap(auth_data => {
			let headers = null
			
			// Prepare URL
			if (typeof url === 'string') {
				if (!options) {
					options = {
						headers: new Headers()
					}
				}

				headers = options.headers;

				url = this.API_SERVER + (url.startsWith('/') ? url : ('/' + url));
			} else {
				headers = url.headers;

				url.url = this.API_SERVER + (url.url.startsWith('/') ? url.url : ('/' + url.url));
			}
			
			// Set access token
			if (auth_data && 'token' in auth_data) {
				headers.set('Access-Token', auth_data.token || null)
			}

			return super.request(url, options).catch(this.catchAuthError(this));
		})
	}

	private catchAuthError (self: APIRequest) {
		// we have to pass HttpService's own instance here as `self`
		return (res: Response) => {

			if (res.status === 401) {
				// Do something
			}

			if (res.status === 403) {
				// Do something
			}
			
			return Observable.throw(res)
		}
	}
}

Registrering service provider

It's a little bit more complicated than always... All actions are done in app.module.ts

  • First we need to import necessary stuff:
import { IonicStorageModule, Storage } from '@ionic/storage';
import { XHRBackend, RequestOptions, HttpModule} from '@angular/http';
  • Then we will create a custom function:
export function APIRequestFactory(backend: XHRBackend, options: RequestOptions, storage: Storage) {
	return new APIRequest(backend, options, storage);
}
  • And finally we will add that function to providers in NgModule:
{
	provide: APIRequest,
	useFactory: APIRequestFactory,
	deps: [XHRBackend, RequestOptions, Storage]
}

Doing REST API call

import { Component } from '@angular/core';

import { IonicPage, LoadingController } from 'ionic-angular';
import { APIRequest } from '../../providers/api-request';

@IonicPage()

@Component({
	selector: 'page-messages',
	templateUrl: 'messages.html',
})

export class MessagesPage {

	constructor(
		private loadingCtrl: LoadingController,
		
		private APIRequest: APIRequest) {
	}

	ionViewDidLoad() {
		console.log('ionViewDidLoad UsersPage');

		let loader = this.loadingCtrl.create({
			showBackdrop: true,
			content: 'Loading User Messages'
		})

		loader.present()

		// This request will be executed with token we got from back-end server
		// So when it reaches the server we could identify the user by that token
		this.APIRequest.get('/messages').map(response => response.json()).finally(() => loader.dismiss()).subscribe(response => {
			console.log(response)
		}, err => {
			console.error(err)
		})
	}

}