10

I'm trying to set up a graphcool subscription / websockets as per this tutorial at How To GraphQL but I'm getting the following message:

WebSocket connection to 'wss://subscriptions.graph.cool/v1/###' failed: WebSocket is closed before the connection is established.

I'm seem to have everything as per the tutorial. Do you have any idea why the websockets connection is not being established?


index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
import registerServiceWorker from './registerServiceWorker'
import './styles/index.css'
import { ApolloProvider, createNetworkInterface, ApolloClient } from 'react-apollo'
import { SubscriptionClient, addGraphQLSubscriptions } from 'subscriptions-transport-ws'
import { BrowserRouter } from 'react-router-dom'
import { GC_AUTH_TOKEN } from './constants'

const networkInterface = createNetworkInterface({
  uri: 'https://api.graph.cool/simple/v1/###'
})

const wsClient = new SubscriptionClient('wss://subscriptions.graph.cool/v1/###', {
  reconnect: true,
  connectionParams: {
     authToken: localStorage.getItem(GC_AUTH_TOKEN),
  }
})

const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(
  networkInterface,
  wsClient
)

networkInterface.use([{
  applyMiddleware(req, next) {
    if (!req.options.headers) {
      req.options.headers = {}
    }
    const token = localStorage.getItem(GC_AUTH_TOKEN)
    req.options.headers.authorization = token ? `Bearer ${token}` : null
    next()
  }
}])

const client = new ApolloClient({
  networkInterface: networkInterfaceWithSubscriptions
})

ReactDOM.render(
  <BrowserRouter>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </BrowserRouter>
  , document.getElementById('root')
)
registerServiceWorker()

App.js

import React, { Component } from 'react'
import LinkList from './LinkList'
import CreateLink from './CreateLink'
import Header from './Header'
import Login from './Login'
import Search from './Search'
import { Switch, Route, Redirect } from 'react-router-dom'

class App extends Component {
  render() {
    return (
      <div className='center w85'>
        <Header />
        <div className='ph3 pv1 background-gray'>
          <Switch>
            <Route exact path='/search' component={Search}/>
            <Route exact path='/' component={LinkList}/>
            <Route exact path='/create' component={CreateLink}/>
            <Route exact path='/login' component={Login}/>
          </Switch>
        </div>
      </div>
    )
  }
}

export default App

LinkList.js

import React, { Component } from 'react'
import Link from './Link'
import { graphql, gql } from 'react-apollo'

class LinkList extends Component {

  _updateCacheAfterVote = (store, createVote, linkId) => {
    const data = store.readQuery({ query: ALL_LINKS_QUERY })

    const votedLink = data.allLinks.find(link => link.id === linkId)
    votedLink.votes = createVote.link.votes

    store.writeQuery({ query: ALL_LINKS_QUERY, data })
  }

  componentDidMount() {
    this._subscribeToNewLinks()
    this._subscribeToNewVotes()
  }


  render() {

    if (this.props.allLinksQuery && this.props.allLinksQuery.loading) {
      return <div>Loading</div>
    }

    if (this.props.allLinksQuery && this.props.allLinksQuery.error) {
      return <div>Error</div>
    }

    const linksToRender = this.props.allLinksQuery.allLinks

    return (
      <div>
      {linksToRender.map((link, index) => (
        <Link key={link.id} updateStoreAfterVote={this._updateCacheAfterVote}  index={index} link={link}/>
      ))}
      </div>
    )
  }

  _subscribeToNewLinks = () => {
    this.props.allLinksQuery.subscribeToMore({
      document: gql`
        subscription {
          Link(filter: {
            mutation_in: [CREATED]
          }) {
            node {
              id
              url
              description
              createdAt
              postedBy {
                id
                name
              }
              votes {
                id
                user {
                  id
                }
              }
            }
          }
        }
      `,
      updateQuery: (previous, { subscriptionData }) => {
        const newAllLinks = [
          subscriptionData.data.Link.node,
          ...previous.allLinks
        ]
        const result = {
          ...previous,
          allLinks: newAllLinks
        }
        return result
      }
    })
  }

  _subscribeToNewVotes = () => {
    this.props.allLinksQuery.subscribeToMore({
      document: gql`
        subscription {
          Vote(filter: {
            mutation_in: [CREATED]
          }) {
            node {
              id
              link {
                id
                url
                description
                createdAt
                postedBy {
                  id
                  name
                }
                votes {
                  id
                  user {
                    id
                  }
                }
              }
              user {
                id
              }
            }
          }
        }
      `,
      updateQuery: (previous, { subscriptionData }) => {
        const votedLinkIndex = previous.allLinks.findIndex(link => link.id === subscriptionData.data.Vote.node.link.id)
        const link = subscriptionData.data.Vote.node.link
        const newAllLinks = previous.allLinks.slice()
        newAllLinks[votedLinkIndex] = link
        const result = {
          ...previous,
          allLinks: newAllLinks
        }
        return result
      }
    })
  }

}

export const ALL_LINKS_QUERY = gql`
  query AllLinksQuery {
    allLinks {
      id
      createdAt
      url
      description
      postedBy {
        id
        name
      }
      votes {
        id
        user {
          id
        }
      }
    }
  }
`

export default graphql(ALL_LINKS_QUERY, { name: 'allLinksQuery' }) (LinkList)
Ryan King
  • 3,538
  • 12
  • 48
  • 72
  • I guess the error is on the server. How is the connection established? – Locco0_0 Jul 31 '17 at 06:39
  • I'm not entirely sure. How do I figure that out? I just run yarn start & apollo does it's magic based on index.js above and graphcool handles server side. All I really have is the endpoint they've provided. – Ryan King Jul 31 '17 at 06:50
  • Is the `` correct, which you get from the graph.cool endpoint. In the tutorial it is under the point "Now update the configuration code like so:" – Locco0_0 Jul 31 '17 at 08:09
  • Yes triple checked it. – Ryan King Jul 31 '17 at 08:14
  • ok, you could add the `connectionCallback: (error) => {}` in the SubscriptionClient. Maybe it gives you a better error. Else i do not know how to fix this – Locco0_0 Jul 31 '17 at 08:25
  • Nope, nothing. Even tried setting up a new graph.cool project. Thanks though. – Ryan King Jul 31 '17 at 08:49

3 Answers3

6

Can you add the timeout parameter to your client configuration like this:

const wsClient = new SubscriptionClient('wss://subscriptions.graph.cool/v1/###', {
  reconnect: true,
  timeout: 30000,
  connectionParams: {
     authToken: localStorage.getItem(GC_AUTH_TOKEN),
  }
})

There's a slight mismatch between the subscription-transport-ws client implementation and the Graphcool subscription server implementation how keep-alives are handled, and the long timeout period fixes it.

Read this issue on GitHub and this Graphcool feature request for more background information.

marktani
  • 7,578
  • 6
  • 37
  • 60
3

Turns out the websocket endpoint was incorrect and is different for the Asia Pacific region wss://subscriptions.ap-northeast-1.graph.cool/v1/###

Ryan King
  • 3,538
  • 12
  • 48
  • 72
  • It seems there is a bug of graphql-playground. I had submitted an issue to https://github.com/graphcool/graphql-playground/issues/591 – Huan Mar 06 '18 at 17:33
0

In my case, I was using wss://domain.com/graphql whereas my websocket server was not secure so I had to change it to ws://domain.com/graphql

Srushti Shah
  • 810
  • 3
  • 17