Separate swagger implementation from controller code - node.js

I'm trying to document my api in NestJS. I have followed NestJS documentation and it works very well but I would like to know if there is anyway to separate the swagger decorators from the controller code. Because the api starts to grow, the controller code starts to get a little bit confusing because having the decorators in between the request methods interferes with the way the flow it is seen.
I have used the decorators but when you need in an endpoint guards validation, pipes it gets pretty big and unfocused, because of the amount of decorators that get added and I am not confused swagger is not that important in the actual execution flow as it is guards, validators, etc.
#Post()
#Roles('user')
#ApiResponse({ status: 201, description: 'The token has been successfully created.'})
#ApiResponse({ status: 403, description: 'Forbidden.'})
#UsePipes(new ValidationPipe())
#HttpCode(200)
async createToken(#Body() createTokenDto: CreateTokenDto) {
this.tokenBuilderService.createToken(createTokenDto);
}

Pretty old question but just pointing it out so people will know.
You can separate using the applyDecorators function imported from #nestjs/common which accepts an array of decorators and composes them for you in a single decorator: https://docs.nestjs.com/custom-decorators#decorator-composition
In your example, you could do something like:
// controller.decorator.ts
export function SwaggerDecorator() {
return applyDecorators(
ApiResponse({ status: 201, description: 'The token has been successfully created.' }),
ApiResponse({ status: 403, description: 'Forbidden.' })
);
}
Note that the decorators are without the # symbol.
and import it in the controller file:
import { SwaggerDecorator } from './controller.decorator'
#Post()
#Roles('user')
#SwaggerDecorator()
#UsePipes(new ValidationPipe())
#HttpCode(200)
async createToken(#Body() createTokenDto: CreateTokenDto) {
this.tokenBuilderService.createToken(createTokenDto);
}
I just faced this issue and it cleared A LOT of swagger code in my controllers.

No.You can't separate the swagger decorators from the controller code.
I usually place it at the end to separate them from pipes and guards:
#Post()
#Roles('user')
#UsePipes(new ValidationPipe())
#HttpCode(201)
#ApiResponse({ status: 201, description: 'The token has been successfully created.'})
#ApiResponse({ status: 403, description: 'Forbidden.'})
async createToken(#Body() createTokenDto: CreateTokenDto) {
this.tokenBuilderService.createToken(createTokenDto);
}

Related

How to use DTOs in nestjs outside of controllers? (messages coming from a queue)

We have a Nestjs app that listens to a queue and gets a message. Whenever we get a message, we use it to send an email. We have a DTO for that:
export class SendEmailDTO {
#Expose()
#IsDefined()
#IsString()
subject: string;
We are using a library aws-sdk where we receive a message anytime there's a message in a queue:
// import { validate } from 'class-validator';
#SqsMessageHandler(process.env.EMAIL_REQUEST_CONSUMER_QUEUE_NAME)
async handleMessage(message: AWS.SQS.Message) {
// here we want to run validations for message using our DTO.
const errors = await validate(message);
if (errors.length !== 0) {}
}
We managed to use the validate method and validate the message received. But we don't like this approach since we are running the validations ourselves.
We would like to have the validation tight to the DTO, to mimic what nestjs does. Something like this:
export class SendEmailDTO {
#Expose()
#IsDefined()
#IsString()
subject: string;
constructor(input: Input) {
const errors = validate(input);
Logger.debug(errors, '### in SendEmailDTO constructor! errors');
}
}
But unfortunately it does not work. We are not sure if this is the right direction.
how can we run the validations within the constructor?
validate returns a promise, we need to wait, but we don't know how to do it within the constructor
are we totally wrong with this direction, and we can not use the validate method within the constructor?. If not, how can we use the DTO outside the nestjs controller world?

How should I create for nestjs response dto?

How should I create the dto for the nestjs response?
I am currently creating the following code.
I want to define it like the dto of input parameters.
■ response code like this
return {
statusCode: 200,
message: 'successs',
data: {
id: 10
}
}
■ I want to do like this
async test: Promise<SuccessDto> {
return respoinse: SuccessDto
}
I believe there is no need to do that. You can access the code through the headers and the convention is the following
200 is ok / success,
201 is created,
204 is no response,
400 is bad request,
401 is unauthorized,
403 is forbidden,
404 is not found
But to answer your question, it might be possible through interceptors. Their sandbox :
https://github.com/nestjs/nest/blob/master/sample/21-serializer/src/app.controller.ts
use the Res, create a DTO with a generic or a class to extends it and glue it together. Use the #Type(() => YourDTO) to expose what you need. But your kind of reinventing the wheel, NestJS takes care of it and you can overwrite the Response status if needed.

Is there a way to get request context within a decorator in Nest JS

I am trying to build a decorator to "log" request info
export const Tracking = () => {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
const method = descriptor.value;
descriptor.value = async function(...args: any[]) {
console.log(/** Request info */)
console.log(/** Headers, Body, Method, URL...*/)
return method.call(this, ...args);
}
}
}
and try to use it on a controller method like this.
export class Controller {
#Get('/path')
#Tracking()
public async getData(#Headers('user') user: User) {
return this.service.getData(user.id);
}
}
If this is impossible, is there a way to apply interceptor to some method of controller?
Or is there a thread(like)-level context for request?
Thanks!!
Decorators don't have access to the request information because of what a decorator is. It's a higher order function that is called to set metadata for the class, class member, class method, or class method parameter. This metadata can be read at runtime, but it is called and set essentially as soon the file is imported. Due to this, there's no way to call a decorator on each request, even Nest's #Body() and #Req() are called at the time of import and read at the time of the request (actually earlier but that's besides the point).
What you're looking for here sounds more like an interceptor, like Micael Levi and hoangdv have already mentioned. The Nest docs show a basic logging example, and there are packages out there like #ogma/nestjs-module (disclaimer: I'm the author) that handle this request logging/tracking for you including the addition of correlation IDs.

Why #Body() in Post request is not working properly? [Nest.js]

I'm starting to learn Nest.js, so I am following an Academind Tutorial (link).
My code is not working as expected when I try to get the body variable with the #Body() decorator in the POST request. Following this part of the code in products.controller.ts
#Post()
async addProduct(#Body() body: Product) {
console.log(body);
const generatedId = this.productService.insertProduct(body.title, body.description, 5.99);
return generatedId;
}
In the terminal the output is just an empty object: {}
I have searched for other examples to look at how to do it properly. I found a tutorial in DigitalOcean where they also use #Body in the POST request; they leave a the end of the tutorial a repo with the example. This example is neither working for me.
I just did a small change in addBook() function on book.service.ts file for returning the new book instead of all books
addBook(book): Promise<any> {
return new Promise(resolve => {
this.books.push(book);
// resolve(this.books);
resolve(book);
});
}
I do the following POST request from Postman but an empty object is being the response.
All other HTTP requests are working just nice, except for the POST one.
Any ideas what could be wrong with the code? Thanks in advance. 😃
You're sending form-data which NestJS does not correctly parse by default. You can use application/x-www-url-form-encoded or application/json along with the raw option in Postman. The JSON body would look like so:
{
"id": "7",
"title": "Whatever Title",
"desscription": "whats doc",
"author": "Me"
}
And then your server will recognize the body properly. The other option would be to add in a body parser that properly parses form-data. There are several options out there like multer, form-parser, formidable, and others.
i also faced the same issue, first i tried to send the data as raw format but it didn't worked properly, then i used x-www-urlencoded tab on postman and it solved the issue for me.
Edit: i had a typo in raw format, now it's working fine!
Remove the type (Product) for body works for me.
Not related to the example but related to the title.
If you use ValidationPipe with whitelist=true on your app.
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
whitelist — removes any property of query that is not part of DTO.
And do not use decorators from the 'class-validator' on your DTO object
import { IsNotEmpty, MaxLength } from 'class-validator';
export class Cat {
#MaxLength(200)
#IsNotEmpty()
name: string;
age: number; // would be removed by ValidationPipe when whitelist=true
}
The properties would be removed. And you will get the empty object.

Automatic Validation of Swagger API Request In Express?

Excuse me if this question seems obvious, but I am new to Express, Node, and Swagger.
I have written a Swagger specification for an API.
Are there tools in which you can pass a request to a Swagger documented API along with the Swagger.json file to validate that the required parameters are there, that the enum values for those parameters are correct, etc.?
Something akin to:
validator.validate ("./swagger.json", req, function (req, res, err) {
if (err) {
res.status('400').send(err.message());
}
else {
// Call my controller
swaggerController(req, res);
}
});
I believe there is, but it's difficult to find or I'm not looking for the correct thing.
Yes, you can do this.
There is a generator project that does exactly this, express-no-stress. Get it here:
When using it, each API request will be validated against your Swagger API description, provided by you, in Api.yaml.
For example, to validate the body of a POST request to /examples, you can do the following:
Edit Api.yaml
...
definitions:
# define the example body i.e. require the property name
ExampleBody:
type: object
title: example
required:
- name
properties:
name:
type: string
description: The example name
paths:
# define your /examples POST endpoint
# reference the ExamplesBody definition from above
/examples:
post:
tags:
- Examples
description: Create a new example
parameters:
- name: example
in: body
description: number of items to skip
required: true
schema:
$ref: "#/definitions/ExampleBody"
responses:
200:
description: Returns all examples
...
Next, in the Node.js code create a route handler for POSTs to /examples
e.g.
app.post('/examples', function (req, res) {
/* your handler logic here */
/* no need to validate the request payload. it will be done automatically */
});
Note: include only your handler logic. Body validation will be handled automatically.
Here's an example of middleware for validating incoming responses against swagger json-schema. Just a proof of concept but it may point you in the right direction:
swagger-json-schema-middleware-example

Resources