Angular 5 caching http service api calls










10















In my Angular 5 app a certain dataset (not changing very often) is needed multiple times on different places in the app. After the API is called, the result is stored with the Observable do operator. This way I implemented caching of HTTP requests within my service.



I'm using Angular 5.1.3 and RxJS 5.5.6.



Is this a good practise?
Are there better alternatives?



import Injectable from '@angular/core';
import HttpClient from '@angular/common/http';
import Observable from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';

@Injectable()
export class FruitService

fruits: Array<string> = ;

constructor(private http: HttpClient)

getFruits()
if (this.fruits.length === 0)
return this.http.get<any>('api/getFruits')
.do(data => this.fruits = data )
else
return Observable.of(this.fruits);












share|improve this question



















  • 1





    Unrelated to your issue, but if you're using Angular 5, I'd recommend using pipeable operators for your observables instead of the rxjs/add/operator imports.

    – Joe Clay
    Apr 12 '18 at 13:36












  • Related question, stackoverflow.com/questions/40249629/…

    – estus
    Apr 12 '18 at 14:22






  • 2





    also keep in mind if you use pipe operator, do == tap ;)

    – Ringo
    Apr 12 '18 at 14:23







  • 1





    See the 'Why?' section of the page I linked for full info, but TL;DR: Pipeable operators are local to your file rather than applied globally to the entire app, they're more easily optimized out by build tools if they're not used, and they allow you to create custom operators much more easily.

    – Joe Clay
    Apr 12 '18 at 14:39






  • 1





    @JoeClay: You convinced me, thanks, I have learned something again

    – Herman Fransen
    Apr 13 '18 at 10:00















10















In my Angular 5 app a certain dataset (not changing very often) is needed multiple times on different places in the app. After the API is called, the result is stored with the Observable do operator. This way I implemented caching of HTTP requests within my service.



I'm using Angular 5.1.3 and RxJS 5.5.6.



Is this a good practise?
Are there better alternatives?



import Injectable from '@angular/core';
import HttpClient from '@angular/common/http';
import Observable from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';

@Injectable()
export class FruitService

fruits: Array<string> = ;

constructor(private http: HttpClient)

getFruits()
if (this.fruits.length === 0)
return this.http.get<any>('api/getFruits')
.do(data => this.fruits = data )
else
return Observable.of(this.fruits);












share|improve this question



















  • 1





    Unrelated to your issue, but if you're using Angular 5, I'd recommend using pipeable operators for your observables instead of the rxjs/add/operator imports.

    – Joe Clay
    Apr 12 '18 at 13:36












  • Related question, stackoverflow.com/questions/40249629/…

    – estus
    Apr 12 '18 at 14:22






  • 2





    also keep in mind if you use pipe operator, do == tap ;)

    – Ringo
    Apr 12 '18 at 14:23







  • 1





    See the 'Why?' section of the page I linked for full info, but TL;DR: Pipeable operators are local to your file rather than applied globally to the entire app, they're more easily optimized out by build tools if they're not used, and they allow you to create custom operators much more easily.

    – Joe Clay
    Apr 12 '18 at 14:39






  • 1





    @JoeClay: You convinced me, thanks, I have learned something again

    – Herman Fransen
    Apr 13 '18 at 10:00













10












10








10


4






In my Angular 5 app a certain dataset (not changing very often) is needed multiple times on different places in the app. After the API is called, the result is stored with the Observable do operator. This way I implemented caching of HTTP requests within my service.



I'm using Angular 5.1.3 and RxJS 5.5.6.



Is this a good practise?
Are there better alternatives?



import Injectable from '@angular/core';
import HttpClient from '@angular/common/http';
import Observable from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';

@Injectable()
export class FruitService

fruits: Array<string> = ;

constructor(private http: HttpClient)

getFruits()
if (this.fruits.length === 0)
return this.http.get<any>('api/getFruits')
.do(data => this.fruits = data )
else
return Observable.of(this.fruits);












share|improve this question
















In my Angular 5 app a certain dataset (not changing very often) is needed multiple times on different places in the app. After the API is called, the result is stored with the Observable do operator. This way I implemented caching of HTTP requests within my service.



I'm using Angular 5.1.3 and RxJS 5.5.6.



Is this a good practise?
Are there better alternatives?



import Injectable from '@angular/core';
import HttpClient from '@angular/common/http';
import Observable from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';

@Injectable()
export class FruitService

fruits: Array<string> = ;

constructor(private http: HttpClient)

getFruits()
if (this.fruits.length === 0)
return this.http.get<any>('api/getFruits')
.do(data => this.fruits = data )
else
return Observable.of(this.fruits);









angular angular-httpclient






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Apr 16 '18 at 12:54







Herman Fransen

















asked Apr 12 '18 at 13:31









Herman FransenHerman Fransen

1,20421331




1,20421331







  • 1





    Unrelated to your issue, but if you're using Angular 5, I'd recommend using pipeable operators for your observables instead of the rxjs/add/operator imports.

    – Joe Clay
    Apr 12 '18 at 13:36












  • Related question, stackoverflow.com/questions/40249629/…

    – estus
    Apr 12 '18 at 14:22






  • 2





    also keep in mind if you use pipe operator, do == tap ;)

    – Ringo
    Apr 12 '18 at 14:23







  • 1





    See the 'Why?' section of the page I linked for full info, but TL;DR: Pipeable operators are local to your file rather than applied globally to the entire app, they're more easily optimized out by build tools if they're not used, and they allow you to create custom operators much more easily.

    – Joe Clay
    Apr 12 '18 at 14:39






  • 1





    @JoeClay: You convinced me, thanks, I have learned something again

    – Herman Fransen
    Apr 13 '18 at 10:00












  • 1





    Unrelated to your issue, but if you're using Angular 5, I'd recommend using pipeable operators for your observables instead of the rxjs/add/operator imports.

    – Joe Clay
    Apr 12 '18 at 13:36












  • Related question, stackoverflow.com/questions/40249629/…

    – estus
    Apr 12 '18 at 14:22






  • 2





    also keep in mind if you use pipe operator, do == tap ;)

    – Ringo
    Apr 12 '18 at 14:23







  • 1





    See the 'Why?' section of the page I linked for full info, but TL;DR: Pipeable operators are local to your file rather than applied globally to the entire app, they're more easily optimized out by build tools if they're not used, and they allow you to create custom operators much more easily.

    – Joe Clay
    Apr 12 '18 at 14:39






  • 1





    @JoeClay: You convinced me, thanks, I have learned something again

    – Herman Fransen
    Apr 13 '18 at 10:00







1




1





Unrelated to your issue, but if you're using Angular 5, I'd recommend using pipeable operators for your observables instead of the rxjs/add/operator imports.

– Joe Clay
Apr 12 '18 at 13:36






Unrelated to your issue, but if you're using Angular 5, I'd recommend using pipeable operators for your observables instead of the rxjs/add/operator imports.

– Joe Clay
Apr 12 '18 at 13:36














Related question, stackoverflow.com/questions/40249629/…

– estus
Apr 12 '18 at 14:22





Related question, stackoverflow.com/questions/40249629/…

– estus
Apr 12 '18 at 14:22




2




2





also keep in mind if you use pipe operator, do == tap ;)

– Ringo
Apr 12 '18 at 14:23






also keep in mind if you use pipe operator, do == tap ;)

– Ringo
Apr 12 '18 at 14:23





1




1





See the 'Why?' section of the page I linked for full info, but TL;DR: Pipeable operators are local to your file rather than applied globally to the entire app, they're more easily optimized out by build tools if they're not used, and they allow you to create custom operators much more easily.

– Joe Clay
Apr 12 '18 at 14:39





See the 'Why?' section of the page I linked for full info, but TL;DR: Pipeable operators are local to your file rather than applied globally to the entire app, they're more easily optimized out by build tools if they're not used, and they allow you to create custom operators much more easily.

– Joe Clay
Apr 12 '18 at 14:39




1




1





@JoeClay: You convinced me, thanks, I have learned something again

– Herman Fransen
Apr 13 '18 at 10:00





@JoeClay: You convinced me, thanks, I have learned something again

– Herman Fransen
Apr 13 '18 at 10:00












4 Answers
4






active

oldest

votes


















13














The problem with your solution is that if a 2nd call comes while a 1st one is pending, it create a new http request. Here is how I would do it:



@Injectable()
export class FruitService

readonly fruits = this.http.get<any>('api/getFruits').shareReplay(1);

constructor(private http: HttpClient)



the bigger problem is when you have params and you want to cache based on the params. In that case you would need some sort of memoize function like the one from lodash (https://lodash.com/docs/4.17.5#memoize)



You can also implement some in-memory cache operator for the Observable, like:



const cache = ;

function cacheOperator<T>(this: Observable<T>, key: string)
return new Observable<T>(observer =>
const cached = cache[key];
if (cached)
cached.subscribe(observer);
else
const add = this.multicast(new ReplaySubject(1));
cache[key] = add;
add.connect();
add.catch(err =>
delete cache[key];
throw err;
).subscribe(observer);

);


declare module 'rxjs/Observable'
interface Observable<T>
cache: typeof cacheOperator;



Observable.prototype.cache = cacheOperator;


and use it like:



getFruit(id: number) 
return this.http.get<any>(`api/fruit/$id`).cache(`fruit:$id`);






share|improve this answer


















  • 2





    cache operator is good POC but it isn't practical. Uncontrollable growth of private cache storage isn't a good thing IRL. There may be other concerns such as maximum capacity, expiration and force refreshing. This should be handled by cache service.

    – estus
    Apr 12 '18 at 14:21






  • 1





    Are you really sure this is working? I didn't get it to work for me.

    – Herman Fransen
    Apr 16 '18 at 12:30






  • 1





    @HermanFransen works fine for me: codepen.io/anon/pen/jzgLaW

    – Andrei Tătar
    Apr 16 '18 at 12:41











  • @Andrew : not the bigger problem, but the first one. It's not working in my case. I'm using Angular 5.1.3 and RxJS 5.5.6

    – Herman Fransen
    Apr 16 '18 at 12:50







  • 1





    @HermanFransen you mean shareReplay is not working? plnkr.co/edit/pwptj7lKR2FfRw5eVViJ?p=preview

    – Andrei Tătar
    Apr 16 '18 at 13:18


















2














Actually, the easiest way of caching responses and also sharing a single subscription (not making a new request for every subscriber) is using publishReplay(1) and refCount() (I'm using pipable operators).



readonly fruits$ = this.http.get<any>('api/getFruits')
.pipe(
publishReplay(1), // publishReplay(1, _time_)
refCount(),
take(1),
);


Then when you want to get the cached/fresh value you'll just subscribe to fresh$.



fresh$.subscribe(...)


The publishReplay operator caches the value, then refCount maintains only one subscription to its parent and unsubscribes if there are no subscribers. The take(1) is necessary to properly complete the chain after a single value.



The most important part is that when you subscribe to this chain publishReplay emits its buffer on subscription and if it contains a cached value it'll be immediately propagated to take(1) that completes the chain so it won't create subscription to this.http.get at all. If publishReplay doesn't contain anything it'll subscribe to its source and make the HTTP request.






share|improve this answer






























    2














    There is another way doing this with shareReplay and Angular 5, 6 or 7 : create a Service :



    import Observable from 'rxjs/Observable';
    import shareReplay from 'rxjs/operators';
    const CACHE_SIZE = 1;

    private cache$: Observable<Object>;

    get api()
    if ( !this.cache$ )
    this.cache$ = this.requestApi().pipe( shareReplay(CACHE_SIZE) );

    return this.cache_arbitrage$;


    private requestApi()
    const API_ENDPOINT = 'yoururl/';
    return this.http.get<any>(API_ENDPOINT_ARBITRATION).pipe();


    public resetCache()
    this.cache$ = null;



    To read the data directly in your html file use this :



    <div *ngIf="this.apiService.api | async as api">api </div>


    In your component you can subscribe like this:



    this.apiService.api.pipe().subscribe(res => /*your code*/)





    share|improve this answer






























      1














      For Angular 6, RxJS 6 and simple cache expiration use the following code:



      interface CacheEntry<T> 
      expiry: number;
      observable: Observable<T>;


      const DEFAULT_MAX_AGE = 300000;
      const globalCache: [key: string]: CacheEntry<any>; = ;

      export function cache(key: string, maxAge: number = DEFAULT_MAX_AGE)
      return function cacheOperatorImpl<T>(source: Observable<T>)
      return Observable.create(observer =>
      const cached = globalCache[key];
      if (cached && cached.expiry >= Date.now())
      cached.observable.subscribe(observer);
      else
      const add = source.pipe(multicast(new ReplaySubject(1))) as ConnectableObservable<T>;
      globalCache[key] = observable: add, expiry: Date.now() + maxAge;
      add.connect();
      add.pipe(
      catchError(err =>
      delete globalCache[key];
      return throwError(err);
      )
      ).subscribe(observer);

      );
      ;






      share|improve this answer






















        Your Answer






        StackExchange.ifUsing("editor", function ()
        StackExchange.using("externalEditor", function ()
        StackExchange.using("snippets", function ()
        StackExchange.snippets.init();
        );
        );
        , "code-snippets");

        StackExchange.ready(function()
        var channelOptions =
        tags: "".split(" "),
        id: "1"
        ;
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function()
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled)
        StackExchange.using("snippets", function()
        createEditor();
        );

        else
        createEditor();

        );

        function createEditor()
        StackExchange.prepareEditor(
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: true,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        imageUploader:
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        ,
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        );



        );













        draft saved

        draft discarded


















        StackExchange.ready(
        function ()
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f49797910%2fangular-5-caching-http-service-api-calls%23new-answer', 'question_page');

        );

        Post as a guest















        Required, but never shown

























        4 Answers
        4






        active

        oldest

        votes








        4 Answers
        4






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        13














        The problem with your solution is that if a 2nd call comes while a 1st one is pending, it create a new http request. Here is how I would do it:



        @Injectable()
        export class FruitService

        readonly fruits = this.http.get<any>('api/getFruits').shareReplay(1);

        constructor(private http: HttpClient)



        the bigger problem is when you have params and you want to cache based on the params. In that case you would need some sort of memoize function like the one from lodash (https://lodash.com/docs/4.17.5#memoize)



        You can also implement some in-memory cache operator for the Observable, like:



        const cache = ;

        function cacheOperator<T>(this: Observable<T>, key: string)
        return new Observable<T>(observer =>
        const cached = cache[key];
        if (cached)
        cached.subscribe(observer);
        else
        const add = this.multicast(new ReplaySubject(1));
        cache[key] = add;
        add.connect();
        add.catch(err =>
        delete cache[key];
        throw err;
        ).subscribe(observer);

        );


        declare module 'rxjs/Observable'
        interface Observable<T>
        cache: typeof cacheOperator;



        Observable.prototype.cache = cacheOperator;


        and use it like:



        getFruit(id: number) 
        return this.http.get<any>(`api/fruit/$id`).cache(`fruit:$id`);






        share|improve this answer


















        • 2





          cache operator is good POC but it isn't practical. Uncontrollable growth of private cache storage isn't a good thing IRL. There may be other concerns such as maximum capacity, expiration and force refreshing. This should be handled by cache service.

          – estus
          Apr 12 '18 at 14:21






        • 1





          Are you really sure this is working? I didn't get it to work for me.

          – Herman Fransen
          Apr 16 '18 at 12:30






        • 1





          @HermanFransen works fine for me: codepen.io/anon/pen/jzgLaW

          – Andrei Tătar
          Apr 16 '18 at 12:41











        • @Andrew : not the bigger problem, but the first one. It's not working in my case. I'm using Angular 5.1.3 and RxJS 5.5.6

          – Herman Fransen
          Apr 16 '18 at 12:50







        • 1





          @HermanFransen you mean shareReplay is not working? plnkr.co/edit/pwptj7lKR2FfRw5eVViJ?p=preview

          – Andrei Tătar
          Apr 16 '18 at 13:18















        13














        The problem with your solution is that if a 2nd call comes while a 1st one is pending, it create a new http request. Here is how I would do it:



        @Injectable()
        export class FruitService

        readonly fruits = this.http.get<any>('api/getFruits').shareReplay(1);

        constructor(private http: HttpClient)



        the bigger problem is when you have params and you want to cache based on the params. In that case you would need some sort of memoize function like the one from lodash (https://lodash.com/docs/4.17.5#memoize)



        You can also implement some in-memory cache operator for the Observable, like:



        const cache = ;

        function cacheOperator<T>(this: Observable<T>, key: string)
        return new Observable<T>(observer =>
        const cached = cache[key];
        if (cached)
        cached.subscribe(observer);
        else
        const add = this.multicast(new ReplaySubject(1));
        cache[key] = add;
        add.connect();
        add.catch(err =>
        delete cache[key];
        throw err;
        ).subscribe(observer);

        );


        declare module 'rxjs/Observable'
        interface Observable<T>
        cache: typeof cacheOperator;



        Observable.prototype.cache = cacheOperator;


        and use it like:



        getFruit(id: number) 
        return this.http.get<any>(`api/fruit/$id`).cache(`fruit:$id`);






        share|improve this answer


















        • 2





          cache operator is good POC but it isn't practical. Uncontrollable growth of private cache storage isn't a good thing IRL. There may be other concerns such as maximum capacity, expiration and force refreshing. This should be handled by cache service.

          – estus
          Apr 12 '18 at 14:21






        • 1





          Are you really sure this is working? I didn't get it to work for me.

          – Herman Fransen
          Apr 16 '18 at 12:30






        • 1





          @HermanFransen works fine for me: codepen.io/anon/pen/jzgLaW

          – Andrei Tătar
          Apr 16 '18 at 12:41











        • @Andrew : not the bigger problem, but the first one. It's not working in my case. I'm using Angular 5.1.3 and RxJS 5.5.6

          – Herman Fransen
          Apr 16 '18 at 12:50







        • 1





          @HermanFransen you mean shareReplay is not working? plnkr.co/edit/pwptj7lKR2FfRw5eVViJ?p=preview

          – Andrei Tătar
          Apr 16 '18 at 13:18













        13












        13








        13







        The problem with your solution is that if a 2nd call comes while a 1st one is pending, it create a new http request. Here is how I would do it:



        @Injectable()
        export class FruitService

        readonly fruits = this.http.get<any>('api/getFruits').shareReplay(1);

        constructor(private http: HttpClient)



        the bigger problem is when you have params and you want to cache based on the params. In that case you would need some sort of memoize function like the one from lodash (https://lodash.com/docs/4.17.5#memoize)



        You can also implement some in-memory cache operator for the Observable, like:



        const cache = ;

        function cacheOperator<T>(this: Observable<T>, key: string)
        return new Observable<T>(observer =>
        const cached = cache[key];
        if (cached)
        cached.subscribe(observer);
        else
        const add = this.multicast(new ReplaySubject(1));
        cache[key] = add;
        add.connect();
        add.catch(err =>
        delete cache[key];
        throw err;
        ).subscribe(observer);

        );


        declare module 'rxjs/Observable'
        interface Observable<T>
        cache: typeof cacheOperator;



        Observable.prototype.cache = cacheOperator;


        and use it like:



        getFruit(id: number) 
        return this.http.get<any>(`api/fruit/$id`).cache(`fruit:$id`);






        share|improve this answer













        The problem with your solution is that if a 2nd call comes while a 1st one is pending, it create a new http request. Here is how I would do it:



        @Injectable()
        export class FruitService

        readonly fruits = this.http.get<any>('api/getFruits').shareReplay(1);

        constructor(private http: HttpClient)



        the bigger problem is when you have params and you want to cache based on the params. In that case you would need some sort of memoize function like the one from lodash (https://lodash.com/docs/4.17.5#memoize)



        You can also implement some in-memory cache operator for the Observable, like:



        const cache = ;

        function cacheOperator<T>(this: Observable<T>, key: string)
        return new Observable<T>(observer =>
        const cached = cache[key];
        if (cached)
        cached.subscribe(observer);
        else
        const add = this.multicast(new ReplaySubject(1));
        cache[key] = add;
        add.connect();
        add.catch(err =>
        delete cache[key];
        throw err;
        ).subscribe(observer);

        );


        declare module 'rxjs/Observable'
        interface Observable<T>
        cache: typeof cacheOperator;



        Observable.prototype.cache = cacheOperator;


        and use it like:



        getFruit(id: number) 
        return this.http.get<any>(`api/fruit/$id`).cache(`fruit:$id`);







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Apr 12 '18 at 13:34









        Andrei TătarAndrei Tătar

        4,097722




        4,097722







        • 2





          cache operator is good POC but it isn't practical. Uncontrollable growth of private cache storage isn't a good thing IRL. There may be other concerns such as maximum capacity, expiration and force refreshing. This should be handled by cache service.

          – estus
          Apr 12 '18 at 14:21






        • 1





          Are you really sure this is working? I didn't get it to work for me.

          – Herman Fransen
          Apr 16 '18 at 12:30






        • 1





          @HermanFransen works fine for me: codepen.io/anon/pen/jzgLaW

          – Andrei Tătar
          Apr 16 '18 at 12:41











        • @Andrew : not the bigger problem, but the first one. It's not working in my case. I'm using Angular 5.1.3 and RxJS 5.5.6

          – Herman Fransen
          Apr 16 '18 at 12:50







        • 1





          @HermanFransen you mean shareReplay is not working? plnkr.co/edit/pwptj7lKR2FfRw5eVViJ?p=preview

          – Andrei Tătar
          Apr 16 '18 at 13:18












        • 2





          cache operator is good POC but it isn't practical. Uncontrollable growth of private cache storage isn't a good thing IRL. There may be other concerns such as maximum capacity, expiration and force refreshing. This should be handled by cache service.

          – estus
          Apr 12 '18 at 14:21






        • 1





          Are you really sure this is working? I didn't get it to work for me.

          – Herman Fransen
          Apr 16 '18 at 12:30






        • 1





          @HermanFransen works fine for me: codepen.io/anon/pen/jzgLaW

          – Andrei Tătar
          Apr 16 '18 at 12:41











        • @Andrew : not the bigger problem, but the first one. It's not working in my case. I'm using Angular 5.1.3 and RxJS 5.5.6

          – Herman Fransen
          Apr 16 '18 at 12:50







        • 1





          @HermanFransen you mean shareReplay is not working? plnkr.co/edit/pwptj7lKR2FfRw5eVViJ?p=preview

          – Andrei Tătar
          Apr 16 '18 at 13:18







        2




        2





        cache operator is good POC but it isn't practical. Uncontrollable growth of private cache storage isn't a good thing IRL. There may be other concerns such as maximum capacity, expiration and force refreshing. This should be handled by cache service.

        – estus
        Apr 12 '18 at 14:21





        cache operator is good POC but it isn't practical. Uncontrollable growth of private cache storage isn't a good thing IRL. There may be other concerns such as maximum capacity, expiration and force refreshing. This should be handled by cache service.

        – estus
        Apr 12 '18 at 14:21




        1




        1





        Are you really sure this is working? I didn't get it to work for me.

        – Herman Fransen
        Apr 16 '18 at 12:30





        Are you really sure this is working? I didn't get it to work for me.

        – Herman Fransen
        Apr 16 '18 at 12:30




        1




        1





        @HermanFransen works fine for me: codepen.io/anon/pen/jzgLaW

        – Andrei Tătar
        Apr 16 '18 at 12:41





        @HermanFransen works fine for me: codepen.io/anon/pen/jzgLaW

        – Andrei Tătar
        Apr 16 '18 at 12:41













        @Andrew : not the bigger problem, but the first one. It's not working in my case. I'm using Angular 5.1.3 and RxJS 5.5.6

        – Herman Fransen
        Apr 16 '18 at 12:50






        @Andrew : not the bigger problem, but the first one. It's not working in my case. I'm using Angular 5.1.3 and RxJS 5.5.6

        – Herman Fransen
        Apr 16 '18 at 12:50





        1




        1





        @HermanFransen you mean shareReplay is not working? plnkr.co/edit/pwptj7lKR2FfRw5eVViJ?p=preview

        – Andrei Tătar
        Apr 16 '18 at 13:18





        @HermanFransen you mean shareReplay is not working? plnkr.co/edit/pwptj7lKR2FfRw5eVViJ?p=preview

        – Andrei Tătar
        Apr 16 '18 at 13:18













        2














        Actually, the easiest way of caching responses and also sharing a single subscription (not making a new request for every subscriber) is using publishReplay(1) and refCount() (I'm using pipable operators).



        readonly fruits$ = this.http.get<any>('api/getFruits')
        .pipe(
        publishReplay(1), // publishReplay(1, _time_)
        refCount(),
        take(1),
        );


        Then when you want to get the cached/fresh value you'll just subscribe to fresh$.



        fresh$.subscribe(...)


        The publishReplay operator caches the value, then refCount maintains only one subscription to its parent and unsubscribes if there are no subscribers. The take(1) is necessary to properly complete the chain after a single value.



        The most important part is that when you subscribe to this chain publishReplay emits its buffer on subscription and if it contains a cached value it'll be immediately propagated to take(1) that completes the chain so it won't create subscription to this.http.get at all. If publishReplay doesn't contain anything it'll subscribe to its source and make the HTTP request.






        share|improve this answer



























          2














          Actually, the easiest way of caching responses and also sharing a single subscription (not making a new request for every subscriber) is using publishReplay(1) and refCount() (I'm using pipable operators).



          readonly fruits$ = this.http.get<any>('api/getFruits')
          .pipe(
          publishReplay(1), // publishReplay(1, _time_)
          refCount(),
          take(1),
          );


          Then when you want to get the cached/fresh value you'll just subscribe to fresh$.



          fresh$.subscribe(...)


          The publishReplay operator caches the value, then refCount maintains only one subscription to its parent and unsubscribes if there are no subscribers. The take(1) is necessary to properly complete the chain after a single value.



          The most important part is that when you subscribe to this chain publishReplay emits its buffer on subscription and if it contains a cached value it'll be immediately propagated to take(1) that completes the chain so it won't create subscription to this.http.get at all. If publishReplay doesn't contain anything it'll subscribe to its source and make the HTTP request.






          share|improve this answer

























            2












            2








            2







            Actually, the easiest way of caching responses and also sharing a single subscription (not making a new request for every subscriber) is using publishReplay(1) and refCount() (I'm using pipable operators).



            readonly fruits$ = this.http.get<any>('api/getFruits')
            .pipe(
            publishReplay(1), // publishReplay(1, _time_)
            refCount(),
            take(1),
            );


            Then when you want to get the cached/fresh value you'll just subscribe to fresh$.



            fresh$.subscribe(...)


            The publishReplay operator caches the value, then refCount maintains only one subscription to its parent and unsubscribes if there are no subscribers. The take(1) is necessary to properly complete the chain after a single value.



            The most important part is that when you subscribe to this chain publishReplay emits its buffer on subscription and if it contains a cached value it'll be immediately propagated to take(1) that completes the chain so it won't create subscription to this.http.get at all. If publishReplay doesn't contain anything it'll subscribe to its source and make the HTTP request.






            share|improve this answer













            Actually, the easiest way of caching responses and also sharing a single subscription (not making a new request for every subscriber) is using publishReplay(1) and refCount() (I'm using pipable operators).



            readonly fruits$ = this.http.get<any>('api/getFruits')
            .pipe(
            publishReplay(1), // publishReplay(1, _time_)
            refCount(),
            take(1),
            );


            Then when you want to get the cached/fresh value you'll just subscribe to fresh$.



            fresh$.subscribe(...)


            The publishReplay operator caches the value, then refCount maintains only one subscription to its parent and unsubscribes if there are no subscribers. The take(1) is necessary to properly complete the chain after a single value.



            The most important part is that when you subscribe to this chain publishReplay emits its buffer on subscription and if it contains a cached value it'll be immediately propagated to take(1) that completes the chain so it won't create subscription to this.http.get at all. If publishReplay doesn't contain anything it'll subscribe to its source and make the HTTP request.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Apr 12 '18 at 14:48









            martinmartin

            46k1193137




            46k1193137





















                2














                There is another way doing this with shareReplay and Angular 5, 6 or 7 : create a Service :



                import Observable from 'rxjs/Observable';
                import shareReplay from 'rxjs/operators';
                const CACHE_SIZE = 1;

                private cache$: Observable<Object>;

                get api()
                if ( !this.cache$ )
                this.cache$ = this.requestApi().pipe( shareReplay(CACHE_SIZE) );

                return this.cache_arbitrage$;


                private requestApi()
                const API_ENDPOINT = 'yoururl/';
                return this.http.get<any>(API_ENDPOINT_ARBITRATION).pipe();


                public resetCache()
                this.cache$ = null;



                To read the data directly in your html file use this :



                <div *ngIf="this.apiService.api | async as api">api </div>


                In your component you can subscribe like this:



                this.apiService.api.pipe().subscribe(res => /*your code*/)





                share|improve this answer



























                  2














                  There is another way doing this with shareReplay and Angular 5, 6 or 7 : create a Service :



                  import Observable from 'rxjs/Observable';
                  import shareReplay from 'rxjs/operators';
                  const CACHE_SIZE = 1;

                  private cache$: Observable<Object>;

                  get api()
                  if ( !this.cache$ )
                  this.cache$ = this.requestApi().pipe( shareReplay(CACHE_SIZE) );

                  return this.cache_arbitrage$;


                  private requestApi()
                  const API_ENDPOINT = 'yoururl/';
                  return this.http.get<any>(API_ENDPOINT_ARBITRATION).pipe();


                  public resetCache()
                  this.cache$ = null;



                  To read the data directly in your html file use this :



                  <div *ngIf="this.apiService.api | async as api">api </div>


                  In your component you can subscribe like this:



                  this.apiService.api.pipe().subscribe(res => /*your code*/)





                  share|improve this answer

























                    2












                    2








                    2







                    There is another way doing this with shareReplay and Angular 5, 6 or 7 : create a Service :



                    import Observable from 'rxjs/Observable';
                    import shareReplay from 'rxjs/operators';
                    const CACHE_SIZE = 1;

                    private cache$: Observable<Object>;

                    get api()
                    if ( !this.cache$ )
                    this.cache$ = this.requestApi().pipe( shareReplay(CACHE_SIZE) );

                    return this.cache_arbitrage$;


                    private requestApi()
                    const API_ENDPOINT = 'yoururl/';
                    return this.http.get<any>(API_ENDPOINT_ARBITRATION).pipe();


                    public resetCache()
                    this.cache$ = null;



                    To read the data directly in your html file use this :



                    <div *ngIf="this.apiService.api | async as api">api </div>


                    In your component you can subscribe like this:



                    this.apiService.api.pipe().subscribe(res => /*your code*/)





                    share|improve this answer













                    There is another way doing this with shareReplay and Angular 5, 6 or 7 : create a Service :



                    import Observable from 'rxjs/Observable';
                    import shareReplay from 'rxjs/operators';
                    const CACHE_SIZE = 1;

                    private cache$: Observable<Object>;

                    get api()
                    if ( !this.cache$ )
                    this.cache$ = this.requestApi().pipe( shareReplay(CACHE_SIZE) );

                    return this.cache_arbitrage$;


                    private requestApi()
                    const API_ENDPOINT = 'yoururl/';
                    return this.http.get<any>(API_ENDPOINT_ARBITRATION).pipe();


                    public resetCache()
                    this.cache$ = null;



                    To read the data directly in your html file use this :



                    <div *ngIf="this.apiService.api | async as api">api </div>


                    In your component you can subscribe like this:



                    this.apiService.api.pipe().subscribe(res => /*your code*/)






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Dec 19 '18 at 15:59









                    D3FD3F

                    212




                    212





















                        1














                        For Angular 6, RxJS 6 and simple cache expiration use the following code:



                        interface CacheEntry<T> 
                        expiry: number;
                        observable: Observable<T>;


                        const DEFAULT_MAX_AGE = 300000;
                        const globalCache: [key: string]: CacheEntry<any>; = ;

                        export function cache(key: string, maxAge: number = DEFAULT_MAX_AGE)
                        return function cacheOperatorImpl<T>(source: Observable<T>)
                        return Observable.create(observer =>
                        const cached = globalCache[key];
                        if (cached && cached.expiry >= Date.now())
                        cached.observable.subscribe(observer);
                        else
                        const add = source.pipe(multicast(new ReplaySubject(1))) as ConnectableObservable<T>;
                        globalCache[key] = observable: add, expiry: Date.now() + maxAge;
                        add.connect();
                        add.pipe(
                        catchError(err =>
                        delete globalCache[key];
                        return throwError(err);
                        )
                        ).subscribe(observer);

                        );
                        ;






                        share|improve this answer



























                          1














                          For Angular 6, RxJS 6 and simple cache expiration use the following code:



                          interface CacheEntry<T> 
                          expiry: number;
                          observable: Observable<T>;


                          const DEFAULT_MAX_AGE = 300000;
                          const globalCache: [key: string]: CacheEntry<any>; = ;

                          export function cache(key: string, maxAge: number = DEFAULT_MAX_AGE)
                          return function cacheOperatorImpl<T>(source: Observable<T>)
                          return Observable.create(observer =>
                          const cached = globalCache[key];
                          if (cached && cached.expiry >= Date.now())
                          cached.observable.subscribe(observer);
                          else
                          const add = source.pipe(multicast(new ReplaySubject(1))) as ConnectableObservable<T>;
                          globalCache[key] = observable: add, expiry: Date.now() + maxAge;
                          add.connect();
                          add.pipe(
                          catchError(err =>
                          delete globalCache[key];
                          return throwError(err);
                          )
                          ).subscribe(observer);

                          );
                          ;






                          share|improve this answer

























                            1












                            1








                            1







                            For Angular 6, RxJS 6 and simple cache expiration use the following code:



                            interface CacheEntry<T> 
                            expiry: number;
                            observable: Observable<T>;


                            const DEFAULT_MAX_AGE = 300000;
                            const globalCache: [key: string]: CacheEntry<any>; = ;

                            export function cache(key: string, maxAge: number = DEFAULT_MAX_AGE)
                            return function cacheOperatorImpl<T>(source: Observable<T>)
                            return Observable.create(observer =>
                            const cached = globalCache[key];
                            if (cached && cached.expiry >= Date.now())
                            cached.observable.subscribe(observer);
                            else
                            const add = source.pipe(multicast(new ReplaySubject(1))) as ConnectableObservable<T>;
                            globalCache[key] = observable: add, expiry: Date.now() + maxAge;
                            add.connect();
                            add.pipe(
                            catchError(err =>
                            delete globalCache[key];
                            return throwError(err);
                            )
                            ).subscribe(observer);

                            );
                            ;






                            share|improve this answer













                            For Angular 6, RxJS 6 and simple cache expiration use the following code:



                            interface CacheEntry<T> 
                            expiry: number;
                            observable: Observable<T>;


                            const DEFAULT_MAX_AGE = 300000;
                            const globalCache: [key: string]: CacheEntry<any>; = ;

                            export function cache(key: string, maxAge: number = DEFAULT_MAX_AGE)
                            return function cacheOperatorImpl<T>(source: Observable<T>)
                            return Observable.create(observer =>
                            const cached = globalCache[key];
                            if (cached && cached.expiry >= Date.now())
                            cached.observable.subscribe(observer);
                            else
                            const add = source.pipe(multicast(new ReplaySubject(1))) as ConnectableObservable<T>;
                            globalCache[key] = observable: add, expiry: Date.now() + maxAge;
                            add.connect();
                            add.pipe(
                            catchError(err =>
                            delete globalCache[key];
                            return throwError(err);
                            )
                            ).subscribe(observer);

                            );
                            ;







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Nov 14 '18 at 20:27









                            mohlendomohlendo

                            355919




                            355919



























                                draft saved

                                draft discarded
















































                                Thanks for contributing an answer to Stack Overflow!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid


                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.

                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function ()
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f49797910%2fangular-5-caching-http-service-api-calls%23new-answer', 'question_page');

                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown







                                Popular posts from this blog

                                Use pre created SQLite database for Android project in kotlin

                                Darth Vader #20

                                Ondo