Apollo useSubscription hook not emitting new data - reactjs

I have created subscription whenever a new post is added. The subscription works fine on the graphiql interface.
Here is my react code to use useSubscription hook
export const SUSCRIBE_POSTS = gql`
{
subscription
posts {
newPost {
body
id
createdAt
}
}
}
`;
const {
loading: suscriptionLoading,
error: subscriptionError,
data: subscriptionData,
} = useSubscription(SUSCRIBE_POSTS);
when I try to console log subscriptionData I get nothing. when I add a post it is saved in database correctly, the useQuery hook for getting posts also work fine, but when I add a new post I don't see the subscription data. I can't see anything wrong in the console as well. When I log suscriptionLoading, Ido get true at the start. I am not sure how to debug this.
The client setup is done correctly according to the docs https://www.apollographql.com/docs/react/data/subscriptions/
and here is the code
const httpLink = createHttpLink({
uri: "http://localhost:4000/",
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/graphql`,
options: {
reconnect: true,
},
});
const authLink = setContext(() => {
const token = localStorage.getItem("jwtToken");
return {
headers: {
Authorization: token ? `Bearer ${token}` : "",
},
};
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
authLink.concat(httpLink)
);
const client = new ApolloClient({
link: link,
cache: new InMemoryCache(),
});
export default (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);

export const SUSCRIBE_POSTS = gql`
subscription
posts {
newPost {
body
id
createdAt
}
}
`;
Can you try to remove the most-outside brackets of subscription gql?

Related

Graphql useSubscription not giving response everytime

I am using Graphql useSubscription hook to hit a websocket api but there seems to be an issue. I am only receiving data when i enter the component for the first time or when i go back to some other component and come back again, when trying to refresh majority of the times I do not get the data.
Below is my setup for the following.
/* eslint-disable flowtype/no-types-missing-file-annotation */
import Cookies from 'js-cookie'
import { split, HttpLink, InMemoryCache, ApolloClient } from '#apollo/client'
import { setContext } from '#apollo/client/link/context'
import { WebSocketLink } from '#apollo/client/link/ws'
import { getMainDefinition } from '#apollo/client/utilities'
import { onError } from 'apollo-link-error'
import { ApolloLink } from 'apollo-link'
const env = process.env.NODE_ENV
const domain = env === 'development' ? 'localhost' : '.xyz'
const url = env === 'development' ? 'https://staging-xxx.xxxx.xx' : process.env.REACT_APP_API_URL;
const wsURL = env === 'development' ? 'wss://staging-xxx.xxxxx.xx/subscriptions' : process.env.REACT_APP_WSS_URL;
const httpLink = new HttpLink({
uri: url,
credentials: 'include'
})
const authLink = setContext((_: any, { headers }: any) => {
const app_token = Cookies.get('xxxxx', { domain: domain })
let token = app_token || 'insta-checkout'
return {
headers: {
...headers,
MUDEY_AUTH_TOKEN: token
}
}
})
const wsLink = new WebSocketLink({
uri: wsURL,
options: {
reconnect: true,
connectionParams: async () => {
const app_token = await Cookies.get('xxxxx', { domain: domain })
return {
credentials: 'include',
MUDEY_AUTH_TOKEN: app_token,
Authorization: 'Basic xxxxxxxxxxxxxxxxxxx'
}
}
}
})
const link = split(
({ query }: any) => {
const definition = getMainDefinition(query)
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription'
},
wsLink,
authLink.concat(httpLink)
)
const client = new ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([link])
})
export default client
Now when i go my component for the first time, i am calling the subscriptions api by
import React from "react";
import { useMutation, useSubscription } from "#apollo/react-hooks";
export const NewComponent = () => {
const {
loading: loadingPackages,
data,
error,
} = useSubscription(SUBSCRIBE_CAR_PACKAGES, {
onSubscriptionData: useCallback((res: any) => {
const {
subscribeCarJourneyPackages: { message: stopWS, data: packagesResult },
} = res.subscriptionData.data;
if (packagesResult !== null) {
console.log("packarray", packagesResult);
setIsSubsLoading(true);
}
if (stopWS === "SUBSCRIPTION_COMPLETE") {
dispatch({ type: SET_ALL_PACKAGES, payload: packArray });
setIsSubsLoading(false);
} else {
// setIsSubsLoading(true)
}
}, []),
onError: useCallback((err: any) => {
apiErrorHandler(err);
}, []),
variables: { id: journeyID },
});
return null;
};
So the response i see is
But once i start refreshing the page , i only see
So what the issue in my frontend, for not getting the response 100% of the time ? should we need to close the connection everytime we receive response ?
Also i see the subscription api hitting even when i am in my homepage, where ideally it should hit in the results page where i want it, do this happens the moment we define connection and is it normal?
I know it's late but it maybe help someone else !
try giving fetchPolicy: 'cache-and-network' as option below your variables field.
like this :
variables: {... your variables },
fetchPolicy: 'cache-and-network'

CORS, with Apollo client. Only being able to get results with the hook useQuery

This is my apollo client code.
import {
ApolloClient,
ApolloLink,
createHttpLink,
InMemoryCache
} from "#apollo/client"
import { setContext } from "#apollo/client/link/context"
let uri = `${process.env.NEXT_PUBLIC_API_URL}/wp/graphql`
const httpLink = createHttpLink({ uri, credentials: "include" })
const authLink = setContext((_, { headers }) => {
headers
})
let client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: "no-cache"
}
}
})
export { client }
This is my page routing in next where i am trying a simple GraphQL Query.
export default function PrivatePath(props: any) {
console.log("props:", props)
const { data, loading, error } = useQuery(gql`
query MyQuery {
page(id: "/min-sida", idType: URI) {
status
title
}
}
`)
console.log("data:", data)
return (
<ApolloProvider client={client}>
<AuthProvider>
<div></div>
</AuthProvider>
</ApolloProvider>
)
}
export async function getServerSideProps(context: any) {
const slugs = context.query.path[0]
const query = gql`
query MyQuery {
page(id: "/min-sida", idType: URI) {
status
title
}
}
`
const data = await client.query({ query: query })
return {
props: data
}
}
What is interesting to me is that the hook useQuery, does what is expected and when logged in delivers the page title and status.
The client.query, however, does not ever return page title and status, even when logged in it simple returns page: { null }.
My initial thought was that it was because getStatiProps in next won't be able to get the data no matter what, but it seems getServerSideProps is unable to do so as well?
Does anyone have a decent idea for solving this?

Server side react-apollo wp graphql

I'm using ReactJS server side rendering with express and react-apollo.
Everything working perfectly until I use WP acf PRO.
GraphiQL show these fields, also if I console.log it in reactjs component it shows these values in node server only not in client side. But If I remove ACF pro fields then react-apollo get everything in cient side also.
Query I use:
const TEST_DATA = gql`
query test {
page(id: "/homepage", idType: URI, asPreview: false) {
homePageCustom {
homePage {
__typename
... on Page_Homepagecustom_HomePage_SomeData {
enabled
fieldGroupName
}
}
}
}
}
`;
Component I use:
const FrontPage = (props) => {
const { loading, error, data } = useQuery(TEST_DATA, {
fetchPolicy: 'cache-and-network',
errorPolicy: "all",
variables: {},
onError: (d) => {
console.log(d);
},
onCompleted: (d) => {
console.log("onComplete", d);
},
});
if (loading) return <div>...Loading</div>;
if (error) return `Error! ${error.message}`;
console.log("data", data, error, loading);
return (
<div>
Some data here
</div>
);
};
export default FrontPage;
If I use that query then client side show data and working:
const TEST_DATA = gql`
query test {
page(id: "/homepage", idType: URI, asPreview: false) {
homePageCustom {
homePage {
__typename
... on Page_Homepagecustom_HomePage_SomeData {
__typename
}
}
}
}
}
`;
My client side settings:
const cache = new InMemoryCache();
const client = new ApolloClient({
link: createHttpLink({ uri: webConfig.siteURLGraphQL }),
cache: cache.restore(window.__APOLLO_STATE__),
});
const SSRApp = (
<ApolloProvider client={client}>
<BrowserRouter>
<CookiesProvider>
<App />
</CookiesProvider>
</BrowserRouter>
</ApolloProvider>
);
ReactDOM.hydrate(SSRApp, document.querySelector("#root"));
Its interesting that window._ APOLLO_STATE _ get these values I need.
Maybe there are some settings error in my react-apollo setup?
Im really confused and tried 3 days and nothing seems working.

Components do not re-render after calling store.writeQuery()

So I am following the graphql + apollo tutorial at https://www.howtographql.com/react-apollo/6-more-mutations-and-updating-the-store/ and I have a bunch of "Link" components that when upvoted should show the new number of votes. So far it does not re-render when upvoted and I have to refresh the page.
I try to do this by calling store.writeQuery() with the updated data. I checked and the data object I passed in is indeed different from the old:
Here's my initial set-up:
import { ApolloProvider } from "react-apollo";
import { ApolloClient } from "apollo-client";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory"
import { setContext } from "apollo-link-context";
import App from './components/App';
import {AUTH_TOKEN} from "./constants.js";
const authLink = setContext((_, { headers }) =>
{
const token = localStorage.getItem(AUTH_TOKEN);
return {
headers: {
...headers,
authorization: (token ? "Bearer " + token : ""),
}
}
});
const httpLink = createHttpLink({
uri: "http://localhost:4000"
});
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
Here's the mutation component:
<Mutation
mutation={VOTE_MUTATION}
variables={{id: this.props.link.id}}
update={(store, { data: { upVote } }) =>
{
const data = store.readQuery({ query: FEED_QUERY });
const votedLink = data.feed.links.find(link => link.id === this.props.link.id);
votedLink.votes = upVote.link.votes;
console.log(data);
store.writeQuery({
query: FEED_QUERY,
data,
});
} }
>
{
mutationCallback =>
(<div className="ml1 gray f11" style={{cursor: "pointer"}} onClick={mutationCallback}>
▲
</div>)
}
</Mutation>
Here's the query that fetches all the links:
const FEED_QUERY = gql`
{
feed
{
links
{
id url description createdAt
createdBy{
name email
}
votes{
id
}
}
}
}
`;
And I call store.readQuery() and store.writeQuery() with the expectation that the upvoted link will rerender to show the new number of votes. I also log the passed in data object to ensure it has been updated, and it has. But no rerendering. What could be wrong?
add to the apolloClient dataIdFromObject for more info here the: documentation
const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
dataIdFromObject: o => o.id
})

Apollo Subscriptions not listening to new data on client

Previously my apollo setup was listening to subscriptions until I added in socket.io and now my client setup is no longer listening to new data. my server codes seem to be ok based on my testing using graphql playground.
in my browser console, i get the following error message
client.js:652 WebSocket connection to 'ws://localhost:4000/' failed: Error during WebSocket handshake: Unexpected response code: 400
There seems to be some issue with my client side setup to use apollo subscriptions.
Appreciate any pointers? Thanks in advance
import { ApolloClient } from "apollo-client";
import { onError } from "apollo-link-error";
import { ApolloLink, split } from "apollo-link";
import { createUploadLink } from "apollo-upload-client";
import gql from "graphql-tag";
import { withClientState } from "apollo-link-state";
import { InMemoryCache } from "apollo-cache-inmemory";
import { WebSocketLink } from "apollo-link-ws";
import { getMainDefinition } from "apollo-utilities";
import { setContext } from "apollo-link-context";
const cache = new InMemoryCache();
const defaultState = {
currentGame: {
__typename: "currentGame",
teamAScore: 0,
teamBScore: 0,
teamAName: "EAGLES",
teamBName: "LOL"
}
};
const stateLink = withClientState({
cache,
defaults: defaultState,
resolvers: {
Mutation: {
updateGame: (_, { index, value }, { cache }) => {
const query = gql`
query GetCurrentGame {
currentGame #client {
teamAScore
teamBScore
teamAName
teamBName
}
}
`;
const previous = cache.readQuery({ query });
const data = {
currentGame: {
...previous.currentGame,
[index]: value
}
};
cache.writeQuery({ query, data });
return null;
},
resetCurrentGame: (_, d, { cache }) => {
cache.writeData({ data: defaultState });
}
}
}
});
const host = "http://localhost:4000";
// httpLink
const httpLink = createUploadLink({
uri: `${host}/graphql`,
credentials: "same-origin"
});
// wsLink
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/`,
options: {
reconnect: true
}
});
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const webLink = split(
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscription";
},
wsLink,
httpLink
);
// authMiddleware
const authLink = setContext(async (req, { headers }) => {
// const token = await AsyncStorage.getItem("#token");
const token = "";
return {
headers: {
...headers,
authorization: token ? `${token}` : ""
}
};
});
const errorLink = onError(({ networkError, graphQLErrors }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
}
if (networkError) console.log(`[Network error]: ${networkError}`);
});
export const client = new ApolloClient({
link: ApolloLink.from([authLink, stateLink, errorLink, webLink]),
cache
});
My server side code if required
//! Using Apollo Server Express
const app = express();
const path = "/graphql";
const schema = genSchema();
export const startServer = async () => {
const server = new ApolloServer({
schema,
context: ({ req }: any) => ({
req,
pubsub,
userLoader: userLoader()
})
});
app.use(cors());
app.use(authMiddleware);
app.use("/images", express.static("images"));
app.use(
"graphql",
graphqlUploadExpress({
uploadDir: "/",
maxFileSize: 100000000,
maxFiles: 10
}),
graphqlHTTP({ schema }) as any
);
server.applyMiddleware({ app, path });
//! Added Subscription Handler
const httpServer = createServer(app);
server.installSubscriptionHandlers(httpServer);
const port = process.env.PORT || 4000;
await createConnection();
await httpServer.listen({
port
});
console.log(`Server is running on localhost:${port}${server.graphqlPath}`);
};
Rectified. My client side apollo setup should point to ws://localhost:4000/graphql and not just ws://localhost:4000/
// wsLink
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/graphql`,
options: {
reconnect: true
}
});

Resources