'Could not identify object - getting error in graphql apollo client
I'm new to graphql I'm building real time chat app. Currently I'm making it offline first. Using react as front-end.
I'm currently caching the data on localStorage using apollo3-cache-persist. But How do I query the cache data instead of server (when I'm offline) also I want to add messages to the localStorage while I'm offline.
Display the optimistic response when the device is online I want to send the pending data to the server.
my ApolloProvider.js file in client folder
import React from "react";
import {
ApolloClient,
InMemoryCache,
ApolloProvider as Provider,
createHttpLink,
ApolloLink,
split,
} from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { RetryLink } from "@apollo/client/link/retry";
import { persistCache, LocalStorageWrapper } from "apollo3-cache-persist";
import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
import QueueLink from "apollo-link-queue";
let httpLink = createHttpLink({
uri: "http://localhost:4000/",
});
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem("token");
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
});
httpLink = authLink.concat(httpLink);
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/`,
options: {
reconnect: true,
connectionParams: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
},
});
const link = new RetryLink();
const queueLink = new QueueLink();
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
const cache = new InMemoryCache();
const fun = async () =>
await persistCache({
cache,
storage: new LocalStorageWrapper(window.localStorage),
});
fun();
const client = new ApolloClient({
// link: splitLink,
link: ApolloLink.from([splitLink, queueLink, link]),
cache,
name: "chat-app",
version: "1.0.0",
queryDeduplication: false,
defaultOptions: {
watchQuery: {
fetchPolicy: "cache-and-network",
},
},
});
export default function ApolloProvider(props) {
return <Provider client={client} {...props} />;
}
my messages.js file
import React, { Fragment, useEffect, useState } from "react";
import { gql, useLazyQuery, useMutation, InMemoryCache } from "@apollo/client";
import { Col, Form } from "react-bootstrap";
import { useMessageDispatch, useMessageState } from "../../context/message";
import uuid from "react-uuid";
import Message from "./Message";
const SEND_MESSAGE = gql`
mutation sendMessage($uuid: String, $to: String!, $content: String!) {
sendMessage(uuid: $uuid, to: $to, content: $content) {
uuid
from
to
content
createdAt
hasSeen
hasSent
}
}
`;
const GET_MESSAGES = gql`
query getMessages($from: String!) {
getMessages(from: $from) {
uuid
from
to
content
createdAt
hasSeen
}
}
`;
export default function Messages() {
const { users } = useMessageState();
const dispatch = useMessageDispatch();
const [content, setContent] = useState("");
const selectedUser = users?.find((u) => u.selected === true);
const messages = selectedUser?.messages;
const [getMessages, { loading: messagesLoading, data: messagesData }] =
useLazyQuery(GET_MESSAGES, {
update(cache) {
cache.readFragment({});
console.log("reading");
},
});
const [sendMessage] = useMutation(SEND_MESSAGE, {
update(cache, { data: { sendMessage } }) {
cache.modify({
fields: {
getMessages(existingMsg) {
console.log(existingMsg);
const newMsgRef = cache.writeFragment({
data: sendMessage,
fragment: gql`
fragment sendNewMessage on Mutation {
uuid
to
from
content
hasSeen
hasSent
}
`,
});
return existingMsg.push(newMsgRef);
},
},
});
},
onError: (err) => console.log(err),
});
useEffect(() => {
if (selectedUser && !selectedUser.messages) {
getMessages({ variables: { from: selectedUser.username } });
}
}, [selectedUser]);
useEffect(() => {
if (messagesData) {
dispatch({
type: "SET_USER_MESSAGES",
payload: {
username: selectedUser.username,
messages: messagesData.getMessages,
},
});
}
}, [messagesData]);
const submitMessage = (e) => {
e.preventDefault();
if (content.trim() === "" || !selectedUser) return;
let id = uuid();
sendMessage({
variables: { uuid: id, to: selectedUser.username, content },
optimisticResponse: {
sendMessage: {
__typename: "Mutation",
uuid: id,
from: "User",
to: selectedUser.username,
content,
hasSent: false,
hasSeen: false,
createdAt: Date.now(),
},
},
});
setContent("");
};
// Displaying helper text and styling
let selectedChatMarkup;
if (!messages && !messagesLoading) {
selectedChatMarkup = <p className="info-text"> Select a friend</p>;
} else if (messagesLoading) {
selectedChatMarkup = <p className="info-text"> Loading..</p>;
} else if (messages.length > 0) {
selectedChatMarkup = messages.map((message, index) => (
<Fragment key={message.uuid}>
<Message message={message} />
{index === messages.length - 1 && (
<div className="invisible">
<hr className="m-0" />
</div>
)}
</Fragment>
));
} else if (messages.length === 0) {
selectedChatMarkup = (
<p className="info-text">
You are now connected! send your first message!
</p>
);
}
return (
<Col xs={10} md={8}>
<div className="messages-box d-flex flex-column-reverse">
{selectedChatMarkup}
</div>
<div>
<Form onSubmit={submitMessage}>
<Form.Group className="d-flex align-items-center">
<Form.Control
type="text"
className="message-input rounded-pill p-4 bg-secondary border-0"
placeholder="Type a message.."
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<i
className="fas fa-regular fa-paper-plane fa-2x text-primary ml-2"
onClick={submitMessage}
role="button"
></i>
</Form.Group>
</Form>
</div>
</Col>
);
}
But I'm currently getting this error when I try to send the message
react_devtools_backend.js:3973 Invariant Violation: Could not identify object {"uuid":"4855ffc-6b7b-d7c8-a68-2ae0162f80a","from":"User","to":"Fire","content":"example text","createdAt":1648881891383,"hasSeen":false,"hasSent":false,"__typename":"Mutation"}
Also getting error from the mutation error log
Error: Could not identify object {"__typename":"Message","uuid":"4855ffc-6b7b-d7c8-a68-2ae0162f80a","from":"Alan","to":"Fire","content":"example text","createdAt":"2022-04-02T06:44:51.807Z","hasSeen":false,"hasSent":false}
Solution 1:[1]
Apollo uses by default __typename
and id
fields to normalise cache. So in your case, Apollo won't recognise your uuid
property as a cache identifier. You can change the uuid uuid
property, or add keyFields
to your InMemoryCache
config.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Gijs Lebesque |