class-validator unexpectedly triggers validation using createQueryBuilder

Viewed 1790

I'm using Typeorm in conjuction with class-validator, so I have defined an entity like this:

import {
    Entity,
    PrimaryGeneratedColumn,
    Column,
    BaseEntity,
    BeforeInsert,
    BeforeUpdate,
    getRepository
} from "typeorm";
import {
    validateOrReject,
    IsDefined,
} from "class-validator";
import errors from 'restify-errors';

@Entity()
export class License extends BaseEntity {
    @PrimaryGeneratedColumn('uuid')
    public id!: string;

    @Column({ nullable: false })
    @IsDefined({ message: 'name field was not provided' })
    public name!: string;

    @Column({ nullable: false })
    @IsDefined({ message: 'description field was not provided' })
    public description!: string;

    @BeforeInsert()
    @BeforeUpdate()
    async validate() {
        await validateOrReject(this, { skipUndefinedProperties: true });

        // Check if license already exists
        if ((await getRepository(License).count({ name: this.name }) > 0))
            throw new errors.BadRequestError(`There is already a license with name of ${this.name}`);
    }
}

I also defined a middleware that runs a getMany to retrieve data from Entity<T> repository, eg:

export default (entity: any) => async (req: Request, res: Response, next: Next) => {
    const repository = getRepository(entity);
    const query = repository.createQueryBuilder(`${entity}`);

    // Get query params
    for (const propName in req.params) {
        if (req.params.hasOwnProperty(propName)) {
            query.where(`${propName} = :param`, { param: req.params[propName] });
        }
    }

    // Pagination
    const page = parseInt(req.query.page, 10) || 1;
    const limit = parseInt(req.query.limit, 10) || 25;
    const startIndex = (page - 1) * limit;
    const endIndex = page * limit;

    const [result, total] = await query
        .skip(startIndex)
        .take(endIndex)
        .getManyAndCount();

    res.send({
        "success": true,
        "data": result,
        "count": total,
        "message": null
    });

    next();
};

when I run the middleware, the function async validate() is also triggered, but it shouldn't because I'm getting data not inserting or updating them. Infact, when the validate method is triggered, Typeorm generate this error:

"QueryFailedError: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'is already a license with name of ${this.name});\r\n });\r\n }\r\n}.id A' at line 12"

If I comment the function validate, the data are correctly returned from the middleware.

What is going on? Is this a bug of the library?

1 Answers

It seems that your middleware is using the following message in your query: There is already a license with name of ${this.name}. Without further indication on how you use your middleware with restify (server.use statement) it is difficult to say how and why this error message payload end in your middleware query but you should check in this direction.

On the other hand, if you need to ensure license name's uniqueness you may want to declare a unique key on name with the following notation instead of using manual validation:

@Column({ nullable: false, unique: true })
public name!: string;

Please find more information in the typeorm documentation: https://github.com/typeorm/typeorm/blob/master/docs/decorator-reference.md#column

Related