Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return 202 Accepted and then continue processing the request in Nest.js

I need a controller action that:

  1. Authorizes the call
  2. Validates it
  3. Returns the 202 Accepted status
  4. Continues processing the request
  5. 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?

like image 974
Forseti Avatar asked Nov 08 '18 10:11

Forseti


2 Answers

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(...);
}
like image 40
Rich Duncan Avatar answered Oct 24 '22 04:10

Rich Duncan


I 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.

like image 151
Kim Kern Avatar answered Oct 24 '22 06:10

Kim Kern