1

I am using trying to create a small project based on Angular 20, where I provide front-end for data and charts from Fintacharts API, using SSR (Server-side Rendering), because CORS policies didn't allow me to fetch data using a normal built. I have encountered a problem of fetching data for the templates using HttpClient. I have implemented a Fintech service, code below provided:

import { isPlatformServer } from '@angular/common';
import { Inject, Injectable, makeStateKey, OnInit, PLATFORM_ID, TransferState } from '@angular/core';
import { environment } from '../../environments/environment.development';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { get } from 'http';

@Injectable({
  providedIn: 'root'
})
export class Fintech {//implements OnInit {
  private Token: string="";
  private TokenType: string="";
  private Providers: Array<string> = [];

  constructor(private http: HttpClient) {
    this.getToken();
  }

  private async getToken(){
    let headers: HttpHeaders;
    headers = new HttpHeaders();
    headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
    let body = new URLSearchParams({
      "grant_type": "password",
      'client_id': 'app-cli',
      'username': environment.USERNAME,
      'password': environment.PASSWORD
    }).toString();
    let ExpireTime: number = 0;
    this.http.post(`${environment.URI}/identity/realms/fintatech/protocol/openid-connect/token`, 
      body, {headers: headers}).subscribe({
        next: (data) => {
          console.log(data);
          let Object_data = Object(data);
          this.Token = Object_data["access_token"];
          this.TokenType =  Object_data["token_type"];
          ExpireTime =  Object_data["expires_in"];
        },
        error: (error) => {
          console.log(error);
        }
    });
    if (ExpireTime != 0) {
      setTimeout(() => this.getToken(), ExpireTime * 1000);
    }
  }

  public getProviders() {
    let headers: HttpHeaders;
    headers = new HttpHeaders();
    headers = headers.set('Authorization', `${this.TokenType} ${this.Token}`);
    return this.http.get(`${environment.URI}/api/instruments/v1/providers`, 
            { headers: headers })

  }
}

I tried to fetch data from components that utilize this Service, but it only enters endless loop of the same error upon fetch (sometimes it actually fetches data, but only at the beginning). I have figured out that component call the GetProviders() methods 6 times before, and 6 times after the Token is finally fetched, but I have no idea why it keeps going in case it encounters an error. Especially when I tried to place return; to exit the method.

Aside from the timeout problem, I have no idea what causes this error, and how to handle/fix it:

HttpErrorResponse {
  headers: _HttpHeaders {
    headers: Map(0) {},
    normalizedNames: Map(0) {},
    lazyInit: undefined,
    lazyUpdate: null
  },
  status: 0,
  statusText: 'Unknown Error',
  url: 'https://platform.fintacharts.com/api/instruments/v1/providers',
  ok: false,
  type: undefined,
  name: 'HttpErrorResponse',
  message: 'Http failure response for https://platform.fintacharts.com/api/instruments/v1/providers: 0 undefined',
  error: TypeError: fetch failed
      at node:internal/deps/undici/undici:15422:13
      at _ZoneDelegate.invoke (eval at runInlinedModule (file:///mnt/d/Programs/VSC%20Projects/StockServer/node_modules/vite/dist/node/module-runner.js:988:20), :336:158)
      at ZoneImpl.run (eval at runInlinedModule (file:///mnt/d/Programs/VSC%20Projects/StockServer/node_modules/vite/dist/node/module-runner.js:988:20), :105:35)
      at eval (eval at runInlinedModule (file:///mnt/d/Programs/VSC%20Projects/StockServer/node_modules/vite/dist/node/module-runner.js:988:20), :1040:30)
      at _ZoneDelegate.invokeTask (eval at runInlinedModule (file:///mnt/d/Programs/VSC%20Projects/StockServer/node_modules/vite/dist/node/module-runner.js:988:20), :362:171)
      at ZoneImpl.runTask (eval at runInlinedModule (file:///mnt/d/Programs/VSC%20Projects/StockServer/node_modules/vite/dist/node/module-runner.js:988:20), :143:37)
      at drainMicroTaskQueue (eval at runInlinedModule (file:///mnt/d/Programs/VSC%20Projects/StockServer/node_modules/vite/dist/node/module-runner.js:988:20), :522:23)
      at processTicksAndRejections (node:internal/process/task_queues:105:5)
      at runNextTicks (node:internal/process/task_queues:69:3)
      at listOnTimeout (node:internal/timers:569:9) {
    [cause]: AggregateError [ETIMEDOUT]: 
        at internalConnectMultiple (node:net:1134:18)
        at internalConnectMultiple (node:net:1210:5)
        at Timeout.internalConnectMultipleTimeout (node:net:1742:5)
        at listOnTimeout (node:internal/timers:610:11)
        at process.processTimers (node:internal/timers:543:7) {
      code: 'ETIMEDOUT',
      [errors]: [Array]
    }
  }
}
4
  • 1
    0 undefined means handshake failed due to some error. You need to update your CORS policy. Commented Aug 5 at 5:54
  • 1
    Wait for the token before making authenticated requests, otherwise your requests will fail and may be retried endlessly. Commented Aug 5 at 6:49
  • 1
    The core issue is that your service tries to fetch API data before the authentication token is available, resulting in failed requests and repeated errors. Make sure all API calls that need authentication are made only after the token is successfully received. One common solution is to use an RxJS ReplaySubject or a Promise to signal when the token is available, and only perform data requests after that. Commented Aug 5 at 6:50
  • I tried using ReplaySubject, but I have no knowledge how I can do that properly, and got the same error. Same with the Promise. But thanks for the heads up Commented Aug 5 at 7:13

1 Answer 1

0

Denis, Please try it like this. As Jagadeesh mentioned, the token was not available before the code moved on. This is how you can use the ReplaySubject.

private tokenReady$ = new ReplaySubject<void>(1); // Emits once when token is ready

constructor(private http: HttpClient) {
  this.getToken();
}

private getToken() {
  let headers = new HttpHeaders({
    'Content-Type': 'application/x-www-form-urlencoded',
  });

  const body = new URLSearchParams({
    grant_type: 'password',
    client_id: 'app-cli',
    username: environment.USERNAME,
    password: environment.PASSWORD,
  }).toString();

  this.http.post(`${environment.URI}/identity/realms/fintatech/protocol/openid-connect/token`, body, { headers })
    .subscribe({
      next: (data: any) => {
        this.Token = data["access_token"];
        this.TokenType = data["token_type"];
        const expireTime = data["expires_in"];

        this.tokenReady$.next(); // ✅ Notify that token is ready

        setTimeout(() => this.getToken(), expireTime * 1000);
      },
      error: (err) => console.error('Token fetch failed', err)
    });
}

Then revise your getModifiers method like this.

public getProviders(): Observable<any> {
  return this.tokenReady$.pipe(
    // Only emit after token is ready
    // switchMap ensures we wait before making the call
    switchMap(() => {
      const headers = new HttpHeaders().set('Authorization', `${this.TokenType} ${this.Token}`);
      return this.http.get(`${environment.URI}/api/instruments/v1/providers`, { headers });
    }),
    take(1) // Only respond once per call
  );
}

This should allow a safe call to getProviders as the call will wait for the token to arrive.

Hope this helps.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.