0

I have an NestJS app which is a service for other applications to handle each of their own app and document related processess. I am using an Interceptor to capture every request and response and updating a database for auditing purposes.

A problem I am running into is implementing a way to update each request's progression...

export enum RequestDetails {
    INPROGRESS = 'INPROGRESS',
    COMPLETE = 'COMPLETE',
    ERROR = 'ERROR'
}

Meaning in the interceptor I have a object where I am handling the initial state of the request and updating the database, see below.

intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        // any route with this decorator will not be logged.
        const preventReqResLog: boolean = this.reflector.get<boolean>('preventReqResLog', context.getHandler());

        const req: Request = context.switchToHttp().getRequest();
        const { statusCode } = context.switchToHttp().getResponse();
        const { originalUrl, method, params, query, headers, body } = req;
        const isHealthRoute: boolean = originalUrl.endsWith('health/detailed') || originalUrl.endsWith('health');

        const timeStamp: string = this.dSUtil.getCurrentTS();
        const requestDetails: DSRequestRequestDetails = new DSRequestRequestDetails();

        requestDetails.appName = body.appName;
        requestDetails.customerName = body.customerName;
        requestDetails.originalUrl = originalUrl;
        requestDetails.method = method;
        requestDetails.params = params;
        requestDetails.id = headers.awsrequestid as string;
        requestDetails.type = EItemType.REQUEST;
        requestDetails.query = query;
        requestDetails.headers = headers;
        requestDetails.requestState = EDSRequestRequestDetails.INPROGRESS; // in progress
        requestDetails.timeStamp = timeStamp;
    try {
        if (!isHealthRoute && !preventReqResLog) {
            this.logService.info(
                JSON.stringify({
                    originalUrl,
                    method,
                    params,
                    query,
                    headers,
                    requestId: DSRequestContextService.getCurrentReqId(),
                    body
                })
            );

            (async () => {
                await this.dsDynamoDBService
                    .putItem({
                        TableName: this.dsConfig.aws.dynamoDB.table,
                        Item: requestDetails
                    })
                    .then((response) => response)
                    .catch((error) => {
    
                        this.logService.error(error);
                    });
            })();
        }
        (req as any).isLogReqRes = !preventReqResLog;
    } catch (error) {
        this.logService.error(error);
    }

But how would I update each request with its progress considering they're asynchronus?

I imagine if there was an error I should adding this line to the catch in the database call right?

requestDetails.requestState = EDSRequestRequestDetails.ERROR;

And if its successsfull do I update this in the

`return next.handle().pipe(
        tap((data) => {
            try {
                if (!isHealthRoute && data) {
                    data.requestId = DSRequestContextService.getCurrentReqId();
                }
                if (!isHealthRoute && !preventReqResLog) {
                    this.logService.info(
                        JSON.stringify({
                            originalUrl,
                            method,
                            params,
                            query,
                            headers,
                            requestId: DSRequestContextService.getCurrentReqId(),
                            statusCode,
                            data
                        })
                    );

                        (async () => {
requestDetails.requestState = EDSRequestRequestDetails.COMPLETE; 
                    await this.dsDynamoDBService
                        .putItem({
                            TableName: this.dsConfig.aws.dynamoDB.table,
                            Item: requestDetails
                        })
                        .then((response) => response)
                        .catch((error) => {
                            this.logService.error(error);
                        });
                })();
                    }
                } catch (error) {
                    this.logService.error(error);
                }
            })
        );`

I think what is confusing me is let's say Request A comes through and being that it is async it takes some time, and then Request B comes through and also takes time, but then Request C comes through and is pretty fast. Will the interceptor just wait and update requestDetails.requestState based on my logic for each?

Is there more to this? Thanks! Any help would be appreciated!

Antonio Pavicevac-Ortiz
  • 7,239
  • 17
  • 68
  • 141

1 Answers1

0

Interceptors work on Observables, and each observable value keeps its context, so requests A, B, and C will all have their own values as they proceed through the server without issue (so long as it's the request object you update and read from and not some global store variable that doesn't track by some sort of key tied to each request). Each request will also be processed in realtime without waiting for other requests to have finished, so if A and B are taking their async time and C blazes through, you might see a log that A and B started processing, then that C started and finished processing before A or B finish, but that's fine due to Node's event loop and handling of asynchronous events.

Jay McDoniel
  • 57,339
  • 7
  • 135
  • 147
  • Thank you for responding! So question for you I am just realizing the `requestState` prop I have created on the `requestDetails` object for each Interceptor should be getting updates if the controller and/or the service fail. How could I inject update if that happens? Right now my error is if the call to the database borks and that's not request related... – Antonio Pavicevac-Ortiz Jul 24 '23 at 16:46