What is the correct way to update a post within a graphql subscription?

What is the correct way to update a post within a graphql subscription?


0

I’m working on a project using graphql with React Native in the frontend and Nodejs in the backend with mongodb as my database. In this project, I have posts that are created and shown on a screen. I’ve done this using graphql subscriptions for real time updating of posts, but I need the user to also be able to delete their own posts and for that update to be shown in real time. Currently, I have:

Backend:

import User from '../../models/User.js';
import Posts from '../../models/Posts.js';
import { requireAuth } from '../../services/auth.js';
import { PubSub } from 'graphql-subscriptions';
import { ObjectId } from "mongodb";
// need to change for product PubSub is not advised for production!!!

const pubsub = new PubSub();
const NEW_MESSAGE_EVENT = 'newMessage';

export default {
  getPost: async (_, { offset, limit, topic },{user}) => {
  try {
    const post = await Posts
    .find({"post.topic":topic})
    .skip(offset)
    .sort({"post.dateSent": -1 })
    .limit(Math.min(limit, 100))   
    return post
      } catch (error) {
        throw error;
      }
    },

    createPost: async (_, { input }) => {
      try {
        const post = new Posts(input);
        await post.save();
        pubsub.publish('NEW_POST_EVENT', { newPost: post });
        return post;
      } catch (error) {
        throw error;
    }
  },

  newPost: {
    subscribe: () => pubsub.asyncIterator(['NEW_MESSAGE_EVENT'])
 },

};

Frontend:

...
export const GET_POST = gql`
  query ($offset: Int!, $topic: String) {
    getMessage(offset: $offset, limit: 20, topic: $topic) {
     _id
     sender {
       senderId
     }
      post {
        text
        topic
        dateSent
      }
    }
  }
`;

export const CREATE_POST_MUTATION = gql`
  mutation createPost($input: PostInput!) {
    createPost(input: $input) {
        post {
          text
          dateSent
        }
      }
  }
`;

export const POST_SUBSCRIPTION = gql`
subscription {
  _id
  newPost {
    sender {
      senderId
    }
     post {
       text
        topic
       dateSent
     }
   } 
  }
`;

export default function PostScreen() {

const [createPost,{ data: createPostDate, error:createPostError}] = useMutation(CREATE_POST_MUTATION)
  async function createPostHandler (item: any) {
    createPost({
    variables: { 
    input: { 
       ...
   }, 
  }})
  }
const {data, loading, error, subscribeToMore, fetchMore: fetchMorePosts} = useQuery(GET_POST,  {fetchPolicy: 'cache-and-network', variables: { offset: 0, topic: ''}})
  useEffect(() => {
  subscribeToMore({
     document: POST_SUBSCRIPTION,
      updateQuery: (prev, { subscriptionData }) => {
         if (!subscriptionData) {
            return prev;
          }
          const newPost = subscriptionData.data.newPost;
          return {
            getPost:[newPost, ...prev.getPost]
          };
        },
        onError: err => console.error(err)
      });
}, []);

const fetchMoreAndUpdateCache = useMemo(() => {
  return async (offset: number) => {
    await fetchMorePosts({
      variables: { offset },
      updateQuery: (prev, { fetchMoreResult }) => {
        if (!fetchMoreResult) {
          return prev;
        }
        
        const moreItems = fetchMoreResult.getPost;
        if(moreItems.length < 1) {
          setStopFetchMore(true);
        }

        return {
          getPost: [...prev.getPost, ...moreItems]
        };
      }
    });
  };
}, []);

const fetchMore = useCallback(() => {
  if(!data || loading || isStopFetchMore) {
    return;
  }

  const offset = data.getPost.length;
  if (offset >= 20) {
    fetchMoreAndUpdateCache(offset);
  }
}, [isStopFetchMore, loading, data]);

return (
...
)
}

I’ve thought of two ways of being able to delete and have it update with the subscription, but I’m not sure if either is the correct way. The first way would be changing newPost to updatePost and have something like:

Backend:

....

 type PostSubscription {
    data: Post
    action: String
  }

 type Subscription {
    updatePost: PostSubscription
  }

deletePost: async (_, { ids}) => {
  try {
     const deletePosts = await Posts.deleteMany({_id:{$in:[...ids]}});
     pubsub.publish('UPDATE_POST_EVENT', {
      updatePost: {
          action: 'DELETE',
          data: deletePosts,
      }
    })
    return deletePosts;
  } catch (error) {
    throw error;
}
},

  updatePost: {
    subscribe: () => pubsub.asyncIterator(['UPDATE_POST_EVENT'])
 },

in the backend and try to check for changes in useEffect(() => {subscribeToMore({ document: POST_SUBSCRIPTION,... in the frontend , where maybe I would have to filter out deletePost from the getPost query.

export const POST_SUBSCRIPTION = gql`
subscription {
  _id
  updatePost {
    data{
      sender {
        senderId
      }
      post {
         text
         topic
         dateSent
      }
    }
    action
   } 
  }
`;

...

useEffect(() => {
  subscribeToMore({
     document: POST_SUBSCRIPTION,
      updateQuery: (prev, { subscriptionData }) => {
         if (!subscriptionData) {
            return prev;
          }
          const updatePost = subscriptionData.data.updatePost.data;
          const action = subscriptionData.data.updatePost.action;
          if (action === "CREATE") {
              return {
                 getPost:[updatePost, ...prev.getPost]
              };
          }
          else if (action === "DELETE"){
            getPost ...
         },
        onError: err => console.error(err)
      });
}, []);

The second way would be by having a whole separate subscription event: deletePost: { subscribe: () => pubsub.asyncIterator(['DELETE_POST_EVENT'])}, but I’m not 100% sure on how I would use that to update the data in the frontend. I would really appreciate any help or advice on how to do this. Thank you!


Load 4 more related questions


Show fewer related questions

0



Leave a Reply

Your email address will not be published. Required fields are marked *