Event listener

Executes logic when certain events occur in an entity's lifecycle.

Even though everything is already working properly, we could argue in favor of another approach. The service is where the logic is stored, therefore it can get bloated quickly if not enough care is taken. With this in mind, we may try to relocate some logic for better organization.

TypeORM has a feature called Subscriber. It is a class that allows to create Entity Listeners, which are methods that "listen" for specific events in an entity's lifecycle. To better understand this, we can think like the following: I want that, when a user is about to be created, or updated (in this case, his password), then his password should be hashed. There are two listeners that listen to these events: beforeInsert() and beforeUpdate(). Then, let's get to work.

Create the file users -> subscribers -> users.subscriber. Underneath you can find how should be its basic skeleton, in order to listen to users' events. You'll also need to add it to providers in the UsersModule.

@EventSubscriber()
export class UsersSubscriber implements EntitySubscriberInterface<User> {
  constructor(private readonly dataSource: DataSource) {
    dataSource.subscribers.push(this);
  }

  listenTo() {
    return User;
  }
}

Now, we should inject in the constructor, the HashingService.

After that, we can proceed to the beforeInsert() listener. Notice that it has as parameter the event. We then extract the entity related to the event to perform the desired operation.

async beforeInsert(event: InsertEvent<User>) {
  const { entity: user } = event;

  user.password = await this.hashingService.hash(user.password);
}

In the beforeUpdate() listener, the process is very similar. We just also check if the field of the entity being updated is different than the field of the entity already in the database. We do this because, as the preload() method is used to update, in case a password is not sent, then it will have the value present in the database before the entity is updated.

async beforeUpdate(event: UpdateEvent<User>) {
  const { entity, databaseEntity: databaseUser } = event;
  const user = entity as User;

  if (user.password !== databaseUser.password) {
    user.password = await this.hashingService.hash(user.password);
  }
}

With this, we have relocated this logic from the service and alleviated its burden. Just don't forget to actually remove that logic from the service.

Commit - Using event listeners to hash password

Last updated