I have a #Post() route that I'm trying to get tested in swagger, but the request doesn't show any kind of data types in the UI. I have another #Post() route that is working and has stuff showing in swagger for the body, with just #Body() user: CreatedUserDto and everything is there.
Here's the one that's working:
#Post('register')
#ApiResponse({
status: 201,
description: 'Successful Registration of User Account.',
type: UserCreatedDto,
})
async registerUser(#Body() user: CreateUserDto): Promise<UserCreatedDto> {....}
and here is the one that is not. I've also tried it without the #ApiImplicitBody() with no luck.
#ApiImplicitBody({
name: 'user',
type: LoginUserDto,
})
#ApiResponse({
status: 200,
description: 'Successful Login!',
type: LoginResponse,
})
#ApiResponse({ status: 400, description: 'Invalid login attempt' })
#UseGuards(AuthGuard('local'))
#Post('login')
async login(
#Request() req,
#Body('user') user: LoginUserDto,
): Promise<LoginResponse> {
try {
return await this.authService.login(req.user);
} catch (e) {
throw e;
}
}
and this is the LoginUserDto
export class LoginUserDto {
#ApiModelProperty()
readonly userName: string;
#ApiModelProperty()
readonly password: string;
}
Related
Hey i'm try to pass data from my browser, which uses React, to my API - which then manages the database. I can only get the API and database to work via Postman because when i Fetch POST the payload, it puts the payload into the name of the response object.
response = '{"username":"lolawdawd","email":"wadwa","password":"xiCm9RDsi8YM6vE"}': ''
This then stops the api from making use of the data. I've tried many different methods and cannot figure this out. My code for Fetch is as follows:
const handleSubmit = async (e) => {
e.preventDefault();
const input = {
username: user,
email: email,
password: pass
}
console.log(input);
try {
await fetch(`http://localhost:8000/api/register`, {
method: "POST",
body: JSON.stringify({
username: user,
email: email,
password: pass
}),
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
}})
navigate('/login');
} catch (err) {
console.log(err.message);
}
};
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 },
},
},
},
})
I have a controller's login() method that can return either 200 OK with JWT token fitted with Session payload or 451 Unavailable due to legal reasons with JWT token fitted with AcceptTerms payload if user trying to log in needs to agree to new version of terms&conditions.
#Nest.Post("login")
#ApiOperation({ title: 'Log in the user given email and password', operationId: 'userLogin'})
#Nest.UseFilters(UnavailableLegalReasonsExceptionFilter)
#ApiResponse({ status: HttpStatus.OK, description: "User was properly logged in", type: JwtToken})
#ApiResponse({ status: 451, description: "User haven't confirmed the latest version of Terms & Conditions", type: JwtToken})
async login(#Nest.Body() body: DTOs.LoginDto): Promise<JwtToken> {
...
}
Now, I'd like to document it with Swagger. JwtToken is defined as:
export class JwtToken {
#ApiModelProperty({enum: TOKEN})
type: TOKEN;
#ApiModelProperty({description: "Number of seconds"})
expiresIn: number;
#ApiModelProperty({ description: "Token", example: "eyJhb ... wWFQ"})
accessToken: string;
}
and TOKEN:
export enum TOKEN {
AcceptTerms = "AcceptTerms",
Session = "Session"
}
But writing that login() returns just a JwtToken is confusing as in case of 200 the type is Session and in case of 451 it is AcceptTerms. The payloads differ significantly too:
export class JwtPayload {
#ApiModelProperty({enum: TOKEN, description: "Token type"})
type: TOKEN;
#ApiModelProperty()
issuedAt: number;
}
export class LoginJwtPayload extends JwtPayload {
#ApiModelProperty()
name: string;
#ApiModelProperty({description: "T&C version accepted"})
tncAccepted: number;
}
export class AcceptTermsJwtPayload extends JwtPayload {
#ApiModelProperty({format: "email"})
email: string;
}
200's token uses LoginJwtPayload payload and 451's uses AcceptTermsJwtPayload.
How to document it using Nest's SwaggerModule?
In a Rails API, I have a login POST method in my UsersController which takes 2 parameters (mail and password) and check in DB if a record is found and if so returns it as JSON.
def login(mail, password)
mail, password = params.values_at(:mail, :password)
user = User.where(mail: mail, password: password)
render json: user
end
In my front side, in React, I call this method with fetch which takes the mail and password values in a form and expect to have the user as JSON in my 'res':
login = () => {
if(this.state.mail != null && this.state.password != null){
fetch('http://127.0.0.1:3001/api/login', {
method: 'post',
body: JSON.stringify({
mail: this.state.mail,
password: this.state.password
}),
headers: {
'Accept': 'application/json',
'Content-type': 'application/json'
}
})
.then((res) => {
console.log(res)
if(res.data.length === 1 ){
const cookies = new Cookies();
cookies.set('mercato-cookie',res.data[0].id,{path: '/'});
this.setState({redirect: true})
}
})
} bodyUsed: false
headers: Headers { }
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://127.0.0.1:3001/api/login"
__proto__: ResponsePrototype { clone: clone(), arrayBuffer: arrayBuffer(), blob: blob(), … } auth.js:32
}
Problem is my res doesn't correspond to what I return with render json: user, so I made a console.log(res) :
Response
bodyUsed: false
headers: Headers { }
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://127.0.0.1:3001/api/login"
__proto__: ResponsePrototype { clone: clone(), arrayBuffer: arrayBuffer(), blob: blob(), … } auth.js:32
I tried returning simple JSON text in case there was a problem with my user variable and also tried changing render json: user to format.json { render json: user } but with no result :/
I made the request on Postman and it returns the appropiate JSON, so i guess the problem comes from my fetch ?
Fetch's response doesn't automatically translate to JSON, you need to call response.json() (which returns a promise) in order to get the JSON value. See this example from MDN, or here's some ES6 to match your code:
fetch(myRequest)
.then(response => response.json())
.then((data) => {
// I'm assuming you'll have direct access to data instead of res.data here,
// depending on how your API is structured
if (data.length === 1) {
const cookies = new Cookies();
cookies.set('mercato-cookie', data[0].id, {path: '/'});
this.setState({redirect: true});
}
});
I need to describe the routes of my application, thus I use swagger:
paths:
/users:
get:
description: Returns all users
responses:
"204":
description: No Content Found
schema:
$ref: "#/definitions/NoContentFound"
definitions:
NoContentFound:
required:
- message
properties:
message:
type: string
I got this error on swagger UI:
Unknown response type
On app.js:
const User = require('../server/models/user');
const sendJsonResponse = function sendJsonResponse(req, res, status, content) {
res.status(status);
res.json(content);
};
app.get('/users/:usersid', (req, res) => {
User
.query()
.where('id', id)
.then(users => {
if(users.length === 0) {
sendJsonResponse(req, res, 204, 'No users Found');
}
});
};
How can I fix that on swagger?
Some responses, such as 204 No Content, have No Body. To indicate the response body is empty, do not specify content for the response:
Reference: Empty Response Body