DEV Community

Cover image for NestJS-TypeORM Listener & Subscriber
Justin Martin for Kezios

Posted on • Updated on • Originally published at kezios.fr

NestJS-TypeORM Listener & Subscriber

NestJS est devenu un incontournable pour créer des API efficaces et stables, tout en fournissant au développeur un environnement de développement très complet. Les “entity listeners” et les “subscribers” font partie de ses outils peu connus et pourtant pouvant être très utile.

Les entity listeners et les subscribers permettent d’attacher l’exécution d’une fonction à un événement.

Entity listener:

Pour initialiser les listeners on utilise un decorator, et une méthode directement au sein de la déclaration de l’entité. Voici la liste des décorateurs disponible : @AfterLoad, @BeforeInsert, @AfterInsert, @BeforeUpdate, @AfterUpdate, @BeforeRemove, @AfterRemove, @BeforeSoftRemove, @AfterSoftRemove, @BeforeRecover, @AfterRecover.

Un des cas d’usage classique de “entity listeners” concerne le hachage automatique du mot de passe. En effet, avant d’insérer un compte dans la base de données, on va hacher son mot de passe.

account.entity.ts

@Entity()
export class Account extends BaseEntity {
  @Column({ length: 100, select: false })
  password!: string;

  @Column({ length: 100 })
  email!: string;

  @BeforeInsert
  autoHashPassword(): void {
        this.password = bcrypt.hashSync(this.password, SALT_ROUNDS);
  }
}
Enter fullscreen mode Exit fullscreen mode

Cependant les entity listener ont une limitation relativement importante, on ne peut pas faire de call à la base de données dans notre listener. Pour effectuer ce genre de call il faut utiliser les subscribers.

Subscribers :

Pour mettre en place un subscribers on doit créer une nouvelle classe implémentant l’interface EntitySubscriberInterface et une fois de plus utiliser un décorateur. Une fois dans notre classe, il suffit de créer des méthodes avec un nom spécifique comme : afterInsert, beforeInsert, …

Par exemple, si ont souhaite notifier tous les utilisateurs de la création d’un nouveau compte:

account.subscriber.ts :

@Injectable()
@EventSubscriber()
export class AccountSubscriber implements EntitySubscriberInterface<Account> {
  constructor(
    private readonly connection: Connection, 
    private readonly notificationService: NotificationService
  ) {
    connection.subscribers.push(this);
    Logger.log(`Subscriber Account initialized`);
  }

  listenTo(): typeof Account {
    return Account;
  }

  afterInsert = async (event: InsertEvent<Account>): Promise<void> => {
    const newAccountMail = event.entity.email; 
    const emailsToNotify = await event.manager.find(Account, { where: { email: Not(newAccountMail)}});
    await notificationService.sendEmail(
      emailsToNotify,
      "Nouvelle utilisation",
      `Salut un nouvel utilisateur nous a rejoint, vous pouvez le contacter ici: ${newAccountMail}` 
    );
  };
}
Enter fullscreen mode Exit fullscreen mode

Pour aller plus loin :

Pour qu’un subscriber fonctionne il faut indiquer à TypeORM de le prendre en compte. Si vous utilisez la même syntaxe que moi, c’est fait automatique par le constructeur via cette instruction connection.subscribers.push(this);. De plus, cette méthode vous permet d’utiliser des services dans votre subscriber.

Cependant, si vous voulez procéder manuellement à l’enregistrement des subscribers, il vous faudra supprimer l’instruction `

connection.subscribers.push(this); puis ajouter subscribers=[/dist/**/*.subscriber.js] dans votre configuration.

source:

Discussion (0)