Cloud Functions(TypeScript) - How to use custom Models instead of nested maps/dictionaries

Viewed 948

I made custom models for users and posts in my cloud functions as follows(models.ts):

export class UserModel {
    picture?: string;
    fullName: string;
    totalPostsCount?: number;
    recentPosts?: PostModel[];

    constructor(userMap: any) {   //the documentations usually had specific properties for the constructor instead of a map/dictionary. I thought this would be better. Am I wrong?
        this.fullName = userMap.fullName;
        this.picture = userMap.picture;
        this.totalPostsCount = userMap.totalPostsCount;
        this.recentPosts = userMap.recentPosts;     //since this is another Custom Model, do we need to convert userMap.recentPosts dictionary array to PostModel[] here?
    }
}

export class PostModel {
    title: string;
    postDate: admin.firestore.Timestamp;   //is this the right type for a date object in cloud functions? there is also a FirebaseFirestore.Timestamp
    creator_user: UserModel;

    constructor(postMap: any) {
        this.title = postMap.title;
        this.creator_user = postMap.creator_user;   //since this is another Custom Model, do we need to convert postMap.creator_user dictionary to UserModel here?
    }
}

Instead of dealing with map/dictionary objects from firestore documentSnapshots, I want to fill in these Models. This way I can manipulate the fields with more of a error free way.

I found about FirestoreDataConverter which supposedly helps for this problem. There is a Custom Objects tutorial on Firestore documentation which shows a different implementation. I copy pasted the codes in the links below for ease:

class Post {
  constructor(readonly title: string, readonly author: string) {}

  toString(): string {
    return this.title + ', by ' + this.author;
  }
}

const postConverter = {
  toFirestore(post: Post): firebase.firestore.DocumentData {
    return {title: post.title, author: post.author};
  },
  fromFirestore(
    snapshot: firebase.firestore.QueryDocumentSnapshot,
    options: firebase.firestore.SnapshotOptions
  ): Post {
    const data = snapshot.data(options)!;
    return new Post(data.title, data.author);
  }
};

const postSnap = await firebase.firestore()
  .collection('posts')
  .withConverter(postConverter)
  .doc().get();
const post = postSnap.data();
if (post !== undefined) {
  post.title; // string
  post.toString(); // Should be defined
  post.someNonExistentProperty; // TS error
}

And;

class City {
    constructor (name, state, country ) {
        this.name = name;
        this.state = state;
        this.country = country;
    }
    toString() {
        return this.name + ', ' + this.state + ', ' + this.country;
    }
}

    // Firestore data converter
  cityConverter = {
      toFirestore: function(city) {
          return {
              name: city.name,
              state: city.state,
              country: city.country
              }
      },
      fromFirestore: function(snapshot, options){
          const data = snapshot.data(options);
          return new City(data.name, data.state, data.country)
      }
  }

 // Set with cityConverter
db.collection("cities").doc("LA")
  .withConverter(cityConverter)
  .set(new City("Los Angeles", "CA", "USA"));

Both of these create errors in my Cloud Functions code which is written in TypeScript. My question is FirestoreDataConverter the best way for this? What are the options? Can someone please post the right way to model a user<->post relationship to use in cloud functions? (while keeping in mind both models have the other model as properties and also what would happen if the maps from server have nested maps?)

0 Answers
Related