GraphQL Subscriptions 是 GraphQL 规范的一部分,它允许客户端订阅服务器上的实时数据变化。当服务器端的数据发生变化时,订阅的客户端会收到实时更新。
以 Hasura 为例,Hasura 是一个流行的 GraphQL 引擎,支持实时数据流。
Hasura实时数据流
首先,你需要在 Hasura 控制台上启用 GraphQL Subscriptions
功能,并创建一个订阅。假设我们有一个 messages 表,并且想要在有新消息时通知客户端:
subscription NewMessage {
messages(where: { created_at: { _gt: NOW() } }) {
id
content
user {
name
}
}
}
这个订阅会监听 messages
表中创建时间大于当前时间的消息。
在客户端,你可以使用 Apollo Client 或其他 GraphQL 客户端库来处理订阅。以下是一个使用 Apollo Client 的示例:
import { useSubscription } from '@apollo/client';
import gql from 'graphql-tag';
const NEW_MESSAGE_SUBSCRIPTION = gql`
subscription NewMessage {
messages(where: { created_at: { _gt: NOW() } }) {
id
content
user {
name
}
}
}
`;
function MessageFeed() {
useSubscription(NEW_MESSAGE_SUBSCRIPTION, {
onSubscriptionData: ({ subscriptionData }) => {
console.log('New message:', subscriptionData.data.messages);
// 更新 UI 或存储新消息
},
errorPolicy: 'all', // 处理错误
});
// 渲染 UI 逻辑
...
}
useSubscription
是 Apollo Client 提供的 Hook,它会处理订阅的生命周期。当有新的订阅数据到来时,onSubscriptionData 回调会被调用,你可以在这里处理新数据,比如更新 UI。
注意,为了使订阅工作,你需要配置 Apollo Client 以连接到 Hasura 的 WebSocket endpoint,通常以 /v1/graphql
结尾,例如:
import { ApolloClient, InMemoryCache, createHttpLink, split } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { WebSocketLink } from '@apollo/client/link/ws';
const httpLink = createHttpLink({
uri: 'http://localhost:8080/v1/graphql',
});
const wsLink = new WebSocketLink({
uri: 'ws://localhost:8080/v1/graphql',
options: {
reconnect: true,
},
});
const link = split(
// 使用 `getMainDefinition` 来区分普通查询和订阅
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
这样,客户端就可以接收来自 Hasura 的实时消息更新了。请注意,实际部署时,你需要替换 localhost 和端口号为你的 Hasura 实例的实际地址和端口。
自定义 Subscription 服务端
如果你不使用像 Hasura 这样的现成解决方案,而是在自己的服务器上实现 GraphQL Subscriptions,你可能需要使用 GraphQL 框架,如 Apollo Server 或 GraphQL Yoga,来设置 WebSocket 连接。
安装必要的依赖
首先,确保安装了 apollo-server-express
和 subscriptions-transport-ws
:
npm install apollo-server-express subscriptions-transport-ws graphql
设置服务器
接下来,创建一个简单的 Apollo Server,并配置 Subscription:
const { ApolloServer, gql } = require('apollo-server-express');
const { createServer } = require('http');
const { SubscriptionServer } = require('subscriptions-transport-ws');
const express = require('express');
// 定义 GraphQL Schema
const typeDefs = gql`
type Query {
hello: String
}
type Subscription {
newMessage: Message
}
type Message {
id: ID!
content: String!
createdAt: String!
}
`;
// 实现 resolver
const messages = [];
let nextMessageId = 1;
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
Subscription: {
newMessage: {
subscribe: () => pubsub.asyncIterator('NEW_MESSAGE'),
},
},
};
// 使用 PubSub 发布订阅模式
const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();
// 模拟新消息的发布
setInterval(() => {
const content = `Message ${nextMessageId}`;
messages.push({ id: nextMessageId++, content, createdAt: new Date().toISOString() });
pubsub.publish('NEW_MESSAGE', { newMessage: messages[messages.length - 1] });
}, 5000);
const server = new ApolloServer({ typeDefs, resolvers });
const app = express();
server.applyMiddleware({ app });
const httpServer = createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
// 设置 SubscriptionServer
new SubscriptionServer({
execute,
subscribe,
schema,
onConnect: (connectionParams, webSocket, context) => {
console.log('Client connected!');
return context; // 可以在此处添加认证逻辑
},
onDisconnect: (webSocket, context) => {
console.log('Client disconnected!');
},
}, {
server: httpServer,
path: server.subscriptionsPath,
});
这段代码创建了一个简单的 Apollo Server,它提供了一个查询 hello
和一个订阅 newMessage
。服务器每5秒模拟一次新消息,并通过 pubsub.publish
发布到 NEW_MESSAGE
主题,所有订阅该主题的客户端都会收到更新。
客户端连接
客户端连接到 WebSocket 端点的方式与之前使用 Apollo Client 的示例相似,只需确保连接到正确的 WebSocket 地址(通常是 /graphql
后面加上 subscriptions
路径,具体取决于你的服务器配置)。
通过这种方式,你可以实现在自定义服务器上从零开始搭建 GraphQL Subscriptions,为应用程序提供实时数据更新功能。