Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apollo Subscriptions: Apollo Graphql is receiving updates on Playground but not on client

Tags:

I'm using react on Apollo GraphQL subscriptions and I can receive updates on Apollo Playground but not on Client. Here is the response on the Apollo Playground:

enter image description here

Graphql Server is on http://localhost:4000/ and subscriptions on ws://localhost:4000/graphql. However, it works on the playground but not on client-side. I have set up Apollo client in this manner to receive updates from server:

import ApolloClient from 'apollo-boost';
import { WebSocketLink } from 'apollo-link-ws';
import { HttpLink } from 'apollo-link-http';
import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';

const httpLink = new HttpLink({
  uri: 'http://localhost:4000/graphql'
});

export const wsLink = new WebSocketLink({
  uri: `ws://localhost:4000/graphql`,
  options: {
    reconnect: false
  }
});

export const link = split(
  // split based on operation type
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);



export const client = new ApolloClient({
  uri: 'http://localhost:4000/',
});

In my view I have used useSubscriptions:

const MESSAGE_SENT_SUBSCRIPTION =  gql`subscription {
  messageSent {
    id
    message
  }
}`
const {data: newMessage, loading: newMessageLoading} = useSubscription(MESSAGE_SENT_SUBSCRIPTION, {});

And on render, I have used:

{!newMessageLoading && JSON.stringify(newMessage)}

But from client, it doesn't receive updates but I am sure that it connects with Graphql WebSockets server.

oc

Server Side:

let database = require("./src/database.js")
let schema = require("./src/schema.js");
let resolvers = require("./src/resolvers.js");
let {ApolloServer} = require("apollo-server");

// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({ 
  typeDefs: schema, 
  resolvers: resolvers,
  context: {
    database
  }
});

// The `listen` method launches a web server.
server.listen().then(({ url,subscriptionsUrl ,subscriptionsPath}) => {
  console.log(`🚀  Server ready at ${url}`);
  console.log(`realtime here at ${subscriptionsUrl} and path ${subscriptionsPath}`)
});

What I'm doing wrong here, Is there anyone who came across with such issue?

like image 512
Ilyas karim Avatar asked Oct 08 '19 12:10

Ilyas karim


People also ask

Does Apollo Federation support subscriptions?

Subscriptions are not currently supported in Apollo Federation. This article uses the graphql-ws library to add support for subscriptions to Apollo Server 4. We no longer recommend using the previously documented subscriptions-transport-ws , because this library is not actively maintained.

Which transport does Apollo use to implement subscriptions?

Because subscriptions usually maintain a persistent connection, they shouldn't use the default HTTP transport that Apollo Client uses for queries and mutations. Instead, Apollo Client subscriptions most commonly communicate over WebSocket, via the graphql-ws library.

How do GraphQL subscriptions work?

Subscriptions are a GraphQL feature that allows a server to send data to its clients when a specific event happens. Subscriptions are usually implemented with WebSockets. In that setup, the server maintains a steady connection to its subscribed client.

How do you update the Apollo Client cache after a mutation?

If a mutation updates a single existing entity, Apollo Client can automatically update that entity's value in its cache when the mutation returns. To do so, the mutation must return the id of the modified entity, along with the values of the fields that were modified.


1 Answers

You need to pass splited link to ApolloClient constructor. Try to pass it like this (client side):

import ApolloClient from 'apollo-boost';
import { WebSocketLink } from 'apollo-link-ws';
import { HttpLink } from 'apollo-link-http';
import { split } from 'apollo-link';
import { onError } from 'apollo-link-error';
import { getMainDefinition } from 'apollo-utilities';

const httpLink = new HttpLink({
  uri: 'http://localhost:4000/graphql'
});

export const wsLink = new WebSocketLink({
  uri: `ws://localhost:4000/subscriptions`,
  options: {
    reconnect: false
  }
});

export const link = split(
  // split based on operation type
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

export const graphqlServer = new ApolloClient({
    link: ApolloLink.from([
        onError(({
            graphQLErrors,
            networkError
        }) => {
            if (graphQLErrors) {
                graphQLErrors.map(({
                        message,
                        locations,
                        path
                    }) =>
                    console.log(
                        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
                    )
                );
            }
            if (networkError) {
                console.log(`[Network error]: ${networkError}`);
            }
        }),
        link // YOUR LINK (NOW MATCHING YOUR CODE)
    ])
});

And server side:

...
const server = new ApolloServer({ 
  typeDefs: schema, 
  resolvers: resolvers,
  subscriptions: {
        path: '/subscriptions'
  },
  context: {
    database
  }
});
...

Note that /subscriptions also passed to ApolloClient

like image 80
Artemiy Vereshchinskiy Avatar answered Oct 20 '22 13:10

Artemiy Vereshchinskiy