I need a controller action that:
- Authorizes the call
- Validates it
- Returns the 202 Accepted status
- Continues processing the request
- Makes a call to external API with the results of previously accepted and now processed request.
First two points are easy, I use AuthGuard
then class-validator
. But I don't know how to return the HTTP response then continue with processing.
As the request consists of an array of (possibly long-running) tasks I thought of using interceptor that uses RxJS to observes the status of tasks and calls external PI upon their completion. However, I have no experience with using RxJS or interceptors (not this way) so I'd really don't know how to leave interceptor's process running but immediately pass control to controller's action.
Also, perhaps there is another, better way? No interceptor but just put all the flow logic in the controller? Some other option?
I need a controller action that:
- Authorizes the call
- Validates it
- Returns the 202 Accepted status
- Continues processing the request
- Makes a call to external API with the results of previously accepted and now processed request.
First two points are easy, I use AuthGuard
then class-validator
. But I don't know how to return the HTTP response then continue with processing.
As the request consists of an array of (possibly long-running) tasks I thought of using interceptor that uses RxJS to observes the status of tasks and calls external PI upon their completion. However, I have no experience with using RxJS or interceptors (not this way) so I'd really don't know how to leave interceptor's process running but immediately pass control to controller's action.
Also, perhaps there is another, better way? No interceptor but just put all the flow logic in the controller? Some other option?
Share Improve this question edited Nov 9, 2018 at 21:03 Kim Kern 60.4k20 gold badges216 silver badges212 bronze badges asked Nov 8, 2018 at 10:14 ForsetiForseti 2,9456 gold badges25 silver badges34 bronze badges2 Answers
Reset to default 12I expect you have a service that does the external API call (asynchronously) and returns either a Promise
or an Observable
:
@Injectable()
export class ExternalApiService {
constructor(private readonly httpService: HttpService) {}
makeApiCall(data): Observable<AxiosResponse<any>> {
return this.httpService.post('https://external.api', data);
}
}
I also assume that there is a PreprocesserService that makes asynchronous calls (for example getting user information from a database).
Controller
@Post()
@UseGuards(AuthGuard('jwt'))
@HttpCode(HttpStatus.ACCEPTED)
async post(@Body(new ValidationPipe()) myDataDto: MyDataDto) {
// The preprocessing might throw an exception, so we need to wait for the result
const preprocessedData = await this.preprocessService.preprocess(myDataDto);
^^^^^
// We do not need the result to respond to the request, so no await
this.externalApiService.makeApiCall(preprocessedData);
return 'Your data is being processed.';
}
When you make asynchronous calls, the execution of your controller method will only wait if you explicitly tell it to by either using async/await
or returning a Promise
/ an Observable
.
In this example, we want to wait for the result of the this.preprocessService.preprocess(myDataDto)
before we send the response. We do that by using await
(the method must be declared as async
).
We want to make the request to the external API but we do not need the result for our response. Because we are not the using await
here it will make the http call and immediately execute the next line (the return statement) without waiting for the result.
If your service returns a promise you could do something like the following, the API will return 202 while the processing continues. Of course any processing with the 'then' is happening after 'beginProcessing' is complete.
@Post()
@HttpCode(HttpStatus.ACCEPTED)
beginProcessing(@Req() request, @Body() data: Data): void {
this.service.process(data).then(...);
}