Defining many relations in typeorm for private entity properties

487 views Asked by At

I'm using typeorm library for entity mapping in my node app. I want to include many type relations between entities. I have followed the docs and in it the entity properties are marked as public, so we can access it from other entities. I have defined entity properties as private. So I have created a getProperty method in entity to give back the property on entity instance. Now typeorm throws an error saying entity.getProperty is not a function.

User Entity

@Entity()
class User {

    private USER_ID_PREFIX: string = "uid-";

    @PrimaryColumn()
    private id: string;
    @Column({nullable: false, length: 15})
    private firstname: string;
    @Column({nullable: false, length: 15})
    private lastname: string;
    @Column({nullable: false, unique: true, length: 25})
    private email: string;
    @Column({nullable: false})
    private password: string;
    @OneToMany(() => Role, (role) => role.getProperty(EntityProperty.USER))
    private roles: Role[];

    public constructor(...args: any[]) {
        if (args.length === 0)
            return;
        if (args.length === 1) {
            if (args[0] instanceof UserCreateRequestDto) {
                let requestDto = args[0];
                this.id = this.USER_ID_PREFIX + randomUUID();
                this.firstname = requestDto.getProperty(DtoProperty.FIRSTNAME);
                this.lastname = requestDto.getProperty(DtoProperty.LASTNAME);
                this.email = requestDto.getProperty(DtoProperty.EMAIL);
                this.password = requestDto.getProperty(DtoProperty.PASSWORD);
            }
        }
    }

    public getProperty(property: EntityProperty): any {
        if (property === EntityProperty.ID)
            return this.id;
        if (property === EntityProperty.FIRSTNAME)
            return this.firstname;
        if (property === EntityProperty.LASTNAME)
            return this.lastname;
        if (property === EntityProperty.EMAIL)
            return this.email;
        if (property === EntityProperty.PASSWORD)
            return this.password;
        if (property === EntityProperty.ROLES)
            return this.roles;
        throw new Error(`No such entity property as ${property.toString()} exists in User`);
    }
}

export default User;

Role Entity

@Entity()
class Role {

    private ROLE_ID_PREFIX: string = "rid-";

    @PrimaryColumn()
    private id: string;
    @Column({nullable: false})
    private role: string;
    @ManyToOne(() => User, (user) => user.getProperty(EntityProperty.ROLES))
    private user: User;

    public constructor(...args: any[]) {
        if (args.length === 0)
            return;
        if (args.length === 1) {
            if (args[0] instanceof RoleCreateRequestDto) {
                let requestDto = args[0];
                this.id = this.ROLE_ID_PREFIX + randomUUID();
                this.role = requestDto.getProperty(DtoProperty.ROLE);
            }
        }
    }

    public getProperty(property: EntityProperty): any {
        if (property === EntityProperty.ID)
            return this.id;
        if (property === EntityProperty.ROLE)
            return this.role;
        if (property === EntityProperty.USER)
            return this.user;
        throw new Error(`No such entity property as ${property.toString()} exists in Role`);
    }
}

export default Role;

Error stack

query: SELECT VERSION() AS `version`
Error initializing mysql datasource TypeError: user.getProperty is not a function
    at RelationMetadata.givenInverseSidePropertyFactory (D:\Swivel_Projects\Ignite\Project 1\backend\ignite-auth-nodejs\src\entities\Role.ts:22:43)
    at RelationMetadata.buildInverseSidePropertyPath (D:\Swivel_Projects\Ignite\Project 1\backend\ignite-auth-nodejs\node_modules\src\metadata\RelationMetadata.ts:628:29)
    at D:\Swivel_Projects\Ignite\Project 1\backend\ignite-auth-nodejs\node_modules\src\metadata-builder\EntityMetadataBuilder.ts:1116:26
    at Array.forEach (<anonymous>)
    at EntityMetadataBuilder.computeInverseProperties (D:\Swivel_Projects\Ignite\Project 1\backend\ignite-auth-nodejs\node_modules\src\metadata-builder\EntityMetadataBuilder.ts:1096:34)
    at D:\Swivel_Projects\Ignite\Project 1\backend\ignite-auth-nodejs\node_modules\src\metadata-builder\EntityMetadataBuilder.ts:158:18
    at Array.forEach (<anonymous>)
    at EntityMetadataBuilder.build (D:\Swivel_Projects\Ignite\Project 1\backend\ignite-auth-nodejs\node_modules\src\metadata-builder\EntityMetadataBuilder.ts:157:25)
    at ConnectionMetadataBuilder.buildEntityMetadatas (D:\Swivel_Projects\Ignite\Project 1\backend\ignite-auth-nodejs\node_modules\src\connection\ConnectionMetadataBuilder.ts:106:11)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
1

There are 1 answers

0
Jagroop On

Here TypeORM requires the properties to be public because TypeORM relies on direct access to the properties but in you code you have marked entity properties to private. So this can be solved by modifying your code as :

@Entity()
class User {
    @PrimaryColumn()
    public id: string;

    @Column({ nullable: false, length: 15 })
    public firstname: string;

    @Column({ nullable: false, length: 15 })
    public lastname: string;

    @Column({ nullable: false, unique: true, length: 25 })
    public email: string;

    @Column({ nullable: false })
    public password: string;

    @OneToMany(() => Role, (role) => role.user)
    public roles: Role[];

    // Rest of the code...
}

@Entity()
class Role {
    @PrimaryColumn()
    public id: string;

    @Column({ nullable: false })
    public role: string;

    @ManyToOne(() => User, (user) => user.roles)
    public user: User;

    // Remaining code...
}