#Response DTO with Spread Operator

27 messages · Page 1 of 1 (latest)

elder schooner
#

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?

gleaming thicket
celest bone
elder schooner
celest bone
#

Pipes are input-only, aren't they?

gleaming thicket
elder schooner
#

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)

celest bone
#

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.

elder schooner
#

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...

elder schooner
#

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.

celest bone
#

Sounds right, yes.

polar socket
#

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 🙂

celest bone
#

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[];