Firebase withConverter throws an error when used in Admin SDK but not in firestore client

Viewed 201

I am trying to use the existing converter that we use on our client-side code when fetching data from firestore, the client-side converter works, but when I try to use it on the admin SDK, it throws some confusing type errors:

Client Side Code (Working)

async function getUser() {
  const db = getFirestore(); // from '@firebase/firestore'
  const userSnap = await getDoc(
    doc(db, 'users/Vl7vMV6AIrUFRL9iFkCWN8tgaXE2').withConverter(
      userConverter
    )
  );

  if (!userSnap.exists()) {
    return null;
  }

  const user = userSnap.data();

  return user;
}

Server side / Admin SDK (Error)

async function getUser() {
  const db = admin.firestore(); // from 'firebase-admin'
  const snap = await db
    .collection('users')
    .doc('Vl7vMV6AIrUFRL9iFkCWN8tgaXE2')
    .withConverter(userConverter)
    .get();

  if (!snap.exists) {
    return null;
  }

  const user = snap.data();

  return user;
}

Converter

const userConverter: FirestoreDataConverter<User> = {
  toFirestore(data: PartialWithFieldValue<User>): DocumentData {
    const user: PartialWithFieldValue<User> = {};

    if ('id' in data) {
      user.id = data.id;
    }

    if ('email' in data) {
      user.email = data.email;
    }

    if ('displayName' in data) {
      user.displayName = data.displayName;
    }

    if ('color' in data) {
      user.color = data.color;
    }

    if ('avatarUrl' in data) {
      user.avatarUrl = data.avatarUrl;
    }

    if ('status' in data) {
      user.status = data.status;
    }

    if ('jobTitle' in data) {
      user.jobTitle = data.jobTitle;
    }

    if ('phoneNumber' in data) {
      user.phoneNumber = data.phoneNumber;
    }

    if ('providerIds' in data) {
      user.providerIds = data.providerIds;
    }

    return user;
  },
  fromFirestore: (
    snap: QueryDocumentSnapshot,
    options: SnapshotOptions
  ): User => {
    const data = snap.data(options);

    const user: User = {
      id: snap.id,
      color: data.color,
      email: data.email,
      status: data.status,
      jobTitle: data.jobTitle,
      avatarUrl: data.avatarUrl,
      displayName: data.displayName,
      phoneNumber: data.phoneNumber,
      providerIds: data.providerIds,
    };

    return user;
  },
};

Does anyone know a fix? I can't seem to find other examples online with typescript.

1 Answers

I have reproduced your case, and FirestoreDataConverter worked fine for Admin SDK. Maybe you are not taking the code example from

https://googleapis.dev/nodejs/firestore/latest/global.html#FirestoreDataConverter (Admin SDK -> Node.js -> firebase-admin/firestore->firestoreDataConverter) but here https://firebase.google.com/docs/reference/node/firebase.firestore.FirestoreDataConverter ( Node.js client -> firebase.firestore -> firebaseDataConverter)

enter image description here

enter image description here

Below is the functional code :

**index.ts**

import { initializeApp, getApp } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
 
initializeApp();
const app = getApp();
const db = getFirestore(app);
class Post {
   constructor(readonly title: string, readonly authr: number) { }
   toString(): string {
       return this.title + ', by ' + this.authr;
   }
}
const postConverter = {
   toFirestore(post: Post): FirebaseFirestore.DocumentData {
       return { title: post.title, author: post.authr };
   },
   fromFirestore: (
       snapshot: FirebaseFirestore.QueryDocumentSnapshot
   )=>{
   const data = snapshot.data();
       return new Post(data.title, data.authr);
   }
};
db.collection('users')
   .withConverter(postConverter)
   .doc("101").get().then((postSnap) => {
       const post = postSnap.data();
       if (post !== undefined) {
           post.title; // string
           post.toString(); // Should be defined
           console.log(post.toString());
           //post.someNonExistentProperty; // TS error
       }
   })

**package.json**

{
 "dependencies": {
   "firebase-admin": "^10.3.0"
 }
}
Related