1

I would like to set up a graphql client with React for both uploading file and handle subscriptions from a graphql server. The file upload and the other queries work well. The problem is with subscriptions. I get in the browser console the following error: WebSocket connection to 'ws://localhost:3001/subscriptions' failed: Connection closed before receiving a handshake response

I have used apollo-upload-client for file upload and apollo-link-ws for subscriptions.

I can see that subscriptions-transport-ws suggests using createNetworkInterface and addGraphQLSubscriptions but this approach is not compatible with apollo-upload-client that only supports createUploadLink.

I'm stuck. Please help.

I setup my client like this:

import React from 'react';
import ApolloClient from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { createUploadLink } from 'apollo-upload-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloLink, Observable, split } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';


const cache = new InMemoryCache();

const request = async (operation) => {
    const token = localStorage.getItem('token');
    operation.setContext({
        headers: {
            authorization: token ? `Bearer ${token}` : '',
        },
    });
};

const httpLink = createUploadLink({ uri: 'http://localhost:3001/graphql' });

// Create a WebSocket link:
const wsLink = new WebSocketLink({
    uri: 'ws://localhost:3001/subscriptions',
    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 link = split(
    // split based on operation type
    ({ query }) => {
        const { kind, operation } = getMainDefinition(query);
        return kind === 'OperationDefinition' && operation === 'subscription';
    },
    wsLink,
    httpLink,
);


const requestLink = new ApolloLink((operation, forward) =>
    new Observable((observer) => {
        let handle;
        Promise.resolve(operation)
            .then(oper => request(oper))
            .then(() => {
                handle = forward(operation).subscribe({
                    next: observer.next.bind(observer),
                    error: observer.error.bind(observer),
                    complete: observer.complete.bind(observer),
                });
            })
            .catch(observer.error.bind(observer));

        return () => {
            if (handle) handle.unsubscribe();
        };
    }));

const apolloClient = new ApolloClient({
    link: ApolloLink.from([
        requestLink,
        link,
    ]),
    cache,
});

export const withApolloClient = App => (
    <ApolloProvider client={apolloClient}>
        <App client={apolloClient} />
    </ApolloProvider>
);

export default apolloClient;
Federico Bellucci
  • 623
  • 2
  • 7
  • 26

3 Answers3

0

I am using a similar config but instead of importing WebSocketLink from apollo-link-ws I imported it from @apollo/client.

With that setup i had both the subscription and upload working.

import { WebSocketLink } from "@apollo/client/link/ws";

0

Use the uploadLink created via createUploadLink in the split:

const uploadLink = createUploadLink({
  uri: 'http://localhost:3001/graphql',
});

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

Explanation: The uploadLink already checks whether the GraphQL query contains a file upload. If not, it will do a normal fetch.

See the apollo-upload-client docs:

Creates a terminating Apollo Link for Apollo Client that fetches a GraphQL multipart request if the GraphQL variables contain files (by default FileList, File, Blob, or ReactNativeFile instances), or else fetches a regular GraphQL POST or GET request (depending on the config and GraphQL operation).

Andru
  • 5,954
  • 3
  • 39
  • 56
-2

I would suggest to use graphql-server-express like this

chintan adatiya
  • 1,233
  • 14
  • 17