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

Oneshot middleware #810

Closed
Speedy1991 opened this issue Nov 14, 2019 · 10 comments
Closed

Oneshot middleware #810

Speedy1991 opened this issue Nov 14, 2019 · 10 comments
Labels

Comments

@Speedy1991
Copy link

Speedy1991 commented Nov 14, 2019

Hi!
Currently I'm trying to add some one-shot-middlewares to the graphene schema (e.g. version check, policy check). The problem is, that the middleware get's fired for each field instead for a single request.
I can't use the DjangoMiddlewares because the error doesn't get wrapped into the graphql response error field

I found 2 solutions for this problem:

  1. I set a variable _skip=False in the middleware which is toggled to True before the first next(...) call, e.g.:
lass LatestPolicyAcceptedMiddleware:

    def __init__(self, *args, **kwargs):
        super(LatestPolicyAcceptedMiddleware, self).__init__(*args, **kwargs)
        self._skip = False

    def resolve(self, next, root, info, **kwargs):
        if self._skip:
            return next(root, info, **kwargs)
        ...
        self._skip = True
        return next(root, info, **kwargs)

Well - that works but feels really odd.

  1. Patching the GraphQLView.get_response method like this:
    def get_response(self, request, data, show_graphiql=False):
        try:
            check_min_native_version(request)
            latest_policy_check(request)
            return super(PatchedGraphQLView, self).get_response(request, data, show_graphiql)
        except (PolicyRequiredException, UpgradeNeededException) as e:
            result = self.json_encode(request, {"errors": [self.format_error(e)], "data": None}, pretty=show_graphiql)
            status_code = e.code
            return result, status_code

This feels also odd, but a way cleaner.

So - what's the best way to integrate a one-time middleware? It feels like the framework is missing this essential feature.

@melvinkcx
Copy link

I'm running into this issue as well, I have a middleware that checks the auth of the user and it is being consistently called for each field.

@melvinkcx
Copy link

Graphene middleware is called at a field-level, each field resolution would trigger calls to all middleware. I'm not sure if graphene will support request-level middleware, I just created an issue: graphql-python/graphene#1117

For now, what I do is add an attribute into info.context object and toggle that to indicate should the middleware logic be executed.

class AuthMiddleware:
    def resolve(self, next, root, info, **kwargs):
        if not hasattr(info.context, "auth_checked") and info.context.auth_checked:
            # Check user auth
            info.context.auth_checked = True
            
        return next(root, info, **kwargs)

@mrfoxes
Copy link

mrfoxes commented Jan 29, 2020

Graphene middleware is called at a field-level, each field resolution would trigger calls to all middleware. I'm not sure if graphene will support request-level middleware, I just created an issue: graphql-python/graphene#1117

For now, what I do is add an attribute into info.context object and toggle that to indicate should the middleware logic be executed.

class AuthMiddleware:
    def resolve(self, next, root, info, **kwargs):
        if not hasattr(info.context, "auth_checked") and info.context.auth_checked:
            # Check user auth
            info.context.auth_checked = True
            
        return next(root, info, **kwargs)

It's the same problem i'm noticing in my case but as you can see the middleware, despite not executing the logic runs anyway without executing nothing and so we're wasting execution time.
Assuming that you're requesting just one field it'll be fine but when you request a lot of fields can became a real bottleneck

Any idea so that we just run the middleware once for a specific context of execution?

Screenshot 2020-01-29 at 13 31 57

@melvinkcx
Copy link

I just released graphql-utilities including utilities to support operation-level only middleware. It targets graphql-core>=3.0 (or graphql-core-next), feel free to check it out.

@Speedy1991
Copy link
Author

@melvinkcx Thank you for this package

Just copied the run_only_once decorator into my own code because I don't want to have another dependency for 10 lines of code :)

it would be awesome if the run_only_once decorator can be picked into graphene_django
Whats your opinion @zbyte64 ?

@mrfoxes
Copy link

mrfoxes commented Feb 4, 2020

@melvinkcx Thank you for this package

Just copied the run_only_once decorator into my own code because I don't want to have another dependency for 10 lines of code :)

it would be awesome if the run_only_once decorator can be picked into graphene_django
Whats your opinion @zbyte64 ?

I've tried also the decorator but turn out for me that the middleware run just once and at the first request only, then for the others isn't just executed

@melvinkcx Can you confirm the behaviour?

@melvinkcx
Copy link

melvinkcx commented Feb 5, 2020 via email

@richin13
Copy link

I've tried also the decorator but turn out for me that the middleware run just once and at the first request only, then for the others isn't just executed

I confirm this behaviour is also happening to me. I need my middleware to execute at request level but with run_only_once it executes only on the first request

@melvinkcx
Copy link

@richin13 @mrfoxes This issue is fixed and is now available in graphql-utilities>=0.2.0.

@divyanshusoni21
Copy link

@melvinkcx if you could just tell how to use run_only_once decorator in my django-graphql project . it would be a great help.
PS. : i have gone thorugh the documentation of graphql-utilities project.

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

No branches or pull requests

6 participants