#Http Service Intercept
83 messages · Page 1 of 1 (latest)
The question isn't clear. What do you want to do with each interception?
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
So what's the current problem?
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
And I don't know what "it" in this context is
"it" is handling 3rd party api authorization
That would be up to their documentation, right?
whos?
Are you not sure of how to authenticate the request to third party servers from your server, or something else?
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
Is the client going to send the authentication token for the third party server?
Are you saving the initial authentication token anywhere?
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
Why do you need to add the access token to the request?
To the request object coming into the server or the request object being sent to the third party server?
to the request for the third party api
So it seems like you have a good idea of what you need to do, where's the concern?
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
Why does it feel too much?
🤷♂️
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
Does this work? That should be the first question. Then worry about optimization, right?
yes it does work
Okay. So why is this al handled in a Nest interceptor?
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
Why use a nest interceptor though? I don't see why not do that all in the service
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
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
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
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
isnt retryWhen deprecated
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
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
ok but can i change the headers of the request with a pipe or someting
Why would you need to change the headers inside the rxjs pipe?
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
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?
😵💫 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
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
ok i see, but i have to do that with every request
Then abstract the service out so it's built in to the outgoing http request
huh?😅
you mean like do a custom http service that extends from the actual http service?
Yeah, pretty much
is there something about that in the docs?
Nope, just your standard class extension OOP practices
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
Not really, just generally software development knowledge that comes with experience
ok i will look into that custom http service
see? i knew my approach was to overcomplicated
that is called a gateway right?
What is called a gateway
you know what? never mind its fine
thanks for your help and sorry for wasting your time
Not a waste of time. Happy to help people learn
👍