firebase-admin-node: FirebaseError type definition as class instead of interface

Description

Currently is FirebaseError defined as interface in typescript definition file. So in case of async/await we have to handle errors checking e.g. code property existence:

try {
    const decodedIdToken = await this.auth.verifyIdToken(firebaseIdToken);
    // some another calls async methods
} catch(err) {
    if (err.code === 'auth/argument-error') {
        // handle errors - but err is still any type
    }
}

This is proper way to handle errors but a little bit cleaner way would be:

try {
    const decodedIdToken = await this.auth.verifyIdToken(firebaseIdToken);
    // some another calls async methods
} catch(err) {
    if (err instanceof FirebaseError) {
        // handle error codes and proper typing here
    }
}

It is just a small improvement but in this way we have correct type in if statement and also FirebaseError.code would be some string literal type with defined all possible error codes.

type FirebaseErrorCode = 'auth/argument-error' | 'auth/id-token-expired' | ... ;

class FirebaseError extends Error {
    code: FirebaseErrorCode;
}

I think this could be really helpful in error handling.

Target environment

  • Operating System version: all
  • Firebase SDK version: latest
  • Library version: latest
  • Firebase Product: common

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 42
  • Comments: 21 (6 by maintainers)

Most upvoted comments

@jukbot For me using firebase-admin I had to do:

import { FirebaseError } from 'firebase-admin';

export default function isFirebaseError(error: unknown): error is FirebaseError {
  return (error as FirebaseError).code !== undefined;
}

Then:

try {
      ...
    } catch (error) {
      if (isFirebaseError(error)) {
        res.status(error.code === 'auth/id-token-expired' ? 401 : 100).end('Not authenticated properly');
      }
    }

Now if only I can find a way to get at the error code variables. I see them in the utils subdirectory, but I’m not sure they’re exposed.

@hiranya911 Are you still trying to phase out error codes as your comment from 2018 mentioned? I think it would be better to expose a bunch of error code enums instead of me doing things like error.code === 'auth/id-token-expired'

Alternately, in latest version of firebase sdk, now it’s can import global FirebaseError from ‘@firebase/util’ package and using with type guard as below.

import { FirebaseError } from '@firebase/util'

try {
    // Some firebase functions
    await signInWithEmailAndPassword(auth, email, password)
} catch (error: unknown) {
   if (error instanceof FirebaseError) {
      console.error(error.code)
   }
}

Here is a method I am using for now that uses a custom type guard.

interface IFirebaseError extends Error {
   code: string;
   message: string;
   stack?: string;
}

/** Check if we have a firebase error. */
export function isFirebaseError(err: any): err is IFirebaseError {
   return err.code && err.code.startsWith('auth/');
}

Alternately, in latest version of firebase sdk, now it’s can import global FirebaseError from ‘@firebase/util’ package and using with type guard as below.

import { FirebaseError } from '@firebase/util'

try {
    // Some firebase functions
    await signInWithEmailAndPassword(auth, email, password)
} catch (error: unknown) {
   if (error instanceof FirebaseError) {
      console.error(error.code)
   }
}

Amazing! thank you! This is super helpful.

Just in case anyone is interesed, instead of using instanceof to check if error comes from Firebase, I have made this workaround to check if error is from the auth system:

function isFirebaseError(err) {
  if (err.code)
    return err.code.startsWith('auth/');
  return false;
}

Hope it helps 😉

Any update on this @hiranya911? I’m currently checking if my error object has a code property in my error middleware, but this interferes with other packages I’m using. (other errors thinking it’s a FirebaseError by having a code property)

It would be a whole lot cleaner / easier if we could use error instanceof FirebaseError. The codes on the error object work perfectly fine for me, as I can do the label mapping to a nicer message myself, but the problem is more in the interface not being a class.

Edit: I just noticed the errors in auth do have nice labels for mapping, but the firestore ones have just a number.

Also using enum with comments instead of string literal could be a better improvement.

enum FirebaseErrorCode {
    /** Thrown if a method is called with incorrect arguments. */
    AUTH_ARGUMENT_ERROR = 'auth/argument-error',
    // ...
}

And comments are visible in IDE:

screenshot 2018-11-30 at 09 53 45