https://github.com/syfun/gql-subscriptions
A Python3.7+ port of Apollo Graphql Subscriptions.
https://github.com/syfun/gql-subscriptions
graphql pubsub python subscription
Last synced: about 2 months ago
JSON representation
A Python3.7+ port of Apollo Graphql Subscriptions.
- Host: GitHub
- URL: https://github.com/syfun/gql-subscriptions
- Owner: syfun
- License: mit
- Created: 2020-04-21T13:33:23.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2020-09-03T06:28:42.000Z (almost 5 years ago)
- Last Synced: 2025-05-02T02:04:12.900Z (about 2 months ago)
- Topics: graphql, pubsub, python, subscription
- Language: Python
- Size: 24.4 KB
- Stars: 6
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# gql-subscriptions
A Python3.7+ port of [Apollo Graphql Subscriptions](https://github.com/apollographql/graphql-subscriptions).
This package contains a basic asyncio pubsub system which should be used only in demo, and other pubsub system(like Redis).
## Requirements
Python 3.7+
## Installation
`pip install gql-subscriptions`
> This package should be used with a network transport, for example [starlette-graphql](https://github.com/syfun/starlette-graphql)
## Getting started with your first subscription
To 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 simple `PubSub` instance - it is simple pubsub implementation, based on `asyncio.Queue`.
```python
from gql_subscriptions import PubSubpubsub = PubSub()
```Now, implement your Subscriptions type resolver, using the `pubsub.async_iterator` to map the event you need(use [python-gql](https://github.com/syfun/python-gql)):
```python
from gql_subscriptions import PubSub, subscribepubsub = PubSub()
SOMETHING_CHANGED_TOPIC = 'something_changed'
@subscribe
async def something_changed(parent, info):
return pubsub.async_iterator(SOMETHING_CHANGED_TOPIC)
```Now, the GraphQL engine knows that `somethingChanged` is 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 the [PubSub implementations](#pubsub-implementations) backed by an external store. (e.g. Redis).
## Filters
When publishing data to subscribers, we need to make sure that each subscriber gets only the data it needs.
To do so, we can use `with_filter` decorator, which wraps the `subscription resolver` with 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]
...
````ResolverFn` is a async function which returned a `typing.AsyncIterator`.
```
async def something_changed(parent, info) -> typing.AsyncIterator
````FilterFn` is a filter function, executed with the payload(published value), operation info, arugments, and must return bool.
For example, if `something_changed` would also accept a argument with the ID that is relevant, we can use the following code to filter according to it:
```python
from gql_subscriptions import PubSub, subscribe, with_filterpubsub = PubSub()
SOMETHING_CHANGED_TOPIC = 'something_changed'
def filter_thing(payload, info, relevant_id):
return payload['somethingChanged'].get('id') == relevant_id@subscribe
@with_filter(filter_thing)
async def something_changed(parent, info, relevant_id):
return pubsub.async_iterator(SOMETHING_CHANGED_TOPIC)
```## Channels Mapping
You can map multiple channels into the same subscription, for example when there are multiple events that trigger the same subscription in the GraphQL engine.
```python
from gql_subscriptions import PubSub, subscribe, with_filterpubsub = PubSub()
SOMETHING_UPDATED = 'something_updated'
SOMETHING_CREATED = 'something_created'
SOMETHING_REMOVED = 'something_removed'@subscribe
async def something_changed(parent, info):
return pubsub.async_iterator([SOMETHING_UPDATED, SOMETHING_CREATED, SOMETHING_REMOVED])
```## PubSub Implementations
It can be easily replaced with some other implements of [PubSubEngine abstract class](https://github.com/syfun/gql-subscriptions/blob/master/gql_subscriptions/engine.py).
This package contains a `Redis` implements.
```python
from gql import subscribe
from gql_subscriptions.pubsubs.redis import RedisPubSubpubsub = RedisPubSub()
SOMETHING_CHANGED_TOPIC = 'something_changed'
@subscribe
async def something_changed(parent, info):
return pubsub.async_iterator(SOMETHING_CHANGED_TOPIC)
```You can also implement a `PubSub` of your own, by using the inherit `PubSubEngine` from this package, this is a [Reids example](https://github.com/syfun/gql-subscriptions/blob/master/gql_subscriptions/pubsubs/redis.py).