2

Background:

I used nestjs interceptor to put returned data from controller inside data property and add some other properties. Now I'd like to use @ApiOkResponse that would reflect the nested properties.

Controller returns

{
   prop1: 'val1',
   prop2:  'val2'
}

After it was intercepted it returns

data: {
   prop1: 'val1',
   prop2:  'val2'
},
addedProp: 'addedVal'

I also have say two classes:

// Have many variations of similar classes (for different controllers (types of data)
class NotYetIntercepted {
   @ApiProperty()
   prop1: string;

   @ApiProperty()
   prop2: string;
}

class Intercepted<T = any> {
   @ApiProperty()
   data: T;

   @ApiProperty()
   addedProp: string;
}

Challenge

Now I'd like to add to my controllers @ApiOkResponse({ type: Intercepted }) but also somehow specify that data property of class Intercepted should be of type NotYetIntercepted.

I tried creating a custom decorator like this:

import { ValidateNested } from 'class-validator';
import { ApiProperty, ApiResponseOptions, ApiOkResponse } from '@nestjs/swagger';
import { Intercepted } from '@appnamespace/models';
import { Type } from 'class-transformer';

export const CustomApiOkResponse = (notYetIntercepted: Function, options?: Omit<ApiResponseOptions, 'type'>) => {

  class InterceptedWithData extends Intercepted {

    @ApiProperty()
    @ValidateNested()
    @Type(() => notYetIntercepted)
    data: typeof notYetIntercepted;
  }

  return ApiOkResponse({
    ...options,
    type: InterceptedWithData,
  });
};

That didn't work. When I removed @Type() => notYetIntercepted) and set data as data: notYetIntercepted it sort of worked somehow (with typescript warning) but it overridden all values in my swagger docs to whatever was the last passed value to (@CustomApiOkResponse(AnotherNotYetIntercepted)).

I know I could create a class for each nested data type but is there a cleaner solution?

Thank you for your time

Max
  • 93
  • 3
  • 7

2 Answers2

0

One option would be to pass the Swagger schema dynamically using your custom Decorator. In nestjs/terminus something very similar has been done - see health-check.decorator.ts and health-check.schema.ts.

In a nutshell, you can pass the custom definitions via the schema option.

export const CustomApiOkResponse = (notYetIntercepted: Function, options?: Omit<ApiResponseOptions, 'type'>) => {
  return ApiOkResponse({
    ...options,
    schema: {
     // .. your Swagger Schema
    }
  });
};
Livio Brunner
  • 727
  • 2
  • 8
  • 22
0

I had some problems with circular dependencies when tried to set type on @ApiOkResponse, and my problem have been solved by removing all null values properties in DTO. Nest refusing null as type in DTO.

hellvisor
  • 463
  • 5
  • 15