Skip to content

[WIP] Add websockets support to Sanic #3903

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

DoctorJohn
Copy link
Member

@DoctorJohn DoctorJohn commented Jun 13, 2025

Description

TODO:

  • fix the test client
    • The Sanic test client can only send requests to websocket endpoints, but cannot work with the actually websocket at all
    • Sanic claims it can be run as an ASGI application, but doing that and using the resulting ASGI app with our ASGI test client doesn't work either: websocket route handlers suddenly receive an object that does not conform with a Sanic WebSocket at all, breaking everything
  • make the view easier to use: Sanic's HTTPMethodView cannot handle websockets. We could look at Blueprints or ask users to register route handlers individually.
  • resolve route conflicts: looks like Sanic does not allow GET and WebSocket route handlers on the same path (i.e., /graphql)

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Documentation

Issues Fixed or Closed by This PR

Summary by Sourcery

Add support for GraphQL subscriptions over WebSockets in Sanic by implementing a dedicated WebSocket adapter and extending the GraphQLView to handle WS connections, along with updated test client infrastructure.

New Features:

  • Introduce SanicWebSocketAdapter to enable GraphQL WebSocket transports.
  • Extend GraphQLView with a websocket() handler for subscription workflows and protocol negotiation.
  • Add keep-alive, debug, and protocol configuration options to GraphQLView initialization.
  • Integrate WebSocket routes into the Sanic test client and provide a ws_connect helper for testing.

Enhancements:

  • Unify HTTP and WebSocket request and context handling in GraphQLView methods.
  • Provide default implementations for subprotocol selection and WebSocket response creation.

Tests:

  • Update Sanic test client to register WebSocket routes and handle GraphQL WS transports.
  • Add an async ws_connect context manager for WebSocket integration tests.
  • Include SanicHttpClient in test suite configuration for WebSocket support.

the native sanic test client appears to be useless for testing websockets.
they claim sanic apps can be run as asgi apps, but that changes the whole
interface for websocket objects and breaks everything. nice
Copy link
Contributor

sourcery-ai bot commented Jun 13, 2025

Reviewer's Guide

This PR adds full WebSocket support for Sanic by introducing a custom WebSocket adapter, extending the GraphQLView to handle WebSocket routes and protocols (including initialization parameters and runtime methods), enhancing the Sanic test client to exercise subscription endpoints, and updating the base view’s type signatures to accommodate WebSocket requests.

New Class Diagram: SanicWebSocketAdapter

classDiagram
    class AsyncWebSocketAdapter {
        %% Base class methods (conceptual)
    }
    class SanicWebSocketAdapter {
        +ws: Websocket
        +__init__(view: AsyncBaseHTTPView, request: Websocket, response: Websocket)
        +iter_json() AsyncGenerator~object, None~
        +send_json(message: Mapping~str, object~) None
        +close(code: int, reason: str) None
    }
    AsyncWebSocketAdapter <|-- SanicWebSocketAdapter
Loading

Updated Class Diagram: GraphQLView and AsyncBaseHTTPView WebSocket Handling

classDiagram
    class AsyncBaseHTTPView {
        +websocket_adapter_class: Callable[[Self, WebSocketRequest, WebSocketResponse], AsyncWebSocketAdapter] // Type hint 'Self' adopted
    }
    class GraphQLView {
        +keep_alive: bool  // New attribute
        +keep_alive_interval: float  // New attribute
        +debug: bool  // New attribute
        +protocols: Sequence[str]  // New attribute (from subscription_protocols)
        +connection_init_wait_timeout: timedelta  // New attribute
        +websocket_adapter_class: SanicWebSocketAdapter // Concrete adapter type assigned

        +get_root_value(request: Union[Request, Websocket]) Optional[RootValue] // Parameter type updated
        +get_context(request: Union[Request, Websocket], response: Union[TemporalResponse, Websocket]) Context // Parameter types updated
        +websocket(request: Request, ws: Websocket) Websocket // New method for WebSocket route
        +is_websocket_request(request: Union[Request, Websocket]) TypeGuard[Websocket] // Parameter type and return updated
        +pick_websocket_subprotocol(request: Websocket) Optional[str] // Parameter type updated
        +create_websocket_response(request: Websocket, subprotocol: Optional[str]) Websocket // Parameter type and return updated
    }
    AsyncBaseHTTPView <|-- GraphQLView
Loading

File-Level Changes

Change Details Files
Introduce SanicWebSocketAdapter and wire it into the GraphQLView
  • import GraphQL subscription protocol constants
  • define SanicWebSocketAdapter with iter_json, send_json, close
  • assign websocket_adapter_class in GraphQLView
strawberry/sanic/views.py
Extend GraphQLView to support WebSocket routes and options
  • add init parameters for keep_alive, protocols, debug, and timeouts
  • implement async websocket(request, ws) entrypoint calling run()
  • override is_websocket_request, pick_websocket_subprotocol, create_websocket_response
  • adjust get_root_value and get_context to accept Websocket objects
strawberry/sanic/views.py
Enhance Sanic test client to drive WebSocket endpoints
  • extend test GraphQLView with WS mixin and handler classes
  • use add_websocket_route instead of HTTP route registration
  • add ws_connect contextmanager returning AsgiWebSocketClient
tests/http/clients/sanic.py
tests/websockets/conftest.py
Adjust async_base_view typing for WebSocket adapters and multipart data
  • update websocket_adapter_class signature to use Self type
  • change encode_multipart_data parameter type to object
strawberry/http/async_base_view.py

Assessment against linked issues

Issue Objective Addressed Explanation
#2927 Fix the reported error: Cannot return null for non-nullable field Subscription.count. when using subscriptions with Sanic.
#2927 Enable subscriptions to work in Sanic web apps.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

codecov bot commented Jun 13, 2025

Codecov Report

Attention: Patch coverage is 75.36232% with 17 lines in your changes missing coverage. Please review.

Project coverage is 94.94%. Comparing base (758cd0b) to head (e85a1ba).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3903      +/-   ##
==========================================
- Coverage   95.06%   94.94%   -0.12%     
==========================================
  Files         515      515              
  Lines       33583    33634      +51     
  Branches     1736     1739       +3     
==========================================
+ Hits        31924    31934      +10     
- Misses       1377     1419      +42     
+ Partials      282      281       -1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

codspeed-hq bot commented Jun 13, 2025

CodSpeed Performance Report

Merging #3903 will not alter performance

Comparing DoctorJohn:add-websockets-support-to-sanic (e85a1ba) with main (758cd0b)

Summary

✅ 21 untouched benchmarks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sanic - subscriptions not supported
1 participant