'How to handle multiple catchError in Observable?

First time using NestJs with Observables. I'm familiar with how to solve this using Promises, but I'd like to expand my knowledge of Observables.

Below is a "signup" function that needs to do a couple of things. First, it creates the tenant, and then from the result of the tenant, it creates the user.

In the event that the tenant or user already exists a unique constraint violation error will be raised. What I'd like to do in the event the error occurs is bail out of the observable and return the error to the controller. What ends up happening upon catching the first error it will be caught again in the second "catchError"

I've handled this in the second "catchError" below (by looking for a BadRequestException), but this doesn't feel right. My question is if there is a better way to handle this?

Thanks in advance!

signup(signUpDto: SignupRequestDto, tenantDto: TenantDto): Observable<User> {
  const createTenant$ = from(
    this.createTenant(tenantDto.name, tenantDto.key),
  );

  const user = createTenant$.pipe(
    catchError((error, caught) => {
      const err = error as PrismaClientKnownRequestError;
      this._logger.error({ err, caught }, 'tenant err');

      // Unique constraint violation
      if (err.code === 'P2002') {
        throw new BadRequestException(
          'Tenant already exists',
          tenantDto.name,
        );
      } else {
        throw new InternalServerErrorException('Error creating user');
      }
    }),
    switchMap((tenant) => {
      return from(this.createUser(signUpDto, tenant.id));
    }),
    catchError((error, caught) => {
      // If this is error is already handled by the catchError above
      if (error instanceof BadRequestException) {
        throw error;
      } else {
        const err = error as PrismaClientKnownRequestError;
        this._logger.error({ err, caught }, 'user err');

        // Unique constraint violation
        if (err.code === 'P2002') {
          throw new BadRequestException('Email already exists');
        } else {
          throw new InternalServerErrorException('Error creating user');
        }
      }
    }),
  );

  return user;
}


Solution 1:[1]

You can achieve it by using single catchError,but the problem was to identify the source.For that I have taken a private variable to identify the source.

private currentEvent: string = 'createTenant'; //default value createTeanant

  private currentEvent: string = 'createTenant';
  signup(signUpDto: SignupRequestDto, tenantDto: TenantDto): Observable<User> {
    
    const createTenant$ = from(
      this.createTenant(tenantDto.name, tenantDto.key)
    );
    return user = createTenant$.pipe(
      switchMap((tenant) => {
        
        currentEvent = 'createUser';//Assigning new event name
        
        return from(this.createUser(signUpDto, tenant.id));
      }),
      catchError((error, caught) => {
        const err = error as PrismaClientKnownRequestError;

        this._logger.error({ err, caught }, this.currentEvent + ' err');

        // Unique constraint violation
        if (err.code === 'P2002' && this.currentEvent === 'createTenant') {
          throw new BadRequestException(
            'Tenant already exists',
            tenantDto.name
          );
        } else if (err.code === 'P2002' && this.currentEvent === 'createUser') {
          throw new BadRequestException('Email already exists');
        } else {
          throw new InternalServerErrorException('Error creating user');
        }
      })
    );
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Saptarsi