2

Let's have simplified TypeORM entity User:

@Entity()
export class User extends BaseDatabaseEntity {
  @Column({
    length: 255,
  })
  public firstName!: string;

  @Column({ length: 60 })
  @Exclude()
  public password: string;

  @BeforeUpdate()
  @BeforeInsert()
  private hashPassword(): void {
    const tmpPassword = hashSync(
      this.password + 'config.auth.password.secret',
      genSaltSync(),
    );
    this.password = tmpPassword;
  }
}

}

I need to replace config.auth.password.secret with NestJS's config (namespace from ConfigService):

export default registerAs('app', () => {
   return {
    password: {
      secret: process.env.AUTH_PASSWORD_SECRET,
    }
  };
});

, but entities are not part of NestJS structure so I cannot just inject it as usual.

How to achieve NestJS configs in TypeORM entities?

Baterka
  • 3,075
  • 5
  • 31
  • 60

3 Answers3

2

I needed this too and came up with workaround. You can't inject the config to Entity, as was said already.

I came up with solution to export object with config values that I need in Entities:

Initialize Config in app module:

  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [defaultConfig]
    }),
// .....other imports
  ]

defaultConfig is a function gathering and checking config values. On top of that it sets values to STATIC_CONFIG object.

export const STATIC_CONFIG = {
  WEB_APP_URL: '',
  BASE_URL: '',
};

export const defaultConfig = () => {
  const port = +(process.env[ENV_SERVER.PORT] || 3000);
  const production = process.env.NODE_ENV === 'production';
  const retVal = {
    PRODUCTION: production,
    PORT: port,
    BASE_URL: process.env.BASE_URL || 'http://localhost:3000',
    URL_PREFIX: process.env.URL_PREFIX || 'http://localhost:3000',
//    ..... plenty of other values
  }

  if (retVal[ENV_S3.HOST] && !(retVal[ENV_S3.ACCESS] && retVal[ENV_S3.SECRET])) {
    // tslint:disable-next-line:no-console
    console.error('S3 configuration error: no access or secret set; exiting');
    process.exit(1);
  }

  STATIC_CONFIG.WEB_APP_URL = retVal.WEB_APP_URL;
  STATIC_CONFIG.BASE_URL = retVal.BASE_URL;

  return retVal;
};

And finally in my entities I use the STATIC_CONFIG object, for example:

  @Expose({ name: 'image', toPlainOnly: true, groups: [TG_MOBILE] })
  get mobileAppImage() {
    return this.id ? `${STATIC_CONFIG.BASE_URL}/static/image/${this.id}` : undefined;
  }
PeS
  • 3,757
  • 3
  • 40
  • 51
1

Entities are not a part of the injectable providers in NestJS so inherently this isn't possible. You can make it a part of your service code, however, and do the password hashing there before insert/update.

Jay McDoniel
  • 57,339
  • 7
  • 135
  • 147
  • Yes, thats exactly what I wanted to do, but I am not sure how to make entities part of NestJS. Can you provide example? – Baterka Feb 18 '20 at 18:48
  • 1
    You can't. That's what I said. Entities are not `@Injectable()` and there's no plan to make them. Instead, you should move the hasing logic to your service. [This answer](https://stackoverflow.com/a/60119494/9576186) explains a bit more in depth as to why – Jay McDoniel Feb 18 '20 at 18:52
  • So I am supposed to completely remove methods from entities or make them like so: `hashPassword(generator: IUserPassGenerator)`? – Baterka Feb 18 '20 at 18:56
  • If you need access to a service that should be injected, either you'll need to make the logic in a service that uses the entity, or inject the needed service into the service that makes use of the entity and pass the needed service to the entity's method. Personally, I'd go with the former rather than the latter – Jay McDoniel Feb 18 '20 at 18:57
  • @JayMcDoniel but if i need to @Column({ type: DataType.ENUM(config.get('app.i18n.languages')) }) lang: string . What is this configuration for if it cannot be used. I not see that in other frameworks before. – Michael Dec 08 '21 at 14:38
  • @JayMcDoniel why not work if const config = new ConfigService({ ignoreEnvFile: true, isGlobal: true, load: [configuration] }) – Michael Dec 08 '21 at 14:44
  • @JayMcDoniel may be that will work in netity var c = ConfigModule.forRoot({ ignoreEnvFile: true, isGlobal: true, load: [configuration] }), – Michael Dec 08 '21 at 14:58
-1
@Entity()
export class User extends BaseDatabaseEntity {
  @Column({
    length: 255,
  })
  public firstName!: string;

  @Column({
    length: 60,
    transformer: new PasswordTransformer(new ConfigService())
  })
  @Exclude()
  public password: string;
}
export class PasswordTransformer implements ValueTransformer {
  constructor(private config: ConfigService) {}

  from(value: string): string {
    return value;
  }

  to(value: string): string {
    return hashSync(
      this.password + this.config.get('AUTH_PASSWORD_SECRET'),
      genSaltSync(),
    );
  }
}

Tinismo
  • 39
  • 6