Apollo / Graphcool Subscriptions - WebSocket is closed before the connection is established
Asked Answered
M

4

11

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)
Micrometer answered 30/7, 2017 at 12:18 Comment(6)
I guess the error is on the server. How is the connection established?Roundfaced
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.Micrometer
Is the <project-id> correct, which you get from the graph.cool endpoint. In the tutorial it is under the point "Now update the configuration code like so:"Roundfaced
Yes triple checked it.Micrometer
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 thisRoundfaced
Nope, nothing. Even tried setting up a new graph.cool project. Thanks though.Micrometer
M
3

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

Micrometer answered 1/8, 2017 at 11:34 Comment(1)
It seems there is a bug of graphql-playground. I had submitted an issue to github.com/graphcool/graphql-playground/issues/591Heywood
B
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.

Bosket answered 31/7, 2017 at 9:16 Comment(4)
Still the same issue unfortunately.Micrometer
what's your version of subscription-transport-ws?Bosket
[email protected]Micrometer
Thanks, looks like 0.8.2 is required as per your conversation on GitHubBosket
M
3

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

Micrometer answered 1/8, 2017 at 11:34 Comment(1)
It seems there is a bug of graphql-playground. I had submitted an issue to github.com/graphcool/graphql-playground/issues/591Heywood
I
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

Ignescent answered 4/8, 2023 at 13:6 Comment(0)
J
0

I was able to fix mine using the lazy: true option.

reconnect: true,
lazy: true, // <--- This
connectionParams: {
  // ...
},

Source

Jocundity answered 24/11, 2023 at 0:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.