如何使用 Apollo GraphQL 获取订阅?

How to get subscriptions working with Apollo GraphQL?

我要 post 编写大量上下文代码。我还没有看到同时显示客户端和服务器的教程,所以我正在尝试将它们拼凑在一起。

代码

客户

网络接口

const subscriptionClient = new SubscriptionClient(`ws://localhost:8031/`, {
    reconnect: true
});

const networkInterface = createNetworkInterface({
    uri: 'http://localhost:8030/graphql'
});

const subscriptionInterface = addGraphQLSubscriptions(networkInterface, subscriptionClient);

const apolloClient = new ApolloClient({
    networkInterface: subscriptionInterface
});

组件

const SUBSCRIPTION_QUERY = gql`
    subscription onBranchUpdated($id: UUID!) {
        branchUpdated(id: $id) {
            browser
            crawlId
            startDate
            endDate
            baseUrl
            totalPages
            currentPage
        }
    }
`;


class BranchPage extends React.PureComponent {

    render() {
        *snip*
    }

    componentDidMount() {
        this.props.data.subscribeToMore({
            document: SUBSCRIPTION_QUERY,
            variables: {
                id: this.props.match.params.id,
            },
            updateQuery: (prevResult, {subscriptionData}) => {
                log('updateQuery',prevResult,subscriptionData);
            }
        });
    }
}

export default graphql(gql`
    query getBranch($id: UUID!) {
        branch(id: $id) {
            id
            browser
            crawlId
            startDate
            endDate
            baseUrl
            totalPages
            currentPage
        }
    }
`, {
    options: ({match: {params}}) => ({
        variables: {
            id: params.id,
        }
    })
})(BranchPage);

服务器

graphql.schema.graphqls

schema {
    query: Query
    mutation: Mutation
    subscription: Subscription
}

type Branch {
    id: UUID!
    *snip*
}

type Subscription {
    branchUpdated(id: UUID!): Branch
}

解析器

const resolverMap = {
    Query: {
        async branch(obj, {id}, context, info) {
            *snip*
            return branch;
        },
    },
    Mutation: {
        async updateBranch(_, req) {
            let {id, ...updates} = req;
            let branch = await db::queryOne("select * from Branches where id=?", [id]);
            updates = getUpdates(req);
            Object.assign(branch, updates);
            await db::mutate("update Branches set ? where id=?", [updates, id]);
            pubSub.publish(topics.BRANCH_UPDATED, {branchUpdated: branch});
            return branch;
        },
    },
    Subscription: {
        branchUpdated: {
            resolve: (payload, args, context, info) => {
                log('RESSSSSSSSSSSSSSSSoLVE',payload,args);
                return payload;
            },
            subscribe: withFilter(() => pubSub.asyncIterator(topics.BRANCH_UPDATED), (payload, variables) => {
                log('FILTERRRRRRRRRRRRRRRRRRRRRRR');
                return payload.branchUpdated.id === variables.id;
            }),
        },
    },
};

发布订阅

import { PubSub } from 'graphql-subscriptions';

const pubSub = new PubSub();
export default pubSub;

WebSocket 服务器

const wsServer = createServer((request, response) => {
    response.writeHead(404);
    response.end();
});

wsServer.listen(WS_PORT, () => {
    console.log(`Websocket server: http://localhost:${WS_PORT}`)
});

const subscriptionServer = SubscriptionServer.create(
    { 
        schema,
        execute,
        subscribe,
    },
    {
        server: wsServer,
        path: '/',
    }
);

问题

当我使用此组件加载页面时,它使用网络接口加载数据并正确呈现。之后,我希望组件通过 websocket 订阅更新以保持最新。这就是我遇到的问题。

在 Chrome 开发工具中,我可以看到 "updateQuery" 立即被击中,尽管我不确定为什么。此时什么都没有'published'。 subscriptionData 结果为:

{
  data: {
    branchUpdated: null
  }
}

在服务器上,我可以看到原因。在我的resolverMap中,Subscription.branchUpdated.resolve立即被击中。 payloadundefined,可能又是因为 我还没有发布任何东西Subscription.branchUpdated.subscribe 永远不会执行。

当我 发布我的 "branch" 更新时,没有任何反应。 branchUpdatedsubscribe 都没有被调用,即使我调用了 pubSub.publish。没有任何东西返回给客户。

所以我的问题是:

  1. 如何让我的订阅触发,直到我真正发布了一些东西
  2. 更重要的是,当我实际 发布内容时,如何让它们 触发?

终于想通了。有一些问题。

  1. graphql@0.9.6 没有 {subscribe},您至少需要 v0.10
  2. 这是我的实施特有的问题,但我的 withFilter 是错误的。 branch.id 是一个缓冲区——我需要使用 .equals 来比较它们
  3. 我更新了componentDidMount:

    componentDidMount() {
        this.props.data.subscribeToMore({
            document: SUBSCRIPTION_QUERY,
            variables: {
                id: this.props.match.params.id,
            },
            updateQuery: (prevResult, {subscriptionData}) => {
                log('updateQuery',prevResult,subscriptionData.data.branchUpdated);
                return {branch: subscriptionData.data.branchUpdated};
            }
        });
    }