How can I display multiple ResponseDTOs' schemas in Swagger/NestJS? - swagger

I have this route which can return one of these two different DTOs:
#Get()
#ApiQuery({ name: 'legacy', description: "'Y' to get houses legacy" })
async findAllHouses(
#Query('legacy') legacy: string,
): Promise<HousesDto[] | HousesLegacyDto[]> {
...
}
I want to display both of these ResponseDTOs in swagger.
I've tried this decorator:
#ApiOkResponse({
schema: { oneOf: refs(HousesDto, HousesLegacyDto) },
})
// OR
#ApiOkResponse({
schema: {
oneOf: [
{ $ref: getSchemaPath(HousesDto) },
{ $ref: getSchemaPath(HousesLegacyDto) },
],
},
})
with #ApiExtraModels() on top of DTO classes and #ApiProperty() on each properties.
But I still get empty objects in Swagger and I suppose it would not have even taken array types in consideration.
How can I display both of these schemas properly?

Seems to me like a lot of very obscure solutions have been posted here and there, so I will try to clarify what needs to be done.
You have two DTOs:
export class SomeStatusDto {
#ApiProperty({
description: 'Id',
example: 1,
})
#IsNumber()
id: number;
#ApiProperty({
description: 'Status',
example: 'in_progress',
})
#IsString()
status: string;
}
export class ErrorStatusDto {
#ApiProperty({
description: 'Id',
example: 1,
})
#IsNumber()
id: number;
#ApiProperty({
description: 'Error',
example: 'Some error string',
})
#IsString()
error: string;
}
Then you have your controller:
#UseGuards(AccountTypesGuard)
#ApiOperation({ summary: 'Get status of...' })
#Get('status')
#ApiExtraModels(SomeStatusDto, ErrorStatusDto)
#ApiOkResponse({
schema: { anyOf: refs(SomeStatusDto, ErrorStatusDto) },
})
async getPullStatus(
#Request() req,
#Param('id', ParseIntPipe) someId: number,
): Promise<SomeStatusDto | ErrorStatusDto> {
// check if someId belongs to user
const idBelongsToUser = await this.myService.validateSomeId(
req.user.id,
someId,
);
if (!idBelongsToUser) {
throw new ForbiddenException(
`SomeId does not belong to user (someId=${someId}, userId=${req.user.id})`,
);
}
const key = `status-${someId}`;
const response = await this.redisService.getByKey(key);
return response ? response : {};
}
Note the solution below. You need to reference the DTOs as #ApiExtraModels() and then you can add them as anyOf: refs(...) in your schema.
#ApiExtraModels(SomeStatusDto, ErrorStatusDto)
#ApiOkResponse({
schema: { anyOf: refs(SomeStatusDto, ErrorStatusDto) },
})
Hope this helps somebody :)

so I encountered a similar issue and this is how you could get the output shown in the image above.
Using the #ApiResponse decorator you could set the two responses using the examples property, try the code sample below
#ApiResponse({
status: 200,
description: 'Successful response',
content: {
'application/json': {
examples: {
HousesDto: { value: HousesDto },
HousesLegacyDto: { value: HousesLegacyDto },
},
},
},
})

Related

Create Swagger Document from a Schema - using swagger-ui-express

I'm using joi-to-swagger and swagger-ui-express and I'm really struggling to find out how to get this schema to show up correctly. It's getting ignored in all scenarios and I can't find the documentation I need to correctly use the schema.
My file:
import j2s from 'joi-to-swagger'
import joi from 'joi'
import swaggerUi from 'swagger-ui-express'
const myJoiSchema = joi
.object({
text: joi.string().max(100).required(),
})
.required()
const schema = j2s(myJoiSchema).swagger
const swaggerDoc = {
swagger: '2.0',
info: {
title: 'title',
version: '1.0',
},
paths: {
'/my-url': {
post: {
summary: 'my api',
// consumes: 'application/json',
// parameters: mySchema, didn't work
requestBody: {
required: true,
schema: {
$ref: '#/components/schemas/mySchema',
},
},
},
},
},
components: {
schemas: {
mySchema: schema,
},
},
}
router.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc))
the schema that is produced for value schema is:
{
type: 'object',
properties: {
text: { type: 'string', maxLength: 100 },
},
required: [ 'text' ],
additionalProperties: false
}
How is the schema supposed to be properly used in the swagger doc? It might be special with swagger-ui-express but I can't confirm I've tested it correctly.

nest+swagger apibody not displayed

I'm using nest and swagger and I have a question.
I wrote the code as below. But in swagger nothing render in request body.
Is it a picktype problem?
Please tell me how to solve it.
thank you
#ApiOperation({ summary: 'about payment' })
#ApiBody({ type: PaymentDto })
#ApiResponse({
type: PaymentResponseDto,
})
#Patch('payment')
async payment(
#Body() paymentDto: PaymentDto,
)
export class PaymentDto extends PickType(Renewal, [
'renewalMethod',
'renewalPrice',
]) {}
#Entity()
export class Renewal{
#ApiProperty({
example: '9,900',
description: 'price',
})
#IsNumber()
#Column({ default: 0 })
renewalPrice: number;
#ApiProperty({
example: 'subs',
description: 'type',
})
#IsEnum(RenewalType)
#Column({ type: 'enum', enum: RenewalType })
renewalMethod: RenewalType;
}
result browser Image

build fastify-swagger scheme for multiple response data with a single 200 code

I use fastify-swagger (https://github.com/fastify/fastify-swagger) for my fastify server. I also use the JSEND (https://github.com/omniti-labs/jsend) standard, which means that the data returned may be different with the same response code 200.
I do not understand how I can build a scheme for multiple response data with a single 200 code.
The Open Api 3 specification (which fastify-swagger supports) seems to allow you to describe this (https://swagger.io/specification /)
I tried this, but it doesn't work. The response from the server is simply not validated and comes as is.
const chema_1 = {
description: 'description',
type: 'object',
properties: {
status: {
type: 'string',
description: 'string'
},
}
}
const chema_2 = {
description: 'description',
type: 'object',
properties: {
body: {
type: 'string',
description: 'number'
},
}
}
fastify.route({
method: 'GET',
url: '/coursesList',
schema: {
response: {
'200': {
oneOf: [
chema_1,
chema_2,
]
}
}
},
handler: async (request, reply) => {
// handler
}
})
I also tried this. Same behavior:
const chema_1 = {
response: {
'200': {
description: 'description',
type: 'object',
properties: {
status: {
type: 'string',
description: 'string'
},
}
}
}
}
const chema_2 = {
response: {
'200': {
description: 'description',
type: 'object',
properties: {
body: {
type: 'string',
description: 'number'
},
}
}
}
}
fastify.route({
method: 'GET',
url: '/coursesList',
schema: {
oneOf: [
chema_1,
chema_2,
]
},
handler: async (request, reply) => {
// handler
}
})
There are no such examples in the documentation. Maybe someone can help me?

Loopback POST array of entry?

I want to insert 10 entries with one query against 10 queries.
I read that it's possible to do it by sending an array like this :
But I get this error:
Do I need to set something? I don't know what to do at all.
Repo with a sample : https://github.com/mathias22osterhagen22/loopback-array-post-sample
Edit:
people-model.ts:
import {Entity, model, property} from '#loopback/repository';
#model()
export class People extends Entity {
#property({
type: 'number',
id: true,
generated: true,
})
id?: number;
#property({
type: 'string',
required: true,
})
name: string;
constructor(data?: Partial<People>) {
super(data);
}
}
export interface PeopleRelations {
// describe navigational properties here
}
export type PeopleWithRelations = People & PeopleRelations;
The problem with your code was :
"name": "ValidationError", "message": "The People instance is not
valid. Details: 0 is not defined in the model (value: undefined);
1 is not defined in the model (value: undefined); name can't be
blank (value: undefined).",
Here in above as in your #requestBody schema, you are applying to insert a single object property, where as in your body are sending the array of [people] object.
As you can see in your people.model.ts you have declared property name to be required, so system finds for the property "name", which obviously not available in the given array of object as primary node.
As you are passing index array, so its obvious error that you don't have any property named 0 or 1, so it throws error.
The below is the code hat you should apply to get insert the multiple, items of the type.
#post('/peoples', {
responses: {
'200': {
description: 'People model instance',
content: {
'application/json': {
schema: getModelSchemaRef(People)
}
},
},
},
})
async create(
#requestBody({
content: {
'application/json': {
schema: {
type: 'array',
items: getModelSchemaRef(People, {
title: 'NewPeople',
exclude: ['id'],
}),
}
},
},
})
people: [Omit<People, 'id'>]
): Promise<{}> {
people.forEach(item => this.peopleRepository.create(item))
return people;
}
You can also use this below
Promise<People[]> {
return await this.peopleRepository.createAll(people)
}
You can pass the array of your people model by modifying the request body.If you need more help you can leave comment.
I think you have a clear solution now. "Happy Loopbacking :)"

Exporting Swagger/OpenAPI to Postman collection with examples

I have a large codebase in NestJS for which I have used Swagger to document the API routes ("#nestjs/swagger": "^3.1.0"). However, the Swagger UI seems to be underpowered for such large projects and I would like to migrate to Postman. Here is what happens:
Take for example this model structure:
export class IApiGenericRequest {
#ApiModelProperty({
example: null,
default: 0
})
appVersionCode: number;
#ApiModelProperty({
example: false,
default: false
})
testerMode: boolean;
#ApiModelProperty({
example: 1,
default: 1
})
appId: number;
}
export class IApiGenericRequestUserId extends IApiGenericRequest {
#ApiModelProperty({
example: 1,
default: 1
})
userId: number;
}
class IApiEventsV2UserDetails extends IApiGenericRequestUserId {
#ApiModelProperty({
example: 1
})
eventId: number;
#ApiModelProperty({
example: false
})
loadMap: boolean;
}
Example route:
#Post('details')
#ApiResponse({
status: 200,
description: 'Get event user details',
type: null
})
async details(#Res() res: Response, #Body() body: IApiEventsV2UserDetails) {
// call service
}
In Swagger UI the request example is shown properly:
{
"appVersionCode": null,
"testerMode": false,
"appId": 1,
"userId": 25,
"eventId": 5,
"loadMap": false
}
When imported in Postman it looks like this:
{
"appVersionCode": "<number>",
"testerMode": "<boolean>",
"appId": 1,
"userId": 25,
"eventId": "<number>",
"loadMap": "<boolean>"
}
I can't figure out what happens here and how can it be fixed.

Resources