This package implements the PubSubEngine Interface from the graphql-subscriptions package and also the new AsyncIterator interface.It allows you to connect your subscriptions manager to a Redis Pub Sub mechanism to supportmultiple subscription manager instances.
At first, install the graphql-redis-subscriptions
package:
npm install graphql-redis-subscriptions
As the graphql-subscriptions package is declared as a peer dependency, you might receive warning about an unmet peer dependency if it's not installed already by one of your other packages. In that case you also need to install it too:
npm install graphql-subscriptions
Define your GraphQL schema with a Subscription
type:
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
type Subscription {
somethingChanged: Result
}
type Result {
id: String
}
Now, let's create a simple RedisPubSub
instance:
import { RedisPubSub } from 'graphql-redis-subscriptions';
const pubsub = new RedisPubSub();
Now, implement your Subscriptions type resolver, using the pubsub.asyncIterator
to map the event you need:
const SOMETHING_CHANGED_TOPIC = 'something_changed';
export const resolvers = {
Subscription: {
somethingChanged: {
subscribe: () => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC),
},
},
}
Subscriptions resolvers are not a function, but an object with
subscribe
method, that returnsAsyncIterable
.
Calling the method asyncIterator
of the RedisPubSub
instance will send redis a SUBSCRIBE
message to the topic provided and will return an AsyncIterator
binded to the RedisPubSub instance and listens to any event published on that topic.Now, the GraphQL engine knows that somethingChanged
is a subscription, and every time we will use pubsub.publish
over this topic, the RedisPubSub
will PUBLISH
the event over redis to all other subscribed instances and those in their turn will emit the event to GraphQL using the next
callback given by the GraphQL engine.
pubsub.publish(SOMETHING_CHANGED_TOPIC, { somethingChanged: { id: "123" }});
export const resolvers = {
Subscription: {
somethingChanged: {
subscribe: (_, args) => pubsub.asyncIterator(`${SOMETHING_CHANGED_TOPIC}.${args.relevantId}`),
},
},
}
export const resolvers = {
Subscription: {
somethingChanged: {
subscribe: (_, args) => pubsub.asyncIterator(`${SOMETHING_CHANGED_TOPIC}.${args.relevantId}.*`, { pattern: true })
},
},
}
import { withFilter } from 'graphql-subscriptions';
export const resolvers = {
Subscription: {
somethingChanged: {
subscribe: withFilter(
(_, args) => pubsub.asyncIterator(`${SOMETHING_CHANGED_TOPIC}.${args.relevantId}`),
(payload, variables) => payload.somethingChanged.id === variables.relevantId,
),
},
},
}
The basic usage is great for development and you will be able to connect to a Redis server running on your system seamlessly. For production usage, it is recommended to pass a redis client (like ioredis) to the RedisPubSub constructor. This way you can control all the options of your redis connection, for example the connection retry strategy.
import { RedisPubSub } from 'graphql-redis-subscriptions';
import * as Redis from 'ioredis';
const options = {
host: REDIS_DOMAIN_NAME,
port: PORT_NUMBER,
retryStrategy: times => {
// reconnect after
return Math.min(times * 50, 2000);
}
};
const pubsub = new RedisPubSub({
...,
publisher: new Redis(options),
subscriber: new Redis(options)
});
Receiving messages as Buffers
Some Redis use cases require receiving binary-safe data back from redis (in a Buffer). To accomplish this, override the event names for receiving messages and pmessages. Different redis clients use different names, for example:
library | message event | message event (Buffer) | pmessage event | pmessage event (Buffer) |
---|---|---|---|---|
ioredis | message |
messageBuffer |
pmessage |
pmessageBuffer |
node-redis | message |
message_buffer |
pmessage |
pmessage_buffer |
import { RedisPubSub } from 'graphql-redis-subscriptions';
import * as Redis from 'ioredis';
const pubsub = new RedisPubSub({
...,
// Tells RedisPubSub to register callbacks on the messageBuffer and pmessageBuffer EventEmitters
messageEventName: 'messageBuffer',
pmessageEventName: 'pmessageBuffer',
});
Also works with your Redis Cluster
import { RedisPubSub } from 'graphql-redis-subscriptions';
import { Cluster } from 'ioredis';
const cluster = new Cluster(REDIS_NODES); // like: [{host: 'ipOrHost', port: 1234}, ...]
const pubsub = new RedisPubSub({
...,
publisher: cluster,
subscriber: cluster
});
You can learn more on the ioredis
package here.
By default, Javascript objects are serialized using the JSON.stringify
and JSON.parse
methods.This means that not all objects - such as Date or Regexp objects - will deserialize correctly without a custom reviver, that work out of the box with the default in-memory implementation.For handling such objects, you may pass your own reviver function to JSON.parse
, for example to handle Date objects the following reviver can be used:
import { RedisPubSub } from 'graphql-redis-subscriptions';
const dateReviver = (key, value) => {
const isISO8601Z = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;
if (typeof value === 'string' && isISO8601Z.test(value)) {
const tempDateNumber = Date.parse(value);
if (!isNaN(tempDateNumber)) {
return new Date(tempDateNumber);
}
}
return value;
};
const pubSub = new RedisPubSub({ ..., reviver: dateReviver });
pubSub.publish('Test', {
validTime: new Date(),
invalidTime: '2018-13-01T12:00:00Z'
});
pubSub.subscribe('Test', message => {
message.validTime; // Javascript Date
message.invalidTime; // string
});
import { RedisPubSub } from 'graphql-redis-subscriptions';
const pubsub = new RedisPubSub();
const subscriptionManager = new SubscriptionManager({
schema,
pubsub,
setupFunctions: {},
});
Recently, graphql-subscriptions package added a way to pass in options to each call of subscribe.Those options are constructed via the setupFunctions object you provide the Subscription Manager constructor.The reason for graphql-subscriptions to add that feature is to allow pub sub engines a way to reduce their subscription set using the best method of said engine.For example, Meteor's live query could use Mongo selector with arguments passed from the subscription like the subscribed entity id.For Redis, this could be a bit more simplified, but much more generic.The standard for Redis subscriptions is to use dot notations to make the subscription more specific.This is only the standard but I would like to present an example of creating a specific subscription using the channel options feature.
First I create a simple and generic trigger transform
const triggerTransform = (trigger, {path}) => [trigger, ...path].join('.');
Then I pass it to the RedisPubSub
constructor.
const pubsub = new RedisPubSub({
triggerTransform,
});
Lastly, I provide a setupFunction for commentsAdded
subscription field.It specifies one trigger called comments.added
and it is called with the channelOptions object that holds repoName
path fragment.
const subscriptionManager = new SubscriptionManager({
schema,
setupFunctions: {
commentsAdded: (options, {repoName}) => ({
'comments.added': {
channelOptions: {path: [repoName]},
},
}),
},
pubsub,
});
When I call subscribe
like this:
const query = `
subscription X($repoName: String!) {
commentsAdded(repoName: $repoName)
}
`;
const variables = {repoName: 'graphql-redis-subscriptions'};
subscriptionManager.subscribe({query, operationName: 'X', variables, callback});
The subscription string that Redis will receive will be comments.added.graphql-redis-subscriptions
.This subscription string is much more specific and means the the filtering required for this type of subscription is not needed anymore.This is one step towards lifting the load off of the GraphQL API server regarding subscriptions.
Please refer to https://github.com/Grokzen/docker-redis-cluster documentation to start a cluster
$ docker run --rm -p 6379:6379 redis:alpine
$ export REDIS_CLUSTER_IP=0.0.0.0; docker run -e "IP=0.0.0.0" --rm -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 grokzen/redis-cluster
npm run test
原文链接:https://medium.com/@weblab_tech/graphql-everything-you-need-to-know-58756ff253d8 原文作者:Weblab Technology 译者:杨涛 你可能已经构建和使用REST API一段时间了,并且最近开始听说GraphQL--一种新型的API技术。有些人说它很好,但一些人并不认同。那么,我相信你肯定很想知道Gra
graphql-subscriptions GraphQL subscriptions is a simple npm package that lets you wire up GraphQL with a pubsub system (like Redis) to implement subscriptions in GraphQL. You can use it with any Graph
快速开始 GraphQL 是一种用于 API 的查询语言。这是 GraphQL 和 REST 之间一个很好的比较 (译者注: GraphQL 替代 REST 是必然趋势)。在这组文章中, 我们不会解释什幺是 GraphQL, 而是演示如何使用 @nestjs/GraphQL 模块。 GraphQLModule 只不过是 Apollo 服务器的包装器。我们没有造轮子, 而是提供一个现成的模块, 这让
GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。 向你的 API 发出一个 GraphQL 请求就能准确获得你想要的数据,不多不少。 GraphQL 查询总是返回可预测
Graphql editor 是一款 Graphql 的可视化编辑器和 IDE,帮助用户更容易理解 GraphQL 模式,通过使用可视化块系统创建模式。GraphQL Editor 将把它们转化为代码。通过 GraphQL Editor,用户可以在不写任何代码的情况下创建可视化的图表,或者以一种很好的方式呈现其模式。 GraphQL View Code Editor View Hierarchy View
GraphQL CLI Help us to improve new GraphQL CLI. Check out the new structure and commands below!Feel free to contact us in Discord channel. We would love to hear your feedback. Features Helpful command
Fullstack GraphQL Simple Demo Application API built with Node + Express + GraphQL + Sequelize (supports MySQL, Postgres, Sqlite and MSSQL). WebApp built with React + Redux. Written in ES6 using Babel