6

It seems like I have my server set up according to the Apollo docs at http://dev.apollodata.com/tools/apollo-server/setup.html. In my server/main.js file:

//SET UP APOLLO INCLUDING APOLLO PUBSUB
const executableSchema = makeExecutableSchema({
    typeDefs: Schema,
    resolvers: Resolvers,
    connectors: Connectors,
    logger: console,
});

const GRAPHQL_PORT = 8080;
const graphQLServer = express();

// `context` must be an object and can't be undefined when using connectors
graphQLServer.use('/graphql', bodyParser.json(), apolloExpress({
    schema: executableSchema,
    context: {}, //at least(!) an empty object
}));

graphQLServer.use('/graphiql', graphiqlExpress({
    endpointURL: '/graphql',
}));

graphQLServer.listen(GRAPHQL_PORT, () => console.log(
    `GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}/graphql`
));
//SET UP APOLLO INCLUDING APOLLO PUBSUB

It prints out "GraphQL Server is now running on http://localhost:8080/graphql" to the terminal log indicating that the server was successfully initialized.

But at the top of my main_layout component, when I run this code:

import { Client } from 'subscriptions-transport-ws';
const wsClient = new Client('ws://localhost:8080');

...I get this console message:

WebSocket connection to 'ws://localhost:8080/' failed: Connection closed before receiving a handshake response

What am I missing?

VikR
  • 4,818
  • 8
  • 51
  • 96

4 Answers4

4

You need to create a dedicated websocket server. It will run on a different port and the code to set it up is provided on the subscriptions-transport-ws package.

Take a look on the following code from GitHunt-API example: https://github.com/apollostack/GitHunt-API/blob/master/api/index.js#L101-L134

Also you would see that this code is dependent on a class called SubscriptionManager. It is a class from a package called graphql-subscriptions also by the apollo team, and you can find an example of how to use it here: https://github.com/apollostack/GitHunt-API/blob/master/api/subscriptions.js

davidyaha
  • 1,940
  • 1
  • 12
  • 5
  • The code you highlight in GitHunt-API/api/index.js, does not reference the schema, and the Apollo docs say the schema must be referenced in setting up the server. The schema is referenced above the highlighted code, on line 66, but that's for a listener on a different port that seems to be mostly concerned with the GitHub API. Would it be correct to say I need to modify the port 3010 code so as to omit references to GitHub? – VikR Sep 30 '16 at 00:29
  • If you want to setup the subscriptions (as they are right now) you need a schema object that will be given to the SubscriptionManager constructor (which I've pointed in the last part of my answer) and that, in it's turn, will be given to the Server class from the `subscriptions-transport-ws` package. The reference above it is related to the GraphQL POST endpoint which only serves mutations and queries. After all this configuration you would be left with two servers on different ports, one for GraphQL endpoint and one for websocket to to push events to the client. – davidyaha Sep 30 '16 at 07:20
  • A dedicated websocket port is not required - see my answer. – antirealm Nov 14 '17 at 02:27
3

TL;DR: You can use graphql-up to quickly get a GraphQL server with subscriptions support up and ready. Here's a more detailed tutorial on using this in combination with Apollo and the websocket client subscriptions-transport-ws.

Obtain a GraphQL Server with one click

Let's say you want to build a Twitter clone based on this GraphQL Schema in IDL syntax:

type Tweet {
  id: ID!
  title: String!
  author: User! @relation(name: "Tweets")
}

type User {
  id: ID!
  name: String!
  tweets: [Tweet!]! @relation(name: "Tweets")
}

graphql-up

Click this button to receive your own GraphQL API and then open the Playground, where you can add some tweets, query all tweets and also test out subscriptions.

Simple to use API

First, let's create a user that will be the author for all coming tweets. Run this mutation in the Playground:

mutation createUser {
  createUser(name: "Tweety") {
    id # copy this id for future mutations!
  }
}

Here's how you query all tweets and their authors stored at your GraphQL server:

query allTweets {
  allTweets {
    id
    title
    createdAt
    author {
      id
      name
    }
  }
}

Subscription support using websockets

Let's now subscribe to new tweets from "Tweety". This is the syntax:

subscription createdTweets {
  Message(filter: {
    mutation_in: [CREATED]
    node: {
      author: {
        name: "Tweety"
      }
    }
  }) {
    node {
      id
      text
      createdAt
      sentBy {
        id
        name
      }
    }
  }
}

Now create a new tab in the Playground and create a new Tweet:

mutation createTweet {
  createTweet(
    title: "#GraphQL Subscriptions are awesome!"
    authorId: "<id-from-above>"
  ) {
    id
  }
}

You should see a new event popping up in your other tab where you subscribed before.

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

Here is a demo about using Apollo GraphQL, React & Hapi: https://github.com/evolastech/todo-react. It's less overwhelmed than GitHunt-React & GitHunt-API

anhldbk
  • 4,559
  • 5
  • 29
  • 37
2

Seems like you aren't actually making the websocket server. use SubscriptionServer. Keep in mind that it is absolutely NOT true that you have to have a dedicated websocket port (I thought this once too) as davidyaha says. I have both my normal queries and subs on the same port.

import { createServer } from 'http';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { execute, subscribe } from 'graphql';
import { schema } from './my-schema';

// All your graphQLServer.use() etc setup goes here, MINUS the graphQLServer.listen(),
// you'll do that with websocketServer:

// Create WebSocket listener server
const websocketServer = createServer(graphQLServer);

// Bind it to port and start listening
websocketServer.listen(3000, () => console.log(
  `Server is now running on http://localhost:3000`
));

const subscriptionServer = SubscriptionServer.create(
  {
    schema,
    execute,
    subscribe,
  },
  {
    server: websocketServer,
    path: '/subscriptions',
  },
);
antirealm
  • 408
  • 3
  • 10