Skip to content

Instantly share code, notes, and snippets.

@guidobit
Created May 11, 2020 16:29
Show Gist options
  • Save guidobit/693151771f526f9100d24013f6481718 to your computer and use it in GitHub Desktop.
Save guidobit/693151771f526f9100d24013f6481718 to your computer and use it in GitHub Desktop.
Axios example
import {HttpService, Injectable} from '@nestjs/common';
import {BehaviorSubject, concat, EMPTY, Observable, Subject, throwError} from "rxjs";
import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import {ImportedLocation} from "./interfaces/imported-location.interface";
import {
catchError,
concatMap,
debounceTime,
delay,
filter,
map,
mapTo,
reduce,
retry,
retryWhen,
take,
tap
} from "rxjs/operators";
import {logger} from "codelyzer/util/logger";
import {ImportedCharacter} from "./interfaces/imported-character.interface";
import {ImportedEpisode} from "./interfaces/imported-episode.interface";
@Injectable()
export class RickMortyRemoteApiService {
private static baseApiConfig: AxiosRequestConfig = {
baseURL: 'https://rickandmortyapi.com/api',
paramsSerializer: RickMortyRemoteApiService.serializeParams
};
constructor(private readonly httpClient: HttpService) {}
private fetch<T>(path: string, params?: Array<{[key: string]: string}>): Observable<AxiosResponse<T>> {
return this.httpClient.get(path, RickMortyRemoteApiService.baseApiConfig).pipe(
retryWhen(errors => errors.pipe(
delay(5000),
take(3),
))
);
}
private paginate<T>(observable, page: number = 1): Observable<T> {
const pager = new BehaviorSubject(page);
const subject: Subject<T> = new Subject();
let data: Array<T> = [];
const fetchPage = (pageNumber) => observable(pageNumber)
.pipe(
filter((response: AxiosResponse<T>) => !!response.data && !!response.data['results']),
tap((response: AxiosResponse<T>) => {
if (response.data['info']['next'] && response.data['info']['next'].length) {
pager.next(pageNumber + 1);
} else {
pager.complete();
}
}),
map((response: AxiosResponse<T>) => response.data['results'])
);
pager.pipe(debounceTime(5000)).subscribe((pageNumber) => {
fetchPage(pageNumber).subscribe(next => data = data.concat(next));
}, (err) => {
subject.error(err);
}, () => {
// @ts-ignore
subject.next(data);
subject.complete();
});
return subject;
}
private fetchLocations<T = Array<ImportedLocation>>(page: number) {
return this.fetch<T>(`/location?page=${page}`);
}
public listLocations<T = Array<ImportedLocation>>(page: number = 1, autoPaginate = false): Observable<T> {
if (autoPaginate) {
return this.paginate<T>(this.fetchLocations.bind(this))
}
return this.fetchLocations<T>(page)
.pipe(
filter((response: AxiosResponse<T>) => !!response.data && !!response.data['results']),
map((response: AxiosResponse<T>) => response.data['results'])
);
}
public getLocation<T = ImportedLocation>(id: number): Observable<T> {
return this.fetch<T>(`/location/${id}`)
.pipe(
filter((response: AxiosResponse<T>) => !!response.data),
map((response: AxiosResponse<T>) => response.data)
);
}
private fetchCharacters<T = Array<ImportedCharacter>>(page: number) {
return this.fetch<T>(`/character?page=${page}`);
}
public listCharacters<T = Array<ImportedCharacter>>(page: number = 1, autoPaginate = false): Observable<T> {
if (autoPaginate) {
return this.paginate<T>(this.fetchCharacters.bind(this))
}
return this.fetchCharacters<T>(page)
.pipe(
filter((response: AxiosResponse<T>) => !!response.data && !!response.data['results']),
map((response: AxiosResponse<T>) => response.data['results'])
);
}
public getCharacter<T = ImportedCharacter>(id: number): Observable<T> {
return this.fetch<T>(`/character/${id}`)
.pipe(
filter((response: AxiosResponse<T>) => !!response.data),
map((response: AxiosResponse<T>) => response.data)
);
}
private fetchEpisodes<T = Array<ImportedEpisode>>(page: number) {
return this.fetch<T>(`/episode?page=${page}`);
}
public listEpisodes<T = Array<ImportedEpisode>>(page: number = 1, autoPaginate = false): Observable<T> {
if (autoPaginate) {
return this.paginate<T>(this.fetchEpisodes.bind(this))
}
return this.fetchEpisodes<T>(page)
.pipe(
filter((response: AxiosResponse<T>) => !!response.data && !!response.data['results']),
map((response: AxiosResponse<T>) => response.data['results'])
);
}
public getEpisode<T = ImportedEpisode>(id: number): Observable<T> {
return this.fetch<T>(`/episode/${id}`)
.pipe(
filter((response: AxiosResponse<T>) => !!response.data),
map((response: AxiosResponse<T>) => response.data)
);
}
private static serializeParams(params: Array<{[key: string]: string}>): string {
return params
.filter(param => !!param.key && !!param.value)
.map(param => `${param.key}=${param.value}`)
.join();
}
private handleError(error) {
logger.error(error);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment