swagger icon indicating copy to clipboard operation
swagger copied to clipboard

[Feature Request] Multiple response with the same status code

Open ziadalzarka opened this issue 5 years ago • 23 comments

I'm submitting a...


[ ] Regression 
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

Swagger only allows one response per status code, but sometimes we have more than one response body.

Expected behavior

We can work around this in the decorator by:

  • Changing the status field type from number to number | string
  • Checking if the status already exists
  • If it exists, we add a space to the response code

Minimal reproduction of the problem

@ApiForbiddenResponse({ type: UnauthorizedTokenException })
@ApiForbiddenResponse({ type: InsufficientRolesException })

What is the motivation / use case for changing the behavior?

I have more than one error response with different Dto classes using the same status code.

ziadalzarka avatar Apr 06 '19 13:04 ziadalzarka

Would you like to create a PR? :)

kamilmysliwiec avatar Apr 15 '19 15:04 kamilmysliwiec

Would you like to create a PR? :)

Yes I have created a PR resolving the issue.

ziadalzarka avatar May 04 '19 16:05 ziadalzarka

Any updates?

ziadalzarka avatar May 19 '19 00:05 ziadalzarka

something new?

wilsson avatar Jan 16 '20 19:01 wilsson

+1

piotrgomola avatar Feb 27 '20 10:02 piotrgomola

Would love to see this merged.

peterwbeck avatar Apr 22 '20 22:04 peterwbeck

I might be late for this but why don't you use the SchemaObject interface to document your responses?

@ApiForbiddenResponse({
  schema: {
    anyOf: refs([
      UnauthorizedTokenException,
      InsufficientRolesException
    ])
  }
})

fwoelffel avatar Jun 18 '20 13:06 fwoelffel

@fwoelffel Hi I came in while Googleing because I needed a multiple response from swagger. The multi error response you have attached is resolved, but it does not apply to the 200 success responses. Because the arrangement of "refs" only request "funcion types." What kind of settings do I need to set to respond to 200 multiple responses? I can't see anything in browser with Schema Object like this:

  @ApiOkResponse({
    schema: { anyOf: [{ $ref: getSchemaPath(UserOrderInfoDto) }] },
  })

// or

  @ApiOkResponse({
    schema: { anyOf: [{ type: getSchemaPath(UserOrderInfoDto) }] },
  })

;(

melangyun avatar Aug 05 '20 07:08 melangyun

@fwoelffel Hi I came in while Googleing because I needed a multiple response from swagger. The multi error response you have attached is resolved, but it does not apply to the 200 success responses. Because the arrangement of "refs" only request "funcion types." What kind of settings do I need to set to respond to 200 multiple responses? I can't see anything with Schema Object setting like this:

I'm not sure I fully understand your issue, but I'll try to help.

Have you tried with refs like I did in my example? refs can be imported from @nestjs/swagger.

import {
  ApiOkResponse,
  refs,
} from '@nestjs/swagger';

// ...

@ApiOkResponse({
  schema: {
    anyOf: refs([
      UserOrderInfoDto
    ]),
  },
})

fwoelffel avatar Aug 05 '20 07:08 fwoelffel

@fwoelffel Yes, I tried. However, refs require function array type and my UserOrerInfoDto is Class type, so type error occured. 스크린샷 2020-08-05 16 57 50

melangyun avatar Aug 05 '20 08:08 melangyun

@fwoelffel Yes, I tried. However, refs require function array type and my UserOrerInfoDto is Class type, so type error occured. 스크린샷 2020-08-05 16 57 50

Could you share your UserOrderInfoDto implementation? Make sure it is a class and not an interface.


EDIT: Ok, I'm sorry I didn't realize this earlier. Your DTOs should not be wrapped in an array:

@ApiOkResponse({
  schema: {
    anyOf: refs(
      UserOrderInfoDto
    ),
  },
})

Sorry for misleading you. 😞

fwoelffel avatar Aug 05 '20 10:08 fwoelffel

Thank you very much! Your code works well and expresses multiple responses well. In fact, the essential problem was that what I was going to designate as a response "schema" was not registered as a "schemas", so swagger showed empty object.

For those who have missed the official document like me, write additional. If your DTO is not represented or read by an empty object, you must register the DTO through @ApiExtraModels()

스크린샷 2020-08-06 00 55 43

Thank you for your help!!

melangyun avatar Aug 05 '20 15:08 melangyun

I would like this as well

woodcockjosh avatar Aug 18 '21 16:08 woodcockjosh

If it exists, we add a space to the response code

but OpenAPI v3 doesn't allow that :(

error

micalevisk avatar Aug 21 '21 17:08 micalevisk

Another issue that comes with using multiple @ApiX() decorators in this way is that we won't be able to implement the following pattern:

@Controller()
@ApiOkResponse({ description: 'fallback ok' })
class SomeController {
  @Get('foo1')
  foo1() {} // Will have 200 with 'fallback ok' description
  @Get('foo2')
  foo2() {} // Same as above

  @Get('bar')
  @ApiOkResponse({ description: 'only this one will appear in the docs for this endpoint' })
  bar() {}
}

micalevisk avatar Aug 22 '21 15:08 micalevisk

Isn't this essentially adding multiple response examples to the same swagger path?

kasvith avatar Sep 14 '21 03:09 kasvith

I tried the same but nothing will work. @ApiOkResponse({ schema: { oneOf: refs( UpdateDeviceTokenResponse ) } }) UpdateDeviceTokenResponse - This will be a class. image

VulchiSarath avatar Dec 17 '21 07:12 VulchiSarath

@VulchiSarath have you registered UpdateDeviceTokenResponse? either configuring the nestjs swagger plugin or using @ApiExtraModels(UpdateDeviceTokenResponse)

https://docs.nestjs.com/openapi/types-and-parameters#extra-models

micalevisk avatar Dec 17 '21 11:12 micalevisk

does someone know how to return either UserOrderInfoDto | null?

this does not work unfortunately..:

@ApiOkResponse({
  schema: {
    anyOf: refs(
      UserOrderInfoDto,
      null
    ),
  },
})

brandart avatar Jan 25 '22 16:01 brandart

What if my responses are just strings? This doesn't seem to work:

	@ApiBadRequestResponse({
		schema: {
			example: 'Response 1',
			anyOf: refs(
				() => 'Response 1',
				() => 'Response 2',
				() => 'Response 3'
			)
		}
	})

Displee avatar Feb 03 '22 12:02 Displee

Try passing a SchemaObject instead of ReferenceObject

{
    status: HttpStatus.NOT_FOUND,
    schema: {
      anyOf: [
        {
          title: 'Customer',
          description: `The customer couldn't be found in our system, please verify and try again.`,
          example: `The customer couldn't be found in our system, please verify and try again.`,
        },
        {
          title: 'Supplier',
          description: `The supplier couldn't be found in our system, please verify and try again.`,
          example: `The supplier couldn't be found in our system, please verify and try again.`,
        },
        {
          title: 'PartNumbers',
          description: `The information provided couldn't be found in the part numbers master, please verify and try again.`,
          example: `The information provided couldn't be found in the part numbers master, please verify and try again.`,
        },
      ],
    },
  },

imagen

JoshApplauso avatar Nov 11 '22 19:11 JoshApplauso

One solution I try a lot of time to handle. Maybe it can help your guys

export interface IAppError {
  name: string
  code: string
  status: HttpStatus
  message: string
}

export const ApiSchemaRes = (schema: IAppError): Type<IError> => {
  class SchemaResponse implements IError {
    @ApiProperty({ name: 'type', type: 'enum', enum: ErrorType, default: ErrorType.REST })
    type: ErrorType

    @ApiProperty({ name: 'time', type: 'string', default: new Date().toISOString() })
    time: string

    @ApiProperty({ name: 'code', type: 'string', default: schema.code })
    code: string

    @ApiProperty({ name: 'code', type: 'string', default: schema.message })
    message: string
  }
  Object.defineProperty(SchemaResponse, 'name', { writable: true, value: schema.name })
  return SchemaResponse
}

export const ApiFailedRes = (...schemas: IAppError[]) => {
  return applyDecorators(
    ApiResponse({
      status: schemas[0].status,
      content: {
        'application/json': {
          examples: schemas.reduce((list, schema) => {
            list[schema.name] = { value: schema }
            return list
          }, {}),
        },
      },
    }),
  )
}

Then I add this decorator to any controller I want to

ControllerAttachSchemaSwagger

The result will be look like

Swagger Result Response

lytaitruong avatar Sep 30 '23 18:09 lytaitruong