The following code is an extract of a small library I wrote. Full code is available here.My question is about the following function. I added comments on each state. I'm wondering if I could do things easier, I've the feeling I'm using too many flatMap:/**  * request  *  * @param {string} method  * @param {RxRestItem|FormData|URLSearchParams|Body|Blob|undefined|Object} [body]  * @returns {Observable<RxRestItem|RxRestCollection>}  */request(method: string, body?: BodyParam): Observable<RxRestItem|RxRestCollection> {  let requestOptions = {    method: method,    headers: <Headers> this.requestHeaders,    //this is just a function that transforms a Body to json if needed    body: this.requestBodyHandler(body)  }  let request = new Request(this.URL + this.requestQueryParams, requestOptions);  //let's start from our Request object  return Observable.of(request)  //here, we call functions that can alter the request, think middlewares (see below for this method)  .flatMap(this.expandInterceptors(Config.requestInterceptors))  //now we have the final request, we do a `fetch` on it  .flatMap(request => fetch(request))  //Transform the response to a javascript object  .flatMap(body => Observable.fromPromise(this.responseBodyHandler(body)))  //Here the library transforms object to internal types  .map(e => {    if (!Array.isArray(e)) {      let item: RxRestItem      if (this instanceof RxRestItem) {        item = this        item.element = e      } else {        item = new RxRestItem(this.$route, e)      }      item.$fromServer = true      return item    }    return new RxRestCollection(this.$route, e.map(e => {      let item = new RxRestItem(this.$route, e)      item.$fromServer = true      return item    }))  })  //response middlewares  .flatMap(this.expandInterceptors(Config.responseInterceptors))  .catch(body => {    //this was a real error    if (!(body instanceof Response)) {      return Observable.throw(body)    }    //error interceptors    return Observable.of(body)    .flatMap(this.expandInterceptors(Config.errorInterceptors))  })}The middleware function feels complicated too, it takes an array of functions and apply them (like a reduce). However, this interceptor function can return a Promise, an Observable or a changed value:/**  * expandInterceptors  *  * @param {RequestInterceptor[]|ResponseInterceptor[]|ErrorInterceptor[]} interceptors  * @returns {Observable<any>} fn  */expandInterceptors(interceptors: RequestInterceptor[]|ResponseInterceptor[]|ErrorInterceptor[]) {  return function(origin: any): Observable<any> {    return (<any>interceptors).reduce(      (obs: Observable<any>, interceptor: any) =>        obs.concatMap(value => {          let result = interceptor(value)          if (result === undefined) {            return Observable.of(value)          }          if (result instanceof Observable || result instanceof Promise) {            return result          }          return Observable.of(result)        }),      Observable.of(origin)    )  }}Do you think that I'm doing things the right way? Can this code be simplified?I'm new to reactive programming and still trying to understand some of it's core concepts!
REST request using reactive programming
library;rest;typescript;reactive programming;rxjs