I have a service that returns an object. In my controller, I create a new instance of the ResponseDTO and map the properties by hand. This works fine, except I noticed that if I use a spread operator - I can make properties that don't exist on the DTO be returned on the response without any validation catching it. Does anyone have an idea of what the problem might be?
#Response DTO with Spread Operator
27 messages · Page 1 of 1 (latest)
this is why a global pipe should be set
options like
- whitelist
- forbidNonWhitelisted
- stopAtFirstError
can do a great help avoiding a lot of issues like these
You can use class-transformer's plainToClass with { excludeExtraneousValues: true}, see https://github.com/typestack/class-transformer?tab=readme-ov-file#enforcing-type-safe-instance.
I don't know about a simple, generic way to create instances of classes without extraneous attributes without such library.
I have the global pipe set with whitelist true and forbidNonWhitelisted but it still returns unwanted properties 😭 I'm not at the PC now, but I can share code when I get back to work.
strange indeed
Pipes are input-only, aren't they?
i even didn't notice that hey was talking about output
Sorry, I was talking about output. Is there no way to strip properties on output?
@celest bone : Should this happen automatically if transformation is set to true with excludeExtraneousValues to true as well?
Or do I need to manually call plainToClass?
With excludeExtraneousValues: true and using plainToClass, with this code:
I get a response that includes the extra properties that are inserted with the spread operator in res.user
(We're trying to make sure that our devs can't accidentally spread sensitive info into the responses, specifically)
I only worked with graphql lately, which does it's magic by itself, so I don't know for sure, or what is the best practice.
I believe you'll have to call plainToClass manually, or setup a decorator combo, maybe combined with interceptors.
For the nested objects I think you'll have to annotate the DTO with @Type.
class ResponseDTO {
@Type(() => UserDTO)
user: UserDTO
}
in this case. Probably with some extra option to plainToClass which instructs it to apply plainToClass to nested attributes/objects.
Also, in the screenshot you're missing the third argument. You'll have to provide it explicitly, it won't use whatever you configured on the global validation pipe, as this is a stand-alone use.
Interestingly, when I add the excludeExtraneousValues to the plainToClass call, I get an empty dto 😮
I have nested objects in my DTO, I wonder if I'm not annotating them correctly with classtransformer...
Ah I needed to add @Expose
So basically what I figured out: no matter how much I want, a spread operator can always break class-transformer's ability to remove properties due to the nature of TypeScript.
So the best thing ot do is use excludeExtraneousValues: true and set individual properties to @Expose in the DTO. This is the only sure way I've found to do what I want to do.
Sounds right, yes.
I use class-transformer’s plainToInstance method. It does exactly what you want
You should pass the third optional argument to that method as { excludeExtraneousValues: true } which will excludr any value that’s not in the dto class
plainToInstance(DtoClass, object, { excludeExtraneousValues: true })
That’s how you call it
Oh you already found the answer
Sorry 🙂
Just to clarify, just checked, and plainToInstance is the new name of plainToClass
/**
* Converts plain (literal) object to class (constructor) object. Also works with arrays.
*
* @deprecated Function name changed, use the `plainToInstance` method instead.
*/
export declare function plainToClass<T, V>(cls: ClassConstructor<T>, plain: V[], options?: ClassTransformOptions): T[];