I am using NestJs to create an API, I currently have an endpoint post that receives parameters, processes it, and responds. The problem I have is that I want to change the name of a parameter, but only at the Json level, I tried with the Expose decorator, but it changes the attribute throughout the project.
tokens.controllers.ts
#Post()
async create(#Body() createTokenDto: CreateTokenDto) {
return await this.tokensService.create(createTokenDto);
}
create-token.dto.ts
export class CreateTokenDto {
#IsString()
#IsNotEmpty()
#Length(36)
#Expose({ name: 'product_uuid' })
productUuid: string;
}
The problem with this, is that now the CreateTokenDto object has the value productUuid is always null, is there any way to change the attribute only to the json it receives?
Related
I have a controller with a POST operation to save ReasonCode. Here is what I have in my controller:
import {
CustomAuthGuard,
ReasonCodeService,
ReasonCode,
} from 'cnv-ms-core';
export class ReasonCodeController {
constructor(private readonly reasonCodeService: ReasonCodeService) {}
}
#Post('reason-codes')
#ApiOperation({ summary: 'Save Reason Code' })
#ApiBody({
//type: ReasonCode,
description: 'Reason Code',
required: true,
isArray: false,
})
#ApiResponse({
status: 201,
description: 'Reason Code is Saved Successfully.'
})
async saveReasonCode(
#Body() newReasonCode: ReasonCode,
): Promise<ReasonCode | null> {
return this.reasonCodeService.saveReasonCode(newReasonCode);
}
Here is my interface object:
export interface ReasonCode {
name: string;
description: string;
type: ReasonCodeTypeEnum;
isVisible: boolean;
createdAt?: Date;
updatedAt?: Date;
}
As you can see in the above snippet of my controller, I commented out the 'type' in the '#ApiBody' decorator. I wanted to add this, but when I uncomment I see the error 'ReasonCode only refers to a type, but is being used as a value here' and vs code offers the quick fix to add ReasonCode to imports. But, I already have ReasonCode in imports. How can I add this in the #ApiBody to see it in swagger-ui.
Thanks for the help.
Instead of using an interface to add a type to the body you should use a class to do that and then just add the #ApiProperty() decorator to each property of the class, it's a cleaner solution because you can avoid the #ApiBody() Decorator.
Also, when using a class you can take advantage of the class validator decorators to validate the body before using it.
ApiProperty is exported from #nestjs/swagger
More info here: https://docs.nestjs.com/openapi/types-and-parameters#types-and-parameters
Also check this info about class-validator: https://docs.nestjs.com/pipes#class-validator
changing export interface ReasonCode to export class ReasonCode might solve this
I have a backend server written in typescript on node.js using nest.js as framework.
I want to automatically sanitize the response body of my controllers removing undeclared properties of my DTOs.
I have the following files:
class User {
_id: string
name: string
hashedPassword: string
}
class UserDTO {
_id: string
name: string
}
#ApiTags('users')
#Controller('users')
export class UsersController {
...
#Get(':id')
#UseGuards(JwtAuthGuard)
#ApiBearerAuth()
#ApiOperation({ summary: 'Find one user by id' })
#ApiResponse({
status: 200,
type: UserDto,
})
async findOne(#Param('id') id: string): Promise<UserDto> {
return await this.usersService.findById(id) as UserDto;
}
}
When I declare user as UserDTO I thought that will remove all undeclared properties as UserDTO (in this case the 'hashedPassword') that's not what happened, it still sending the 'hashedPassword', I want to remove it automatically without calling constructores or removing it manually using const { hashedPassword, ...result } = user; return result; on e each service.
It's a pain do this conversions every time I have to send a DTO. I'm looking for something that do this conversions automatically. Does exists something in typescript or in Nest.js that do this conversions automatically for me? Maybe some decorator in the DTO or calling some middleware?
In older projects I used to do this to automatically remove unwanted properties:
const UserSchema = new Schema({
hashedPassword: String,
...
}, {
timestamps: true,
toJSON: {
transform: (doc, ret, options) => {
delete ret.hashedPassword;
return ret;
},
virtuals: false,
}
});
Today I consider this a bad implementation because I adding business logic to my repository. I'm looking for something that sanitize my DTOs automatically. Is that possible?
Sounds like you might be looking for Serialization, which Nest has a setup with an interceptor and class-transformer calling classToPlain() to serialize the response. It sounds like you might be working with Mongo, so keep in mind you may need to do a bit extra in your services to get a true class instance that class-transformer can work with.
As for why your above attempt didn't work, as mentioned by jonrsharpe in the comments, type information doesn't exist at runtime. It's purely there for devs during development and compile time to help us catch bugs that could be easily avoided. Also, to take it a step further x as y tells the compiler "I know that x is of type y, whether you can read that from the code I've written or not". It's essentially telling the compiler you know more than it, so it shouldn't worry.
Disclaimer: My aspiration has obvious and good alternatives, but from what little I know about Nestjs I would not be surprised if supported the following "pattern" somehow.
Given these 2 DTOs:
export class AddExistingMemberDto {
#IsInt()
memberId: number;
}
export class AddNonExistingMemberDto {
#IsString()
username: string;
#IsEmail()
email: string;
}
Can Nestjs check which DTP the request body fulfils, and switch between different controller methods?
I tried:
#Controller('group')
export class GroupController {
#Patch(':id/members/add')
addExistingMember(
#Body() addExistingMemberDto: AddExistingMemberDto,
) {
console.log('Existing', addExistingMemberDt);
}
#Patch(':id/members/add')
addNonExistingMember(
#Body() addNonExistingMemberDto: AddNonExistingMemberDto,
) {
console.log('Non-existing', addNonExistingMemberDto);
}
}
But this always invokes with the first method.
Rationale: Having one controller method that checks the DTO is a fine alternative, but it does require me to merge my two different DTO classes. So I was just curious.
I have requirement to accept type custom JSON from front end and I’m trying to achieve this in NestJS but I’m unable to resolve.
My entity model
import { GraphQLJSONObject } from “graphql-type-json”
export class User {
#Field(()=> GraphQLJSONObject)
#Column({name: meta_data, type: simple-json})
metaData: object
}
DTO class:
export class CreateUserDto {
#Field()
readonly metaData: object;
}
I’m keep getting error
Error: undefined type error. Make sure you are providing an explicit type for the metaData of CreateUserDto class
I’m new to Nestjs and Graphql.
Your usage of graphql-type-json was correct. The error happens because Nest.js can't infer the type of the CreateUserDto.metadata field, You need to use a similar annotation.
#Field(() => GraphQLJSONObject)
readonly metaData: object;
For reference, such error can happen when you use an #Args field too (say in your mutation)
#Mutation((returns) => MyResultSchema)
async createUser(#Args('user', { type: () => GraphQLJSONObject }) user: object)
There's indeed some repetition, but that's because some type information is lost after Typescript is transpiled.
We are using https://github.com/MichalLytek/type-graphql to define our graphql schema When we serialize the raw typescript entity object this doesn't respect the various field annotations in our GQL entities and ends up leaking unwanted data. Example below a Profile entity class
import { Field, Int, ObjectType } from 'type-graphql'
import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'
import { Account } from '../account/account.entity'
export class Profile {
#Field()
#Column({ unique: true })
public username: string
#Field()
#Column()
public name: string
// Relations
#Column()
public accountId: string
#ManyToOne(type => Account, account => account.profiles, { eager: true })
public account: Account
}
account has sensitive data. When we JSON.stringify a Profile reference we don't want account output. Account is not annotated with #Field and we expect it would not be output.
The decorators used by type-graphql only exist to instruct type-graphql how to translate your class to a GraphQL type -- they are not going to somehow impact how an instance of the class is serialized by a native function like JSON.stringify.
In the context of your schema, the account won't ever be returned in the response unless you explicitly create a field for it, even if the Profile instance used by your resolvers has that property. This is a symptom of how field resolution works in GraphQL.js. However, a Profile instance will always have an account property on it because that's what you've defined as part of your class.
It's unclear from your question why you're calling stringify in the first place, but assuming it's to use in some other context, like logging, then you'll want to expose your own method for serializing the instance that limits which properties are returned. This can be done easily using something like lodash's pick or omit.
serialize () {
return _.omit(this, ['account'])
}