Home > Net >  Angular Http Interceptor with caching returns inmutable response
Angular Http Interceptor with caching returns inmutable response

Time:11-09

I'm using angular Http Interceptor to catch request and, in case that these request were already made, the interceptor just returns the HttpResponse cached on a service.

The interceptors fires with this piece of code that makes request to the API:

getSeriesWithGenres() {
    const series$ = this.getSeries()
    const genres$ = this.getGenres()

    return forkJoin([series$, genres$]).pipe(
      map(([series, genres]) => {
        return series.map((serie: Serie) => {
          serie.genre_ids = serie.genre_ids.map(genreId => {
            return genres.find((genre: Genre) => genre.id === genreId)?.name
          })

          return serie
        })
      })
    )
}

And the methods getSeries() and getGenres() made the Http Request as follows:

getSeries() {
    return this.http
      .get<ApiResponse>(
        `
        https://api.themoviedb.org/3/tv/popular?api_key=${environment.apiKey}&language=en-US
        `
      )
      .pipe(map((data: ApiResponse) => data.results as Serie[]))
  }

getGenres() {
    return this.http
      .get(
        `
        https://api.themoviedb.org/3/genre/tv/list?api_key=${environment.apiKey}&language=en-US
        `
      )
      .pipe(map((data: any) => data.genres))
}

So, my problem is when I cached the response, because if method called getSeriesWithGenres() fires twice, Iḿ getting the following error:

TypeError: "genre_ids" is read-only

Note: To cached the response I use the following data structure on another service:

private cache: Map<string, [Date | null, HttpResponse<unknown>]> = new Map()

With the corresponding getters and setters methods

And my interceptor, use this service as follows:

const HALF_HOUR = 1800
const TIME_TO_LIVE = HALF_HOUR

@Injectable()
export class CachingInterceptor implements HttpInterceptor {
  constructor(private cacheResolver: CacheResolverService) {}
  intercept(
    request: HttpRequest<unknown>,
    next: HttpHandler
  ): Observable<HttpEvent<unknown>> {
    if (request.method !== 'GET') {
      return next.handle(request)
    }
    console.log('CachingInterceptor: '   request.url)
    const cachedResponse = this.cacheResolver.get(request.url)
    console.log('CachedResponse:'   JSON.stringify(cachedResponse?.body))
    return cachedResponse ? of(cachedResponse) : this.sendRequest(request, next)
  }

  sendRequest(
    request: HttpRequest<unknown>,
    next: HttpHandler
  ): Observable<HttpEvent<unknown>> {
    return next.handle(request).pipe(
      map((event: HttpEvent<unknown>) => {
        if (event instanceof HttpResponse) {
          this.cacheResolver.set(request.url, event, TIME_TO_LIVE)
        }
        return event
      })
    )
  }
}

CodePudding user response:

The cache sets the content as frozen/read-only, to avoid you tampering with the cache.

Try updating the stream yourself :

getSeriesWithGenres() {
    const series$ = this.getSeries()
    const genres$ = this.getGenres()

    return forkJoin([series$, genres$]).pipe(
      map(([series, genres]) => series.map(serie => ({
        ...serie,
        genre_ids: serie.genre_ids.map(
          genreId => genres.find((genre: Genre) => genre.id === genreId)?.name
        )
      }))),
    )
}
  • Related