Search code examples
postgresqlnestjstypeorm

TypeORM conditional nullable?


I am developing a log in system in NestJS using TypeORM and Postgres. In my login options, I propose the user to use a couple email / password or to use an OAuth authentication. But once an account is created, they are not mutually exclusive (a user can have an email + password and a Google account attached to his account, for example).

Therefore, I would like to make the password OR the OAuthLogin nullable, but at least one of them should never be nullable.

Is it possible to achieve this with TypeORM ?

@Entity()
class User {
    @PrimaryGeneratedColumn()
    public id: number;

    @Column({ unique: true })
    public email: string;

    @Column({ nullable: true })
    @Exclude()
    public password?: string;

    @JoinColumn()
    @OneToMany(() => OAuthLogin, (provider: OAuthLogin) => provider.user, {
        cascade: true,
    })
    public oAuthLogins: OAuthLogin[];
}

export default User;

(P. S.: for my current code, I chose to make the password only nullable...)


Solution

  • The short answer is no, not at the TypeORM level. However, you can achieve this in your application code using the ValidateIf decorator from class-validator:

    @Column({ nullable: true })
    @Exclude()
    @IsNotEmpty()
    @ValidateIf(u => !u.oAuthLogins || u.oAuthLogins.length === 0)
    public password?: string;
    
    @JoinColumn()
    @IsArray()
    @ValidateIf(u => !u.password)
    @OneToMany(() => OAuthLogin, (provider: OAuthLogin) => provider.user, {
    cascade: true,
    })
    public oAuthLogins?: OAuthLogin[];
    

    Elsewhere in your application:

    import { validate } from 'class-validator';
    ...
    validate(user)
    

    If this entity is crossing a controller, you can also use NestJS's ValidationPipe to enforce this at the controller or application level:

    // main.ts
    app.useGlobalPipes(new ValidationPipe({ whitelist: true }));