strawberry-graphql / strawberry-django

Strawberry GraphQL Django extension
https://strawberry.rocks/docs/django
MIT License
409 stars 118 forks source link

Getting subscriptions running #375

Closed sdobbelaere closed 11 months ago

sdobbelaere commented 1 year ago

Hi Everyone!

I'm trying to get subscriptions working in some models, so that I can push updated-information to the the frontend after our task-manger has executed certain actions.

However I seem to be stuck on getting a basic subscription running from the docs:

from strawberry import type, subscription
import asyncio

@type
class Subscription:
    @subscription
    async def count(self, target: int = 100) -> int:
        for i in range(target):
            yield i
            await asyncio.sleep(0.5)

When I try out this subscription in graphiql this is the response:

// subscription{count(target:32)}
// yields

{
  "errors": [
    {
      "isTrusted": true
    }
  ]
}

How did I setup the subscription?

# urls
urlpatterns = [
    path('admin/', admin.site.urls),
    path('graphql/', AsyncGraphQLView.as_view(
        schema=schema,
        graphiql=True,
        subscriptions_enabled=True)),
]

# Schema
schema = strawberry.Schema(
    query=Query,
    mutation=Mutation,
    subscription=Subscription,
    extensions=[DjangoOptimizerExtension()]
)

I must admit I was getting confused that needs to be done to actually get the subscriptions running as in one part of the docs you just set AsyncGraphQLView.as_view. subscriptions_enabled to True. But in other doc's you need to create a new AsyncgraphQlView. Both approaches didn't seem to get me further, so I must be missing something crucial.

Help is tremendously appreciated, feel like I'm going in circles. :-)

Upvote & Fund

Fund with Polar

bellini666 commented 1 year ago

Hi @sdobbelaere ,

Probably we need to improve the documentation regarding subscription support within django.

But basically, to use subscriptions you need to be running with ASGI even in development (tip: you can use daphne which will replace your runserver with an ASGI one), and that ASGI server needs to be able to handle websockets connections. For that you can use the channels integration

sdobbelaere commented 1 year ago

@bellini666 I see, that put things into perspective indeed.

I'm also noticing in the docs that for non-channel connoisseurs, the section that gives back the channel controle makes things harder than they should.

I'll have a look to clarify the docs while I'm working my way though things to get it running. Feel free to add comments on changes on that pull request when it comes.

sdobbelaere commented 1 year ago

@bellini666 I've created a pull-request with my findings getting things running on my end. This should help new users getting things on the rails at least with a the counter as an example.

However, while trying to wrap my models into subscriptions I came across the issue that running Daphne as test-server seems to remove the request/user from the context not only for subscriptions but also on the Queries/Mutations.

  File "/Users/sascha/Plugins/strawberry-graphql-django/strawberry_django/permissions.py", line 292, in resolve
    user = info.context.request.user
           ^^^^^^^^^^^^^^^^^^^^

I see there is a reference to it here: https://strawberry.rocks/docs/general/subscriptions#authenticating-subscriptions However it merely covers part of the use-case since it only talks about the subscription, but doesnt address that this changes the entire authentication behaviour.

How would you suggest we can mitigate this issue throughout?

bellini666 commented 1 year ago

However, while trying to wrap my models into subscriptions I came across the issue that running Daphne as test-server seems to remove the request/user from the context not only for subscriptions but also on the Queries/Mutations.

It depends. Indeed the channels integration has a different context kind, and you would need to wrap channels with its own middlewares to authenticate the user, as is suggested here: https://channels.readthedocs.io/en/latest/topics/authentication.html

sdobbelaere commented 1 year ago

The channels integration seems to move the user object around quite a bit + authentication wasn't enabled on the supplied router. This caused some unexpected behaviour in the existing queries and mutations when using ASGI instead of WSGI

I've extended the pull-request to handle this.

As it stands, the queries and mutations seem to be running according to the guide I wrote.

Next I'm wanting to get the models wrapped, just like queries to show real-time data on the frontend. Which in the end are to be connected to the post-save signal to ensure the updates. For our projects, this is our end-goal, real-time updates on various models.

bellini666 commented 1 year ago

Next I'm wanting to get the models wrapped, just like queries to show real-time data on the frontend. Which in the end are to be connected to the post-save signal to ensure the updates. For our projects, this is our end-goal, real-time updates on various models.

I have some similar use cases in my projects =P

Feel free to ping me on discord in case you want some tips on it, and I can share with you some examples