Using 'excludeExtraneousValues' NestJS flag is not working - nestjs

I have a DTO class of a user in NestJS.
I am using many validations using class-validator package in order to enforce my logic.
If a field that doesn't exists on the DTO definition, I would like to ignore it and even throw an error.
This is why I was trying to use the 'excludeExtraneousValues' flag.
When I do use it, it ignores all the fields, even the ones that defined in the DTO.
import { ApiPropertyOptional } from '#nestjs/swagger';
import {
IsDefined,
IsEmail,
IsOptional,
IsPhoneNumber,
MaxLength,
ValidateIf,
} from 'class-validator';
export default class UserDTO {
#ApiPropertyOptional()
#MaxLength(254)
#IsEmail()
#IsDefined()
#ValidateIf((object) => object.email || !object.phone_number)
email?: string;
#ApiPropertyOptional()
#MaxLength(15)
#IsPhoneNumber()
#IsDefined()
#ValidateIf((object) => object.phone_number || !object.email)
phone_number?: string;
#ApiPropertyOptional()
#IsOptional()
#MaxLength(40)
name?: string;
}
As I mentioned, I am using NestJS.
This is the ValidationPipe definition:
app.useGlobalPipes(
new ValidationPipe({
transform: true,
stopAtFirstError: true,
transformOptions: { excludeExtraneousValues: true },
}),
);
Following the addition of 'excludeExtraneousValues' flag, I cannot send any value, even the ones that is defined.
Is it a bug or am I missing something?

A bit old but I stumbled across and can see it was upvoted, so here goes:
I am wondering if you are mixed up on input vs. output. Your question isn't quite specific enough for me to be 100% sure. NestJS apps often make use of the class-validator and class-transformer libraries on both ends of the equation and can work on DTO's/entities that are dressed up with decorators from these libraries.
Pipes such as your ValidationPipe are geared to the input side of things. Refer to the docs: https://docs.nestjs.com/techniques/validation.
Generally class-validator is the key player on the input side, with class-transformer only playing a role with applicable decorators like #Transform(). To make use of class-transformer, you need to pass the transform: true option as you do.
If you want to control behaviours regarding data fields coming into your ValidationPipe that are not defined in your entity/DTO classes, take a look at the whitelist: boolean, forbidNonWhitelisted: boolean, and forbidUnknownValues: true configuration options to satisfy your needs on the input side of things.
I have a feeling that once you check these options out, you will find that you will want to delete the transformOptions: { excludeExtraneousValues: true } option.
On the output side of things is where interceptors come into play. Often in NestJS projects the included ClassSerializerInterceptor is employed to serialize instances of DTO/entity classes to JSON for a response. Docs: https://docs.nestjs.com/techniques/serialization
This side is where you see more frequent use of class-transformer's decorators such as Exclude(), Expose(), and Transform().
This is also where you are probably more likely to find a use-case for a configuration option like excludeExtraneousValues.
Suppose you had data in an object (e.g. from a database, file, or wherever) and wanted to populate a class (such as a DTO) so you could take advantage of these decorators, but only wanted to have certain fields actually sent back in the response, i.e. the ones that you explicitly specified and decorated in your class, and you didn't want to bother with Exclude() and Expose() decorators all over the place.
In such a case, the options for class-transformer may come in handy depending on what you want to do, e.g.
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector), { excludeExtraneousValues: true }))

Related

NestJS DTO Class-Validator accepts "internal" property names instead of exposed one via Class-Transformer #Expose

I am using Class-Transformer and Class-Validator in combination in a NestJS project.
The NestJS project uses a global Validation Pipe with the following options:
{
whitelist: true,
forbidNonWhitelisted: true,
forbidUnknownValues: true,
transform: true
};
One key point here is to allow only white-listed values and disallow any unknown.
Generally, this all works fine except for a weird edge case involving the #Expose decorator, I just can't figure out how to solve ...
Here is a test DTO. What I want is to expose an internal property "_key" as "id" but forbid the internal value being used.
export class TestDTO {
#Expose({ name: 'id' })
readonly _key?: string;
}
If a user makes a request with "id", everything works perfectly. Also, if a user submits an unknown property name like "id2", class-validator complains, which again as it should be.
But if a user uses the internal name (_key) in the request, class-validator does NOT complain. In addition, the DTO is empty because "id" was expected instead of "_key", so the value of "_key" was not used in constructing the DTO, I think.
The question now is, how can I tell class-validator to consider exposed property names as valid and treat the "internal/transformed" names as invalid / unknown?

how to extend Request type in Express with custom property

I am trying to set up a custom middleware function in Node.js with typescript, where I want to store a decoded json web token into a custom request property 'user' like this
function auth(req: Request, res: Response, next: NextFunction) {
const decodedToken = jwt.verify(token, config.get("secret"));
req.user = decodedToken.user;
next();
}
but I keep getting a compiler error in the terminal
error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs>'.
I found several answers on here that suggested creating a custom.d.ts file and extend the Request type which I did like this:
namespace Express {
interface Request {
user?: string;
}
}
now, the VS Code is happy, I get no linting, I get the correct intellisense and autocompletion and it does not let me assign a number, because it understands it needs to be a string. So it obviously works. However, when I run the code the terminal keeps giving me the same error and insists that property 'user' does not exist.
I tried restarting the TS server but that did not help.
I can make it work by creating a custom interface which extends the Request type and then using that, but I really want to know how to do it with the original Request type.
There's no way to do it on the original object with typescript - that's coming from Express and you're importing it from Express. The best you can do there is extend it like you have, or ignore the errors.
You definitely can do it in theory - you can add whatever you want to the req object in a middleware because it's just that - a javascript object. The warning you're getting is from typescript, not Express.
In practice, what you'd normally do is use res.locals for something like this. In typescript, it's defined as locals: Record<string, any>. So you could set res.locals.user in your middleware and then access it later wherever you want.
I'm not sure if I'm understanding this quite right, but, I think that the best solution is to create a custom Request class, adding this property.
export class CRequest extends Request {
user?: string
//add stuff to constructor etc
}
Now, you want to make it an interface, just replace class with interface
I also do not reccomend you use the same name as Express & Request
I managed to make it work after a lot of googling and playing with folder structure and tsconfig.json file. I put the custom.d.ts file in ./#types/express (the sub-folder has to be there) and set the typeRoots in tsconfig.json like this:
compilerOptions: {
"typeRoots": ["./#types"],
}
I also had to declare the namespace in custom.d.ts
declare namespace Express {
interface Request {
user: any;
}
}
Now it compiles without error

Optional parameter in NestJS Swagger

For an API server, I have a set of models, e.g.
Recording <-> Artist
with ManyToMany relation in TypeORM. The relation needs to be defined in both sides models.
In certain routes I am displaying only Recording and in certain routes also Recordings with Artists, using leftJoinAndSelect. E.g. /api/artists and /api/artists_with_recording.
However, the generated documentation always shows Recordings in Artists.
Is there some easy way to modify the swagger output?
I could make different model objects with swagger markup but with more objects in more contexts that could become pretty messy.
After more searching, I found a solution. I can use the OmitType function from NestJS to create simple ad hoc classes for the documentation.
https://docs.nestjs.com/openapi/mapped-types#omit
So for the route /api/artists I do
#Entity()
class Artist {
...
}
class ArtistWithoutRecording extends OmitType(Artist, ['recording'] as const)
In the controller documentation, I include the ArtistWithoutRecording type.
#Get('artists')
#ApiOperation({
summary: 'Get all artists',
description: 'Some description'
})
#ApiOkResponse({ type: ArtistWithoutRecording })
async getArtists() {
return this.artistDao.findMany()
}

Nest.js injection of model?

I have the follwoing code from tutorial|:
constructor(#InjectModel('User') private readonly userModel: Model<User>) {}
Where User is:
export interface User extends Document {
readonly name: string;
readonly age: number;
readonly phone: string;
}
Could you explain how #InjectModel works, what is 'User' and why we passed Model<User>, what does it mean?
What I can inject also using #InjectModel?
All right, to get into this, first we have to take to truth that interfaces do not exist at runtime. So the User interface you have is only useful during development. I'll try to break this down step by step, starting from the end of the line and working backwards.
Model<User>: Model is an interface type exposed by mongoose that allows us to know that the model we're using has methods like find and create. By saying Model<User> we are saying "This is a mongoose model object that refers to the User interface. This is especially useful for Typescript because as the functions are typed with generics, it knows what the return of methods like find are: an array of User objects. The model interface is really Model<T> where T is the interface that extends Document (another mongoose type).
What is 'User': 'User' is the string equivalent of the name of the interface. If your interface that extends Document is called Dog you use 'Dog', if it's Animal you use 'Animal'. The reason for not passing the interface is because interfaces do not exist at runtime (unlike classes).
How does #InjectModel() work: Okay, the really fun part of the question to answer. Nest works normally by using Injection Tokens. Normally, these tokens are determined by the type of the injected value. In your case Model<User>. Now, the problem here is that A) interfaces don't exist at runtime and B) Typescript does not reflect generics well, so even if Model was a class, all that could be gotten would be Model which isn't enough information on what to inject. So the next logical step Nest takes is to allow a user to provide injection tokens and use the #Inject() decorator. You can do things like injecting an object this way (like package configuration information). Useful, but a bit hard to work with without building your own providers. Now steps in #InjectModel(). #InjectModel() builds an injection token based on the string that's passed into the function. This token is something along the lines of typeModel where type is actually what you pass into the function. This tells Nest specifically what model we are injecting. This also needs to align with the provider created with MongooseModule.forFeature(), hence why name and the value passed to #InjectModel() need to be aligned. Usually it's easiest to align when they use the same string name as the interface.

Take JSON response and transform it to pass around application

In my NestJS app, I'm making a REST request to a remote API I do not have control over. The REST API has a response containing JSON, a large object, most of which I do not need. Let's assume hypothetically that we have a JSON object that looks like the following:
{
"foo": [
1,
2,
3
],
"bar": {
"nested": {
"some_key": "some_val"
}
}
}
What if in this case, after I make a request to this API, I want to pass around only a subset of the above. Something like a NestedDto that would look like the following:
import { IsNotEmpty, IsString } from 'class-validator'
export class NestedDto {
#IsNotEmpty()
#IsString()
someKey: string
}
What is the best way for me to take the data that's being returned from the REST API and transform it into the above-using tools that NestJS offers? I want to be able to take responses from remote APIs and pass said data around inside of my NestJS app using my interface specifications.
All right, if you're just using Axios with no special configuration, you could do what you are already doping in Express by just mapping the response down to what you want. If you want to get a little fancy with it you could always implement some sort of class decorated with class-transformer decorators and use a plainToClass method mixed with the #Transform() decorator.
By the way, by default, NestJS provides an HttpModule that is a wrapper around Axios, but its responses are as RxJS Observables. If you decide to go with the observable route, you can use the observable operator map to do the mapping for you (you'll still have to provide mapping implementation though, like the plainToClass I mentioned above), but that is completely up to you.

Resources