Giter VIP home page Giter VIP logo

Comments (7)

cuchac avatar cuchac commented on July 28, 2024 1

Hello,
from my point of view, there should be support inside the library. May be I'm wrong, but I think it is quite common use-case to have different caching policies for different queries in combined query. I can ask in one query for both "static" and "dynamic" content. For example: Give me currently logged-in user (dynamic) and main menu items (static).
Caching inside controller is unable to cope with this. Could middleware be able to do this caching of one query and avoid caching of another?

from graphqlite.

moufmouf avatar moufmouf commented on July 28, 2024

Hey @pascalwacker ,

Indeed, a special annotation like @Cache is something that could be done.
We added the ability to add custom annotations in GraphQLite 4: https://graphqlite.thecodingmachine.io/docs/next/field-middlewares

It should therefore be relatively easy to cache the query (and compute automatically the cache key from the method's parameters as you suggest).

That being said, there are a few caveats.
The main problem is that if your query returns objects, those objects must be cachable. If you put these objects in cache, you have to put the dependencies of the objects too (and the whole object graph that could be queried through GraphQL). I soon realized that serializing the objects to put in cache is the real hard part.

In my project, I found out it was actually way easier to add caching at the HTTP middleware level. Basically, I'm parsing the GraphQL query before GraphQLite is even started and I'm caching the query and the result. The result if a JSON string so I'm 100% sure it is cachable.

Of course, your mileage may vary though. I'd be curious to know what you think? Don't you think caching at the middleware level might be easier in your case?

from graphqlite.

oojacoboo avatar oojacoboo commented on July 28, 2024

@moufmouf is this something that still needs to be considered? Are there any real advantages to adding this to GraphQLite over just manually handling your caching in your query logic?

from graphqlite.

oojacoboo avatar oojacoboo commented on July 28, 2024

What's the overall object here for caching? I'm assuming it's mostly around eliminating unnecessary database queries? If so, this is something you should handle with your ORM. Is the building out of the output types/fields for a query really that intensive for you to need to cache those?

from graphqlite.

cuchac avatar cuchac commented on July 28, 2024

OK I have another use-case. I have web app and want to load all data from GraphQL endpoint. With decent traffic I have to use some caching technique. To initialize graphqlite library, parse query and serialize let's say 50 objects in each request is too much work to handle the load. I can cache on controller, I can cache on CDN (my prefered solution) I can cache on proxy.
I need a way to set caching policy (whether to cache or not, expiration time) based on parsed query - different queries have different expiration and caching. I'm unable to do this inside controller.
Maybe some caching middleware would be ideal for this task?

from graphqlite.

oojacoboo avatar oojacoboo commented on July 28, 2024

Here is a request body parser that you can use. We use this for parsing requests in order to determine some of our authz. We use this as part of the middleware. But, you could use it in a controller via another service and the request object.

    /**
     * Parses the request body JSON and populates the request object's parsedBody property
     *
     * We're checking to see if the body has already been pre-parsed, as is happening with the upload
     * handler middleware.  It needs to add the uploadedFiles to the request.  The funny thing is that
     * it's using an "operations" key.  So, we're just getting rid of it essentially and using the "query"
     * key, since that's all we really need out of this right now anyway.
     *
     * @see https://github.com/Ecodev/graphql-upload/issues/7
     */
    public static function parseRequestBody(ServerRequestInterface $request): DocumentNode
    {
        $parsedBody = $request->getParsedBody();
        if (!$parsedBody) {
            $contents = $request->getBody()->getContents();
            $parsedBody = json_decode($contents, true);
            if ($parsedBody === false || json_last_error() !== \JSON_ERROR_NONE) {
                throw new BadRequest(json_last_error_msg() . ' in body: "' . $contents . '"');
            }
        }

        $operations = $parsedBody['operations'] ?? null;

        if ($operations) {
            $decoded = json_decode($operations, true);
            $query = $decoded['query'] ?? null;
        } else {
            $query = $parsedBody['query'] ?? null;
        }

        if (!$operations && !$query) {
            throw new UnprocessableEntity('GraphQL request must include query param');
        }

        try {
            $document = GraphQLParser::parse($query, [
                'noLocation' => true,
            ]);
        } catch (\Throwable $e) {
            throw ExceptionConverter::convert($e);
        }

        return $document;
    }

    $document = Helper::parseRequestBody($request);

    // If any field is not in our nonSecuredOpreations array, require Auth
    foreach ($document->definitions as $definition) {
        if (strtolower($definition->kind) !== 'operationdefinition') {
            continue;
        }

        foreach ($definition->selectionSet->selections as $field) {
            if (!in_array($field->name->value, $this->nonSecuredOperations)) {
                return true;
            }
        }
    }

from graphqlite.

oojacoboo avatar oojacoboo commented on July 28, 2024

@cuchac I think we should add a property to the Query and Mutation annotations, similar to Symfony's Route annotation with stateless routes:

https://symfony.com/doc/current/routing.html#stateless-routes

from graphqlite.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.