Search code examples
firebasegraphqlnestjsapollo

How to use firebase timestamp with graphql nest js?


All dates in firebase are stored as timestamps, no matter how do you store them: as ISO string, or just as new Date().

But I don't want to send firestore.Timestamp instance on the client side, so I need to somehow serialize it.

What are the options? How can I send only ISO string, or unix time?


Solution

  • 1. Simpliest and dumbest solution

    Store date using unix time:

    const newItem = {
      ...,
      createdAt: Date.now(),
    };
    
    itemsCollection.add(newItem);
    

    this way it will be stored as plain number and it can be converted back to normal date using new Date()

    2. Use third-party library (worked only for schema first approach for me)

    firestore-graphql-scalars contains scalar type definitions and resolvers for firebase timestamp

    If you are going for scheme-first approach it works perfectly fine, but I couldn't find a way to use it in Nest JS GraphQL code first approach

    3. Define new scalar type yourself (code first approach implementation)

    At the end I came up with this scalar using code first approach:

    @Scalar('FirebaseTimestamp')
    export class FirebaseTimestampScalar implements CustomScalar<number, admin.firestore.Timestamp> {
      description = 'Firebase timestamp';
    
      parseValue(milliseconds: number): admin.firestore.Timestamp {
        return admin.firestore.Timestamp.fromMillis(milliseconds); // value from the client
      }
    
      serialize(timestamp: admin.firestore.Timestamp): number {
        return timestamp.toMillis(); // value sent to the client
      }
    
      parseLiteral(ast: ValueNode): admin.firestore.Timestamp {
        if (ast.kind === Kind.INT) {
          return admin.firestore.Timestamp.fromMillis(Number(ast.value));
        }
    
        return null;
      }
    }
    

    and in data type:

    @ObjectType()
    export class DocumentMeta implements IDocumentMeta {
      @Field(() => ID)
      createdBy: string;
      @Field(() => ID)
      updatedBy: string;
      @Field(() => FirebaseTimestampScalar)
      createdAt: Date;
      @Field(() => FirebaseTimestampScalar)
      updatedAt: Date;
    }
    

    P.S. Don't forget to add this scalar to resolver module providers

    @Module({
      providers: [
        ...resolvers,
        FirebaseTimestampScalar,
      ],
    })
    export class ResolversModule {}
    

    you still have to convert it to date on client side, but on server side it remains as Timestamp instance, so it looks a little bit cleaner

    5. Worth mentioning, but maybe not so useful

    If you still think you need to get Timestamp 'as is' on client-side, you can stringify timestamp object, and parse it on client

    You can do it yourself or using graphql-type-json