How to use promise/async in function returning Observable?

Viewed 472

I am using nestjs and would like to write a function returning Observable (rxjs) with cache.

import { HttpService } from '@nestjs/axios';
import { CACHE_MANAGER, Inject, Injectable } from '@nestjs/common';
import { Cache } from 'cache-manager';
import { map, of, Observable } from 'rxjs';

interface User {
  id: string;
  // ...
}

@Injectable()
export class Service {
  constructor(
    @Inject(CACHE_MANAGER) protected cache: Cache,
    protected readonly httpService: HttpService,
  ) {}
  fetchUser = (id: string): Observable<User> {
    const url = 'xxx';
    const user: string = this.cache.get(`user:${id}`); // but here is `Promise<string>` actually
    if (user) {
      return of(JSON.parse(user) as User);
    }

    return this.httpService.get<User>(url).pipe(
      map(({ data }) => {
        this.cache.set(`user:${id}`, JSON.stringify(data));
        return data;
      })
    );
  }
}

The logic is quite simple, if there is cache, return cache, otherwise call api, save to cache, and return result. The only issue is that cache would return a promise. How to make this work?

1 Answers

You could use RxJS from function to convert the Promise to an Observable. From there, you could use switchMap operator + of function to either return the user fetched from the cache or make a HTTP call.

fetchUser(id: string): Observable<User> {
  const url = 'xxx';

  const user$ = this.httpService.get<User>(url).pipe(
    map(({ data }) => {
      this.cache.set(`user:${id}`, JSON.stringify(data));
      return data;
    })
  );

  return from(this.cache.get(`user:${id}`)).pipe(
    switchMap((user: string) => 
      !!user ? of(user) : user$
    )
  );
}
Related