strawberry-graphql / strawberry

A GraphQL library for Python that leverages type annotations 🍓
https://strawberry.rocks
MIT License
3.94k stars 520 forks source link

Channels integration (subscription) example in the official document doesn't work. #3220

Closed shmoon-kr closed 10 months ago

shmoon-kr commented 10 months ago

Describe the Bug

I tried to follow channels integration example in the official document.

https://strawberry.rocks/docs/integrations/channels

I wrote a code guided in the document and tried to execute sendChatMessage mutation in graphiql page.

mutation echo {
  sendChatMessage(message: "hello room 1", room: { roomName: "room1" })
}

But an error was occurred.

{
  "data": {
    "sendChatMessage": null
  },
  "errors": [
    {
      "message": "'ws'",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "sendChatMessage"
      ]
    }
  ]
}

File "/home/shmoon/PycharmProjects/graphqltest/venv/lib/python3.10/site-packages/strawberry/utils/await_maybe.py", line 12, in await_maybe return await value File "/home/shmoon/PycharmProjects/graphqltest/sample/schema.py", line 35, in send_chat_message ws = info.context["ws"] KeyError: 'ws'

when I looked at the code, it was written.

    @strawberry.mutation
    async def send_chat_message(
            self,
            info: Info,
            room: ChatRoom,
            message: str,
    ) -> None:
        ws = info.context["ws"]
        channel_layer = ws.channel_layer

but there's no key "ws" in info.context dictionary when I debugged. It seems that web socket information was not passed to graphql handler.

Am I missing something?

This is my asgi.py code.

import os
from strawberry.channels import GraphQLProtocolTypeRouter
from django.core.asgi import get_asgi_application
from sample.schema import schema

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'graphqltest.settings')

# Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models.
django_asgi_app = get_asgi_application()

application = GraphQLProtocolTypeRouter(
    schema,
    django_application=django_asgi_app,
)

System Information

Additional Context

Django==4.2.6 channels[daphne]==4.0.0 channels-redis==4.1.0 strawberry-graphql[channels]==0.213.0 strawberry-graphql-django==0.24.1

Upvote & Fund

Fund with Polar

DoctorJohn commented 10 months ago

Hi, thanks for reporting! Ideally all our examples should work out of the box.

In this case I think the issue is, that GraphiQL sends mutations via HTTP POST requests, while the example mutation expects to be called through a websocket connection. This example should theoretically work with a GraphQL client using a websocket connection, but that's not obvious from the example nor ideal.

shmoon-kr commented 10 months ago

Thanks for a comment. I understood and found a simple walkaround for the problem. If you get a channel layer from channels package directly, it works.

        ws = info.context["ws"]
        channel_layer = ws.channel_layer

This part should be refactored to

import channels.layers
...
        channel_layer = channels.layers.get_channel_layer()

I think it would be better to fix the official document since it may mislead developers.