Apollo-link: Having trouble with apollo-link-ws

Created on 24 Jan 2018  路  1Comment  路  Source: apollographql/apollo-link

I have subscriptions working fine by themselves in a Chat service at: http://localhost:3002/graphql, ws://localhost:3002/subscriptions.

I am trying to build a Gateway server so Gateway => Chat Service (and many other microservices).
_(By the way, is this schema stitching Gateway stuff supposed to go in the Server or the Client? Right now I have in its own Node.js server.)_

import { makeRemoteExecutableSchema, introspectSchema, mergeSchemas } from 'graphql-tools';
import fetch from 'node-fetch';
import { HttpLink } from 'apollo-link-http';
import { ApolloLink } from 'apollo-link';
import { setContext } from 'apollo-link-context';
import { WebSocketLink } from 'apollo-link-ws';
import ws from 'ws';

async function makeSchema() {
  const ContextLink = setContext((request, previousContext) => ({
    headers: {
      authorization: previousContext.graphqlContext.headers.authorization,
    },
  }));
  const AuthLink = new HttpLink({
    uri: 'http://localhost:3001/graphql',
    fetch,
  });
  const AuthSchema = makeRemoteExecutableSchema({
    schema: await introspectSchema(AuthLink),
    link: ApolloLink.from([ContextLink, AuthLink]),
  });

  const ChatLink = new HttpLink({
    uri: 'http://localhost:3002/graphql',
    fetch,
  });
  const SubscriptionLink = new WebSocketLink({
    uri: 'ws://localhost:3002/subscriptions',
    options: {
      reconnect: true,
    },
    webSocketImpl: ws,
  });
  const ChatSchema = makeRemoteExecutableSchema({
    schema: await introspectSchema(ChatLink),
    link: SubscriptionLink,  // SubscriptionLink is terminating, so no ChatLink here?
  });

  const mergedSchema = mergeSchemas({
    schemas: [AuthSchema, ChatSchema],
  });

  return mergedSchema;
}

export const schema = makeSchema();

After setting link: SubscriptionLink, queries/mutations/subscriptions to localhost:3002 fail because context is lost and becomes {}. The queries/mutations worked when link: ChatLink, and the subscriptions only work when I hit ws://localhost:3002/subscriptions directly without stitching/links.

What am I doing wrong?


EDIT: Also tried this and no luck:

  const ChatLink = ApolloLink.split(
    ({ query: { definitions } }) => definitions.some(({ kind, operation }) => kind === 'OperationDefinition' && operation === 'subscription'),
    new WebSocketLink({
      uri: `ws://localhost:3002/subscriptions`,
      options: {
        reconnect: true,
      },
      webSocketImpl: ws,
    }),
    new HttpLink({
      uri: 'http://localhost:3002/graphql',
      fetch,
    })
  );

  const ChatSchema = makeRemoteExecutableSchema({
    schema: await introspectSchema(ChatLink),
    link: ChatLink,
  });
question

Most helpful comment

Hi @booboothefool,

You should look into from and split from apollo-link to create your link. Maybe the sample bellow will help you figure this out :

import { from, split } from 'apollo-link';
import { setContext } from 'apollo-link-context';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { importSchema } from 'graphql-import';
import { makeRemoteExecutableSchema } from 'graphql-tools';
import fetch from 'node-fetch';
import path from 'path';
import WebSocket from 'ws';

export default async (httpUri, wsUri, filename) => {
  // 1. Create Apollo Link that's connected to the underlying GraphQL API
  const httpLink = new HttpLink({ uri: httpUri, fetch });

  // Auth link
  const httpAuthLink = setContext((request, previousContext) => ({
    headers: {
      authorization: previousContext.graphqlContext.headers.authorization,
    },
  }));

  // WebSocket link
  const wsLink = new WebSocketLink({
    uri: wsUri,
    options: {
      reconnect: true,
    },
    webSocketImpl: WebSocket,
  });

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

  // 2. Retrieve schema definition of the underlying GraphQL API
  const rootPath = path.join.bind(this, __dirname, './');
  // Works with introspectSchema but be careful of performances issues
  const schema = importSchema(rootPath(`./schemas/${filename}`));

  // 3. Create the executable schema based on schema definition and Apollo Fetch
  const databaseServiceExecutableSchema = makeRemoteExecutableSchema({
    schema,
    link,
  });

  return databaseServiceExecutableSchema;
};

Remote schemas stitching with subscriptions is a little tricky but very powerful.

>All comments

Hi @booboothefool,

You should look into from and split from apollo-link to create your link. Maybe the sample bellow will help you figure this out :

import { from, split } from 'apollo-link';
import { setContext } from 'apollo-link-context';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { importSchema } from 'graphql-import';
import { makeRemoteExecutableSchema } from 'graphql-tools';
import fetch from 'node-fetch';
import path from 'path';
import WebSocket from 'ws';

export default async (httpUri, wsUri, filename) => {
  // 1. Create Apollo Link that's connected to the underlying GraphQL API
  const httpLink = new HttpLink({ uri: httpUri, fetch });

  // Auth link
  const httpAuthLink = setContext((request, previousContext) => ({
    headers: {
      authorization: previousContext.graphqlContext.headers.authorization,
    },
  }));

  // WebSocket link
  const wsLink = new WebSocketLink({
    uri: wsUri,
    options: {
      reconnect: true,
    },
    webSocketImpl: WebSocket,
  });

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

  // 2. Retrieve schema definition of the underlying GraphQL API
  const rootPath = path.join.bind(this, __dirname, './');
  // Works with introspectSchema but be careful of performances issues
  const schema = importSchema(rootPath(`./schemas/${filename}`));

  // 3. Create the executable schema based on schema definition and Apollo Fetch
  const databaseServiceExecutableSchema = makeRemoteExecutableSchema({
    schema,
    link,
  });

  return databaseServiceExecutableSchema;
};

Remote schemas stitching with subscriptions is a little tricky but very powerful.

Was this page helpful?
0 / 5 - 0 ratings