question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Retrieve parent resolver context into sub-query resolver

See original GitHub issue

TL;DR

The use case I’m facing is that I want access to some data computed on the parent resolver in the child resolver.

Description

If we have a query like this:

query {
  library (name: "Borders Library") {
    id
    books (genre: "horror") {
      name
      code
    }
}

The computation of this single query is resolved from an outside-inside fashion. Where the parent library object type is resolved first, then the books child object types are resolved at last.

In this child books resolver, for instance, we might want to sub-set our books based on the library name and book genre. As the computation flows from outside to inside, it feels pretty natural that it might exist a way to record some notion of state/context in the parent resolver, in a way that it could be accessible from the child resolver to help with the computations.

As far as I know, there’s no way to accomplish this in the current state of the framework. I know it would be fragile to implement some ad-hoc logic for this purpose as in a multi-user case it’s hard to pass context from one block of code to another, where this will most likely fall into a multiple producer-consumer problem.

Proposed solution

There’s one place I think this would fit well, and it’s on the info: Info object passed to every resolver.

There could be some dict-like or something where you could put stuff in the parent resolver, that might be accessible from the child resolver’s info parameter.

What have I tried so far

One resolver to rule them all

The most simple solution I can think of is to move all inputs into the parent query like this:

query {
  library (name: "Borders Library", book_genre: "horror") {
    id
    books {
      name
      code
    }
}

And then I can use the parent resolver to compute and populate all fields in one single resolver. But, I feel this solution is no so elegant.

Hacky resolving

By using the info object I can access the raw query, so I could implement the previous solution without moving the books inputs into the parent. In other words, I can retrieve those parameters from the parent resolver from the raw query, and solve everything in one resolver, but making the user think they are different inputs.

### Context object I was thinking of implementing a singleton object to publish some data that the resolvers could access. But the more I think about it, the more complex it sounds because there might be multiple producers and multiple readers. I can’t think of a simple way to coordinate this without unexpected edge cases.

Questions

I want to know if there’s any abstraction that might help me with this use case.

Also as there are no abstractions yet, maybe my approach is a code-smell symptom so feel free to discuss with me any alternatives.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
jkimbocommented, Aug 19, 2021
1reaction
jkimbocommented, Aug 18, 2021

@Vichoko what might help you is strawberry.Private which lets you attach extra information to types without it being exposed in the schema (documentation is coming #450 ).

So your query might be implemented like this:

import strawberry

@strawberry.type
class Library:
	id: str
	name: strawberry.Private[str]  # Don't expose this to the schema
	
	@strawberry.field
	def books(self, genre: str) -> List[Book]:
		books = get_books(
			library_name=self.name,  # use the library name here to get a list of books
			genre=genre,
		)
		return [Book(name=book.name, code=book.code) for book in books]

@strawberry.type
class Query:
	@strawberry.field
	def library(self, name: str) -> Library:
		return Library(id="", name=name)

By using strawberry.Private you can avoid having to store anything on the context object. As you rightly point out it could lead to some weird bugs.

Another thing to note: strawberry.Private can annotate any type so you could pass the whole DB instance to the Library type and make use of it in the books resolver. Thats what I do when integrating with Django models and it works well.

Hope that helps!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Resolvers - Apollo GraphQL Docs
A resolver is a function that's responsible for populating the data for a single field in your schema. It can populate that data...
Read more >
Apollo delegateToSchema - how to send subquery of child ...
I have parent schema in graphql that built with apollo libraries apollo-server and graphql-tools that delegates subqueries to remote graphql ...
Read more >
How to properly flow arguments to sub-queries? #107 - GitHub
Hi I have a situation where retrieving a subnode needs an argument passed to the parent node. I am not sure how to...
Read more >
Resolving nested queries in GraphQL using Apollo Server
Let us look into how we can do this using nested queries in GraphQL. ... The posts query is invoked by the author...
Read more >
GraphQL Resolvers: Best Practices | by Mark Stuart - Medium
root — Result from the previous/parent type; args — Arguments provided to the field; context — a Mutable object that is provided to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found