Skip to content
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

[MIDDLEWARE] middleware executed many times #53

Open
yamila-moreno opened this issue Aug 31, 2018 · 17 comments
Open

[MIDDLEWARE] middleware executed many times #53

yamila-moreno opened this issue Aug 31, 2018 · 17 comments

Comments

@yamila-moreno
Copy link

yamila-moreno commented Aug 31, 2018

Hi there, I'm using flask-graphql in a project and got into a question/issue I don't have many clues. I'm using middlewares; this is the minimum example:

from flask import Flask, request
from flask_graphql import GraphQLView
from graphene import Field, ObjectType, Schema, String, relay


class Hello(ObjectType):
    message = String()


class Query(ObjectType):
    hello = Field(Hello)

    def resolve_hello(self, info):
        return Hello(message="Hi there")

class DummyMiddleware(object):
    def resolve(self, next, root, info, **kwargs):
        print("DummyMiddleware")
        return next(root, info, **kwargs)


dummy_middleware = DummyMiddleware()
schema = Schema(query=Query)

app = Flask(__name__)
app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql',
                                                      schema=schema,
                                                      graphiql=True,
                                                      middleware=[dummy_middleware]))

if __name__ == "__main__":
    app.run(host='0.0.0.0', threaded=True)

(The problem happens no matter the version; the problem also happens when launching the wsgi app with gunicorn).

If I query "hello()", the "DummyMiddleware" will appear twice. If I query a list of elements, the middleware is executed lots of time, twice per element it seems.
For a query this is just a problem of performance, but in a Logout() mutation what happens is:

  • 1st time - the authenticated request is valid, so "logout()" deletes the token
  • 2nd time - the authenticated request is not valid (because the token has been deleted), so it returns an error

Is there anything wrong in my approach to middlewares? I'm not sure which part is responsible (flask, graphql, my configuration...). Any hint would be appreciated.

@gijswobben
Copy link

I have the same issue, but no solution. I did notice however, that the middleware gets called with different inputs. If you try something like this, you'll see what I mean (careful, this creates a LOT of output):

def testMiddleware(next, root, info, *args, **kwargs):
    p = next(root, info, *args, **kwargs)
    def r(x):
        print(type(x), x)
        return x
    return p.then(lambda x: r(x))

Any suggestions are welcome!

@yamila-moreno
Copy link
Author

Thanks! We'll check it.

@gijswobben
Copy link

It appears to be expected behavior. The Django debug middleware is using it to gather all the promises and summarize them: https://github.com/graphql-python/graphene-django/blob/dc561c68c49d1a91637503f765857d819c36769a/graphene_django/debug/middleware.py

However, I think it's still nice to have some kind of work-around.

@aindong
Copy link

aindong commented Jul 5, 2019

Did anyone find a workaround on this? Because I want to log only the root query, not the whole fields many times.

@yamila-moreno
Copy link
Author

I made a workaround for logout only, not for the query-per-field; the solution was:

class Logout(relay.ClientIDMutation):
    class Input:
        pass

    data = Boolean()

    @classmethod
    def mutate_and_get_payload(cls, root, info, **input):
        try:
            # Workaround for https://github.com/graphql-python/flask-graphql/issues/53
            token = info.context['request'].headers.get('Authorization')
            user = auth_service.get_authorized_user_via_token(token)
            if not user:
                raise GenericError('API_ERRORS.BAD_CREDENTIALS')
            auth_service.logout(token)
        except GenericError as e:
            return Promise.reject(GraphQLError(e.message))

        return Logout(data=True)

@maciejzukowski
Copy link

You can check if root variable is null

class LoggingMiddleware(object):
    def resolve(self, next, root, info, **kwargs):
          if root is None:
              #This will only be called once for a request
              print(info.operation.name.value)

@squarewave24
Copy link

checking for root is None appears to work so far, but would love to know if this is the correct way to use it.

i am trying to use middleware for authorization of each graphql request.

@mrfoxes
Copy link

mrfoxes commented Jan 29, 2020

I'm having the same problem, check for root is None seems fine but as you look the middleware continue to run many many times, you just stop executing the logic every time but it continue to run

It seems that every resolver function call the middleware execution

In my case i'm using Django and I'm considering to move the authentication logic inside the default MIDDLEWARE section so that grapqhl stop executing the middleware for each resolver function

Screenshot 2020-01-29 at 13 31 57

@johnfrancisgit
Copy link

Running into the same issue with graphene-django. Only want to execute middleware resolve funcitons once on request, not for every response field.

@diegopaladini
Copy link

Hi, Do you have some news about this topic?

Thanks

@mrfoxes
Copy link

mrfoxes commented Apr 18, 2020

Hi, Do you have some news about this topic?

Thanks

Hi @diegopaladini, for now to run that logic just one time in a middleware I'm doing the same as @maciejzukowski beacause the default graphql behaviour is to run that middelware logic for each resolver that is being called but in my case i don't need it

class LoggingMiddleware(object):
    def resolve(self, next, root, info, **kwargs):
          if root is None:
              #This will only be called once for a request
              print(info.operation.name.value)

@maciejzukowski
Copy link

For those that are using this solution for authentication of requests - I think a better approach is to not have security in the middleware but instead use decorators on the resolvers themselves.

@alimirjahani7
Copy link

alimirjahani7 commented Oct 2, 2020

I have the same problem and the middleware is running four time for any request and in two of them root is not None

@acorrea-B
Copy link

i have the same problem but i use a external service to auth and and a get a spam because launch 10 request 1 per second

@acorrea-B
Copy link

acorrea-B commented Jul 17, 2021

You can check if root variable is null

class LoggingMiddleware(object):
    def resolve(self, next, root, info, **kwargs):
          if root is None:
              #This will only be called once for a request
              print(info.operation.name.value)

is a good solution and works for me, but @graphql-python have to solution

@milieere
Copy link

milieere commented Nov 6, 2023

Hi! graphene fastAPI user here. Currently facing the same challenge. It is very cumbersome to implement authorization since I have a complex schema and GraphQL middleware executes so many times. In the middleware, I need to execute JWT verification, which sends requests to Azure AD endpoints to get certificates, which is overwhelming the server and it becomes unresponsive :(

I had to implement the authorization in the middleware of FastAPI, but this is not ideal either - since now when my user is not authorized and hits graphql endpoint, he only gets back 'Internal Server Error'..

Anyone though about a different workaround? Using a decorator with resolvers is not neat either since I want to authorize every single request, i.e. introspection queries.

@akash-mahmud
Copy link

akash-mahmud commented Feb 3, 2024

Same problem, I am using graphne and django for my graphql server. I have my external server where I just need to pass the jwt. So I am using the middleware system. it seems that the middleware always gets called multiple times. The root None solution works. But still this function is executing multiple times which I don't need,

My middleware code

from django.conf import settings
from graphql_jwt.middleware import JSONWebTokenMiddleware
from .helpers.user import get_user
class CustomJWTMiddleware(JSONWebTokenMiddleware):
    def resolve(self, next, root, info, **kwargs):
        if root is None:
            print(root)
            # Get the JWT token from the request headers
            jwt_token = info.context.META.get('HTTP_AUTHORIZATION')
            # If there is no JWT token, call the next middleware
            if not jwt_token:
                return next(root, info, **kwargs)

            # Send the JWT token to your Node.js server for verification

            try:
                

                user = get_user(jwt_token)
                info.context.user =user
            except Exception as e:
                print(e)
                raise Exception(e)
        print("middleware calling")
        return next(root, info, **kwargs) 

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

No branches or pull requests