Package: gql-subscriptions
gql-subscriptionsA Python3.7+ port ofApollo Graphql Subscriptions.This package contains a basic asyncio pubsub system which should be used only in demo, and other pubsub system(like Redis).RequirementsPython 3.7+Installationpip install gql-subscriptionsThis package should be used with a network transport, for examplestarlette-graphqlGetting started with your first subscriptionTo begin with GraphQL subscriptions, start by defining a GraphQL Subscription type in your schema:type Subscription {
    somethingChanged: Result
}

type Result {
    id: String
}Next, add the Subscription type to your schema definition:schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}Now, let's create a simplePubSubinstance - it is simple pubsub implementation, based onasyncio.Queue.fromgql_subscriptionsimportPubSubpubsub=PubSub()Now, implement your Subscriptions type resolver, using thepubsub.async_iteratorto map the event you need(usepython-gql):fromgql_subscriptionsimportPubSub,subscribepubsub=PubSub()SOMETHING_CHANGED_TOPIC='something_changed'@subscribeasyncdefsomething_changed(parent,info):returnpubsub.async_iterator(SOMETHING_CHANGED_TOPIC)Now, the GraphQL engine knows thatsomethingChangedis a subscription, and every time we use pubsub.publish over this topic - it will publish it using the transport we use:pubsub.publish(SOMETHING_CHANGED_TOPIC, {'somethingChanged': {'id': "123" }})Note that the default PubSub implementation is intended for demo purposes. It only works if you have a single instance of your server and doesn't scale beyond a couple of connections. For production usage you'll want to use one of thePubSub implementationsbacked by an external store. (e.g. Redis).FiltersWhen publishing data to subscribers, we need to make sure that each subscriber gets only the data it needs.To do so, we can usewith_filterdecorator, which wraps thesubscription resolverwith a filter function, and lets you control each publication for each user.ResolverFn = Callable[[Any, Any, Dict[str, Any]], Awaitable[AsyncIterator]]
FilterFn = Callable[[Any, Any, Dict[str, Any]], bool]

def with_filter(filter_fn: FilterFn) -> Callable[[ResolverFn], ResolverFn]
    ...ResolverFnis a async function which returned atyping.AsyncIterator.async def something_changed(parent, info) -> typing.AsyncIteratorFilterFnis a filter function, executed with the payload(published value), operation info, arugments, and must return bool.For example, ifsomething_changedwould also accept a argument with the ID that is relevant, we can use the following code to filter according to it:fromgql_subscriptionsimportPubSub,subscribe,with_filterpubsub=PubSub()SOMETHING_CHANGED_TOPIC='something_changed'deffilter_thing(payload,info,relevant_id):returnpayload['somethingChanged'].get('id')==relevant_id@subscribe@with_filter(filter_thing)asyncdefsomething_changed(parent,info,relevant_id):returnpubsub.async_iterator(SOMETHING_CHANGED_TOPIC)Channels MappingYou can map multiple channels into the same subscription, for example when there are multiple events that trigger the same subscription in the GraphQL engine.fromgql_subscriptionsimportPubSub,subscribe,with_filterpubsub=PubSub()SOMETHING_UPDATED='something_updated'SOMETHING_CREATED='something_created'SOMETHING_REMOVED='something_removed'@subscribeasyncdefsomething_changed(parent,info):returnpubsub.async_iterator([SOMETHING_UPDATED,SOMETHING_CREATED,SOMETHING_REMOVED])PubSub ImplementationsIt can be easily replaced with some other implements ofPubSubEngine abstract class.This package contains aRedisimplements.fromgqlimportsubscribefromgql_subscriptions.pubsubs.redisimportRedisPubSubpubsub=RedisPubSub()SOMETHING_CHANGED_TOPIC='something_changed'@subscribeasyncdefsomething_changed(parent,info):returnpubsub.async_iterator(SOMETHING_CHANGED_TOPIC)You can also implement aPubSubof your own, by using the inheritPubSubEnginefrom this package, this is aReids example.