ariadne icon indicating copy to clipboard operation
ariadne copied to clipboard

Document using ASGI middleware with Ariadne ASGI app

Open salwator opened this issue 5 years ago • 9 comments

Ariadne ASGI app creates its own request on the way and loses all middleware as a side effect, ex. session information.

salwator avatar Nov 08 '19 13:11 salwator

I think everything already works as intended, can we close this issue?

patrys avatar Nov 12 '19 10:11 patrys

@patrys This works as intended, but internal feedback I've gathered so far is negative - people coming from Django expect to use Starlette's middleware to set things on request, and then just access this request from within resolver and have the data they've set on it.

If anything, we should at least document this as gotcha and present idiomatic alternatives (eg. resolving this data in context_value factory).

rafalp avatar Nov 12 '19 11:11 rafalp

Setting things on request works as intended, changes are reflected in scope and picked up by the next request.

patrys avatar Nov 12 '19 12:11 patrys

Ah, so those are available via request.scope? All-right then!

I'll keep this issue open to see if it makes sense to mention this in our docs sometime when I'll be working on it.

rafalp avatar Nov 12 '19 13:11 rafalp

Do you guys have a short tldr on how to do this?

beneshed avatar Dec 29 '19 14:12 beneshed

Middlewares should use request.scope instead of request to store the data to be available from within the Ariadne. When people use Starlette's AuthenticationMiddleware, they follow the docs and use info.context["request"].user within resolvers, which fails because context["request"] instance will be different request than one directly within Starlette. They should do info.context["request"].scope["user"]" instead.

This is known gotcha, and something we should document.

rafalp avatar Dec 29 '19 14:12 rafalp

Ok this is great to know! I installed starlette and created an AuthBackend and attempted to install the middleware inside of the ariadne middleware

But I'm still getting the following error

{
  "data": null,
  "errors": [
    {
      "message": "AuthenticationMiddleware must be installed to access request.auth",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "hello"
      ],
      "extensions": {
        "exception": {
          "stacktrace": [
            "Traceback (most recent call last):",
            "  File \"/graphql/execution/execute.py\", line 625, in resolve_field_value_or_error",
            "    result = resolve_fn(source, info, **args)",
            "  File \"server/api/schema.py\", line 13, in resolve_hello",
            "    print(request.auth)",
            "  File \"starlette/requests.py\", line 116, in auth",
            "    ), \"AuthenticationMiddleware must be installed to access request.auth\"",
            "AssertionError: AuthenticationMiddleware must be installed to access request.auth"
          ],
          "context": {
            "self": "<starlette.re...t 0x10a4e3490>"
          }
        }
      }
    }
  ]
}

beneshed avatar Dec 29 '19 14:12 beneshed

This is same thing. No middleware-specific attribute of request will work within the resolver. You need to use info.context["request"].scope["auth"] for this.

ASGI spec defines scope as value that should be passable around layers of ASGI stack. If you see middleware that modifies request directly but leaves scope out, reach out to its author about this. Chances are the middleware may already be using scope and its only docs that could be updated (this is the case for Starlette).

rafalp avatar Dec 29 '19 14:12 rafalp

To be clear, you only need to write changes to the request.scope object, it's safe to read from the request itself.

patrys avatar Jan 07 '20 15:01 patrys

Any difference with uvcorn[standard] ?

shaozi avatar Nov 18 '22 04:11 shaozi