#Http Service Intercept

83 messages · Page 1 of 1 (latest)

eager crest
#

how should i intercept the httpService requests while also intercepting the controller request

wide dragon
#

The question isn't clear. What do you want to do with each interception?

eager crest
#

ok

#

im trying to do get some orders from ebay and amazon but i need to handle the authorization for both

#

and i tried to use a nest interceptor and an axios interceptor to handle that

#

but im not sure that is the right approach

wide dragon
#

So what's the current problem?

eager crest
#

the problem is that i dont really understand how should i do it

#

i dont know if there is a simpler way to do something like that

wide dragon
#

And I don't know what "it" in this context is

eager crest
#

"it" is handling 3rd party api authorization

wide dragon
#

That would be up to their documentation, right?

eager crest
#

whos?

wide dragon
#

Are you not sure of how to authenticate the request to third party servers from your server, or something else?

eager crest
#

i know how to get authorization from the third party api, what i dont know is how to handle it in a way that whenever i do a request to that api using the httpService i dont have to handle the authorization every time

wide dragon
#

Is the client going to send the authentication token for the third party server?

eager crest
#

no

#

the server already has the access tokens

wide dragon
#

Are you saving the initial authentication token anywhere?

eager crest
#

all i need to do is add the access token to the request and if it expires refresh it and then use the new access token

wide dragon
#

Why do you need to add the access token to the request?

eager crest
#

what do you mean why

#

couse the third party api asks for an access token

wide dragon
#

To the request object coming into the server or the request object being sent to the third party server?

eager crest
#

to the request for the third party api

wide dragon
#

So it seems like you have a good idea of what you need to do, where's the concern?

eager crest
#
export class AuthInterceptor implements NestInterceptor {

  private _encryptKey=this.config.get('ENCRYPTION_KEY')
  constructor(
    private readonly httpService: HttpService,
    private config: ConfigService,
    private authService: AuthService,
    private ebayProfileService: EbayProfileService,
    ) {}

  async intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Promise<Observable<any>> {
    const request = context.switchToHttp().getRequest<Request>();
    const axiosInstance = this.httpService.axiosRef;
    const profileId= request.body.profileId
    const userId = request.user['sub'];
    
    const { accessHash, refreshHash } = await this.ebayProfileService.findBy(profileId);
    
    let accessToken = {value:AES.decrypt(
      accessHash,
      this._encryptKey,
    ).toString(enc.Utf8)};
    const refreshToken = AES.decrypt(
      refreshHash,
      this._encryptKey,
    ).toString(enc.Utf8);
    const axiosRequest = axiosInstance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        return this.requestConfig(config, accessToken.value);
      },
    );

    const axiosResponse = axiosInstance.interceptors.response.use(
      (response: AxiosResponse) => {
        return response;
      },
      (error) => {
        return this.responseError(error, refreshToken, axiosInstance, profileId, accessToken);
      },
    );

    return next.handle().pipe(
      tap(() => {
        axiosInstance.interceptors.request.eject(axiosRequest);
        axiosInstance.interceptors.response.eject(axiosResponse);
      }),
    );
  }
#

the problem is that it seems too much brute force to just do that

#

or is it just me

wide dragon
#

Why does it feel too much?

eager crest
#

🤷‍♂️

#

i dont know maybe im not understanding a backend behavior or something

#

or maybe this kind of handling was already common and i was just trying to reinvent the wheel

wide dragon
#

Does this work? That should be the first question. Then worry about optimization, right?

eager crest
#

yes it does work

wide dragon
#

Okay. So why is this al handled in a Nest interceptor?

eager crest
#

that is because the client calls the my api to get the orders

#
@Patch()
  @UseInterceptors(AuthInterceptor)
  async setOrders(
    @GetCurrentUserId() id: number,
    @Body() { profileId }: { profileId: number },
  ) {
    const profile = await this.profileService.findBy(profileId);
    let next: boolean = true;
    let url: string = '';
    const lastUpdateDate = new Date().toISOString();
    const sent: boolean = !profile.lastUpdateDate;

    do {
      await lastValueFrom(
        this.orderService.getOrdersFromApi(url, profile.lastUpdateDate).pipe(
          tap((data) => {
            data.orders.forEach(async (order) => {
              const dto: CreateOrderDto = {
                ...plainToInstance(CreateOrderDto, order, {
                  excludeExtraneousValues: true,
                }),
                profile,
              };
              await this.orderService.createOrUpdateOrder(dto, sent);
            });
            url = data.next;
            if (!data.next) {
              next = false;
            }
          }),
        ),
      );
    } while (next);

    await this.profileService.update(profileId, { lastUpdateDate });
    console.log('done');
    
  }```
#

when the client manually refreshes their orders the server does the api calls then i use a nest interceptor to then intercept the httpService request in my service

wide dragon
#

Why use a nest interceptor though? I don't see why not do that all in the service

eager crest
#
getOrdersFromApi(
    url: string,
    lastUpdateDate: string,
  ): Observable<OrderResponse> {
    if (!url) {
      url = this._ebayUrl + 'sell/fulfillment/v1/order';
      if (lastUpdateDate) {
        url +=
          '?filter=lastmodifieddate:' + encodeURI('[' + lastUpdateDate + '..]');
      }
    }
    return this.http.get<OrderResponse>(url).pipe(
      map((res) => {
        return res.data;
      }),
    );
  }```
this is the service method im using and i use the nest interceptor to handle every httpService request made
#

i dont know how else should i do that

#

couse what if i have more than one third api call in different methods

wide dragon
#

You could build in the retry into the http.get. Have an observable retryWhen operator and pass the profile to have the service handle all of that. But if this is working for you, then go with it if it isn't really causing any issues

eager crest
#

i mean it had not caused any issues until recently where it started to get 401 errors (i think, the logs were too fast) and trying to refresh infinitely

#

that has never happened before it was just one time and a quick restart "fixed" it

#

so i thought that maybe my approach was too weird and there was like a wild bug somewhere

wide dragon
#

As I alluded to, I would much rather keep this logic together in the service and make use of RxJS pipelines, like using retryWhen to dictate how and when to retry a request and what to do beforehand, but I don't have any specific examples at the moment to look atfor reference

eager crest
#

isnt retryWhen deprecated

wide dragon
eager crest
#

ok let me get this straight

#

what retry does is retry x amount of times if the obserbable gives an error so where do i put the logic for the access token and the refreshing

wide dragon
#

The other option would be catchError which might actually work better in this case

#

I was blazing over the docs and saw the mention of retry's delay being able to replace retryWhen, which is why I mentioned it

eager crest
#

ok but can i change the headers of the request with a pipe or someting

wide dragon
#

Why would you need to change the headers inside the rxjs pipe?

eager crest
#

couse what i need to do is add the access token to the request header and if the token is expired, refresh it and then try again

wide dragon
#

Okay, but why does that relate back to the initial configuration object? You send the initial request, it fails, you go to the catch, you send a new request, get the proper response, update the credentials, send a new request, right?

eager crest
#

😵‍💫 i got lost

#

i want to do an httpService request that needs an access token in the header if the error is 401 i call refresh token and then make the same request but with the new access token

#

so how do i do that for every request to that api

#

for different endpoints

#

that is why i used an interceptor

wide dragon
#

Something generally like this

this.get(url, options).pipe(
  catchError(err => {
    return this.http.get('/refresh', refreshOptions)
  }).pipe(
    map(res => saveAccessAndRefreshBasedOnResponse(res)),
    switchMap(() => this.http.get(url, { ...options, ...getAccessTokenFromProfile() })))
  })
)
#

Parenthesis might be slightly off. Discord isn;'t great for writing code

eager crest
#

ok i see, but i have to do that with every request

wide dragon
#

Then abstract the service out so it's built in to the outgoing http request

eager crest
#

huh?😅

#

you mean like do a custom http service that extends from the actual http service?

wide dragon
#

Yeah, pretty much

eager crest
#

is there something about that in the docs?

wide dragon
#

Nope, just your standard class extension OOP practices

eager crest
#

oh, right, this is the "i gotta crawl before i walk" part

#

this is just a side question is there a specific degree that gets into all of these nooks and crannies

wide dragon
#

Not really, just generally software development knowledge that comes with experience

eager crest
#

ok i will look into that custom http service

#

see? i knew my approach was to overcomplicated

#

that is called a gateway right?

wide dragon
#

What is called a gateway

eager crest
#

you know what? never mind its fine

#

thanks for your help and sorry for wasting your time

wide dragon
#

Not a waste of time. Happy to help people learn

eager crest
#

👍