Search code examples
node.jsnestjs

How to push data after response has been intercepted using nest.js interceptors?


I am sorry if my request sounds easy but I am very new to nestJs.

The goal of my code I have a problem with is:

  • I enter a physical address through a swagger api.
  • I geolocate this address.
  • I want to save both the address and the geolocation result.

For this, I have 2 observables that get data through a nestjs interceptor (before saving to database) and would like to push this data into my database. I am using mongoose=>mongoDB.

The points.interceptor.ts code is:

export class PointsInterceptor implements NestInterceptor {
  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {

    var monaddress = context.switchToHttp().getRequest().body
    const geocodeResult = from(geocoder.geocode(monaddress));
    const geoCoder = await lastValueFrom(geocodeResult);
    return next.handle().pipe(
 //     tap(value => console.log(value)),
      map(value => {
        // Returning an object from both next.handle() and geoPremiseToObservable
        return {
          ...value,
          geoCoder: geoCoder
        }
      })
      )
  }

}

What I can see through VSCode debugger is that data is here when excecuting next.handle(): enter image description here

The points.schema.ts is:

export const PointSchema = new mongoose.Schema({
  address: String,
  geocoded: Object
});

The create-point.dto.ts is:

export class CreatePointDto {
    readonly address: string;
    readonly geocoded: Object;

The point.service.ts is

@Injectable()
export class PointsService {
  constructor(
    @Inject('POINT_MODEL')
    private PointModel: Model<Point>,
  ) {}

  async create(createPointDto: CreatePointDto): Promise<Point> {
    const createdPoint = new this.PointModel(createPointDto);
    return createdPoint.save();
  }

  async findAll(): Promise<Point[]> {
    return this.PointModel.find().exec();
  }
}
    }

The point.controller.ts is :

export class PointsController {
  constructor(private readonly pointsService: PointsService) {}

  @Post()
  @UseInterceptors(PointsInterceptor)
  create(@Body() createPointDto: CreatePointDto) {
     return this.pointsService.create(createPointDto);
  }

  @Get()
  findAll() {
    return this.pointsService.findAll();
  }
}

Thank you in advance for every help.


Solution

  • This should be relatively simple task, but you have to understand how interceptor works.

    Inside interceptor, whatever you do before return will be done before it goes to the service/controller and whatever you do after return will be done after the lifespan of the service/controller.

    Lets change your interceptor a bit to do your work

    const request = context.switchToHttp().getRequest();
    if(request.body) {
      // I dont have  information how you are expecting the address in the controller, Im assuming you are passing as string
      const monaddress = request.body;
      const geocodeResult = from(geocoder.geocode(monaddress));
      const geocoded = await lastValueFrom(geocodeResult);
    
      // matching with your create-point.dto
      request.body = {
        address: monaddress
        geocoded: geocoded
      }
    }
    

    so this will modify your request body to have the geocoded field with the decoded data and will be available in the service itself