【问题标题】:Configuring subscriptions with NEXT.js and Apollo client 2使用 NEXT.js 和 Apollo 客户端 2 配置订阅
【发布时间】:2019-04-28 03:34:45
【问题描述】:

我正在尝试使用 Apollo 2 和 NEXT.js 配置订阅。我可以让客户端连接到服务器,并且它们在 GraphQL 操场上工作,所以错误的配置必须在 withData 文件中,或者在处理订阅的组件中。

在 chrome 中检查网络面板上的套接字连接时,订阅有效负载不会像在 GraphQL 游乐场中那样添加为帧。

与数据:

import { ApolloLink, Observable } from 'apollo-link';
import { GRAPHQL_ENDPOINT, WS_PATH } from '../config/env';

import { ApolloClient } from 'apollo-client';
import { BatchHttpLink } from 'apollo-link-batch-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import { WebSocketLink } from 'apollo-link-ws';
import { createPersistedQueryLink } from 'apollo-link-persisted-queries';
import { onError } from 'apollo-link-error';
import withApollo from 'next-with-apollo';
import { withClientState } from 'apollo-link-state';

function createClient({ headers }) {
  const cache = new InMemoryCache();

  const request = async (operation) => {
    operation.setContext({
      http: {
        includeExtensions: true,
        includeQuery: false
      },
      headers
    });
  };

  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();
      };
    })
  );

  return new ApolloClient({
    link: ApolloLink.from([
      onError(({ graphQLErrors, networkError }) => {
        if (graphQLErrors) {
          console.log({ graphQLErrors });
        }
        if (networkError) {
          console.log('Logout user');
        }
      }),
      requestLink,
      // link,
      withClientState({
        defaults: {
          isConnected: true
        },
        resolvers: {
          Mutation: {
            updateNetworkStatus: (_, { isConnected }, { cache }) => {
              cache.writeData({ data: { isConnected } });
              return null;
            }
          }
        },
        cache
      }),
      createPersistedQueryLink().concat(
        new BatchHttpLink({
          uri: GRAPHQL_ENDPOINT,
          credentials: 'include'
        }),
        process.browser
          ? new WebSocketLink({
            uri: WS_PATH,
            options: {
              reconnect: true
            }
          })
          : null
      )
    ]),
    cache
  });
}

export default withApollo(createClient);

订阅组件:

import { CONVERSATION_QUERY } from '../../constants/queries';
import { CONVERSATION_SUBSCRIPTION } from '../../constants/subscriptions';
import PropTypes from 'prop-types';
import { Query } from 'react-apollo';

const Conversation = props => (
  <Query
    {...props}
    query={CONVERSATION_QUERY}
    variables={{ input: { _id: props._id } }}
  >
    {(payload) => {
      const more = () => payload.subscribeToMore({
        document: CONVERSATION_SUBSCRIPTION,
        variables: { input: { conversation: props._id } },
        updateQuery: (prev, { subscriptionData }) => {
          console.log({ subscriptionData });

          if (!subscriptionData.data.messageSent) return prev;

          const data = subscriptionData;

          console.log({ data });

          return Object.assign({}, prev, {});
        },
        onError(error) {
          console.log(error);
        },
        onSubscriptionData: (data) => {
          console.log('onSubscriptionData ', data);
        }
      });

      return props.children({ ...payload, more });
    }}
  </Query>
);

Conversation.propTypes = {
  children: PropTypes.func.isRequired
};

export default Conversation;

已在 GraphQL 游乐场测试过的订阅:

import gql from 'graphql-tag';

export const CONVERSATION_SUBSCRIPTION = gql`
  subscription messageSent($input: messageSentInput) {
    messageSent(input: $input) {
      _id
      users {
        _id
        profile {
          firstName
          lastName
          jobTitle
          company
          picture
        }
      }
      messages {
        _id
        body
        createdAt
        read
        sender {
          _id
          profile {
            firstName
            lastName
            jobTitle
            company
            picture
          }
        }
      }
    }
  }
`;

然后在componentDidMount中执行more函数:

componentDidMount() {
    this.props.subscribeToMore();
  }

updateQuery 中的日志在控制台中的结果是:

{"data":{"messageSent":null}}

【问题讨论】:

    标签: graphql apollo apollo-client next.js graphql-subscriptions


    【解决方案1】:

    我没有正确配置我的 withData 文件。您需要使用 apollo-link 包中的 split 来让 Apollo 确定是否应该使用 http 或 ws 处理请求。这是我的工作配置文件。

    import { ApolloLink, Observable } from 'apollo-link';
    import { ApolloClient } from 'apollo-client';
    import { BatchHttpLink } from 'apollo-link-batch-http';
    import { InMemoryCache } from 'apollo-cache-inmemory';
    import { WebSocketLink } from 'apollo-link-ws';
    import { createPersistedQueryLink } from 'apollo-link-persisted-queries';
    import { getMainDefinition } from 'apollo-utilities';
    import { onError } from 'apollo-link-error';
    import { split } from 'apollo-link';
    import withApollo from 'next-with-apollo';
    import { withClientState } from 'apollo-link-state';
    import { GRAPHQL_ENDPOINT, WS_PATH } from '../config/env';
    
    function createClient({ headers }) {
      const cache = new InMemoryCache();
    
      const request = async (operation) => {
        operation.setContext({
          http: {
            includeExtensions: true,
            includeQuery: false
          },
          headers
        });
      };
    
      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 httpLink = new BatchHttpLink({
        uri: GRAPHQL_ENDPOINT
      });
    
      // Make sure the wsLink is only created on the browser. The server doesn't have a native implemention for websockets
      const wsLink = process.browser
        ? new WebSocketLink({
          uri: WS_PATH,
          options: {
            reconnect: true
          }
        })
        : () => {
          console.log('SSR');
        };
    
      // Let Apollo figure out if the request is over ws or http
      const terminatingLink = split(
        ({ query }) => {
          const { kind, operation } = getMainDefinition(query);
          return (
            kind === 'OperationDefinition'
            && operation === 'subscription'
            && process.browser
          );
        },
        wsLink,
        httpLink
      );
    
      return new ApolloClient({
        link: ApolloLink.from([
          onError(({ graphQLErrors, networkError }) => {
            if (graphQLErrors) {
              console.error({ graphQLErrors });
            }
            if (networkError) {
              console.error({ networkError});
            }
          }),
          requestLink,
          // link,
          withClientState({
            defaults: {
              isConnected: true
            },
            resolvers: {
              Mutation: {
                updateNetworkStatus: (_, { isConnected }, { cache }) => {
                  cache.writeData({ data: { isConnected } });
                  return null;
                }
              }
            },
            cache
          }),
    
          // Push the links into the Apollo client
          createPersistedQueryLink().concat(
            // New config
            terminatingLink
            // Old config
            // new BatchHttpLink({
            //   uri: GRAPHQL_ENDPOINT,
            //   credentials: 'include'
            // })
          )
        ]),
        cache
      });
    }
    
    export default withApollo(createClient);
    

    【讨论】:

      猜你喜欢
      • 2019-02-02
      • 2017-12-25
      • 2017-12-01
      • 2022-12-12
      • 2021-04-21
      • 2018-12-30
      • 2020-01-26
      • 2020-11-20
      • 2018-07-08
      相关资源
      最近更新 更多