How to inject dependency into TypeORM ValueTransformer or EntitySchema

Viewed 436

In my NestJS project I use TypeORM with external schema definition. I want to use a ValueTransformer to encrypt sensitive data in some database columns, e.g. the email of the user should be encrypted. The ValueTransformer depends on an encryption service from another module. I can't figure out how to inject that service via DI.

export const UserSchema = new EntitySchema<User>(<EntitySchemaOptions<User>>{
    name: 'user',
    columns: {
        email: {
        type: 'varchar',
        nullable: false,
        transformer: new EncryptedValueTransformer()
        } as EntitySchemaColumnOptions,
    },
});
export class EncryptedValueTransformer implements ValueTransformer {

    @Inject()
    private cryptoService: CryptoService

    public to(value: unknown): string | unknown {
        // error: cryptoService undefined
        return this.cryptoService.encrypt(value);
    }
     
    public from(value: unknown): unknown {
        // error: cryptoService undefined
        return this.cryptoService.decrypt(value);
    }
}
@Injectable()
export class CryptoService {
    public constructor(@Inject('CONFIG_OPTIONS') private options: CryptoModuleOptions) {
        // options contain a secret key from process.env
    }

    public encrypt(data: string): string | undefined | null { ... }
    public decrypt(data: string): string | undefined | null { ... }
}
@Module({})
export class CryptoModule {
    public static register(options: CryptoModuleOptions): DynamicModule {
        return {
        module: CryptoModule,
        providers: [
            {
                provide: 'CONFIG_OPTIONS',
                useValue: options,
            },
            CryptoService,
        ],
        exports: [CryptoService],
        };
    }
}
@Module({
    imports: [
        DomainModule,
        CryptoModule.register({ secretKey: process.env.ENCRYPTION_KEY }),
        TypeOrmModule.forFeature([
            UserSchema,
            UserTypeOrmRepository,
        ]),
    ],
    providers: [
        UserRepositoryProvider,
        UserRepositoryTypeOrmAdapter,
    ],
    exports: [
        UserRepositoryProvider,
        UserRepositoryTypeOrmAdapter,
    ],
})
export class PersistenceModule {}

With the above code the CryptoService instance in EncryptedValueTransformer is undefined. I searched related issues. According to this post https://stackoverflow.com/a/57593989/11964644 NestJS needs some context for DI to work. If that context is not given, you can manually provide the dependency.

My workaround now is this:

export class EncryptedValueTransformer implements ValueTransformer {

    private cryptoService: CryptoService

    public constructor() {
        // tightly coupled now an process.env.ENCRYPTION_KEY still undefined at instantiation time
        this.cryptoService = new CryptoService({ secretKey: process.env.ENCRYPTION_KEY });
    }
}

But with this workaround the process.env variable is not yet resolvable at the point where the class is being instantiated, so I need to modify CryptoService in a way that it reads the env variable at runtime itself. And at this point the CryptoService is not reusable anymore.

How can I inject the CryptoService into the EncryptedValueTransformer or into UserSchema with this external schema setup? Or any better way to solve this?

0 Answers
Related