-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Schema-level root resolve function #353
Comments
Wow, Interesting idea. Now that context and root are separate, I wonder if a schema level resolve function shouldn't be the initial source of root instead of passing it to execute? I also ran into some complexity in #304 because the select operation was both the root operation and an interior operation. Wonder if this would improve that? Worth some thought. Thanks. |
This is pretty interesting. I suppose we just haven't run into a use case where we would need such a thing. Also resolve functions are currently properties of fields, and this would be a divergence from that. I suppose the question here is what the semantic purpose of such a resolve function should be? For fields, the purpose is to produce a sub-value given a parent value, where fields at the top level of a query accept the "root" value as input. If there were to be a resolver function above the top level of a query responsible for producing the root value, what would its input be? |
@leebyron Isn't that exactly what in |
Having come across that again, I think it would be very useful to have schema-level root resolve function. Given our use-cases the purpose of such root-level resolve (or maybe it should be called differently to not be confusing. Something like Also this would seem to me a good place to create the 'root' values for the top level as @JeffRMoore is saying, and even seems to me more natural then passing it in through the execute call. |
@Globegitter What you described in the last comment reminds me of something that was recently discussed in different places. I actually had very similar kind of use-cases coming from myself and other users of sangria. I introduced the concept of query reducers in sangria in order to address these use-cases. The main idea is to introduce a query analysis phase in between a query validation and execution phases. It is able to see and analyze the whole query and make some interesting things with this information. For example it is able to see whether query has some "protected" fields and if it does, then it will go to an external service and fetch additional user details. It is an expensive operation, so you don't really want to do this if query only asks for a publically visible fields (this is a real use-case that I heard from at least several sangria users): (also look at the next slide where I listed some of the use-cases) In recent discussion about the decorators we also discussed a possibility of query analysis phase. This may be interesting for you, in case you haven't seen it yet: https://github.com/apollostack/graphql-decorators/issues/1#issuecomment-217146736 |
Sorry for the delayed response here. @Globegitter the Relay encourages use of adding a top level I still have some questions as to why a root resolver would be necessary. In my mind you could very easily do this today without any changes to the library: // Today:
var rootObject = { ... }
var results = await graphql(myQuery, myArgs, myContext, rootObject);
// Tomorrow:
var rootObject = { ... }
var moreSpecificRootObject = resolveRoot(myQuery, myArgs, myContext, rootObject);
var results = await graphql(myQuery, myArgs, myContext, moreSpecificRootObject); In my mind anything that you can do before the validation and execution sequence begins should be able to be provided easily outside of the library in this manner. But perhaps I'm missing something, let me know! |
@leebyron thank you for that detailed answer and for giving some clarification on graphql/relay design decisions. And you are right, we have found a way to make this possible using a 'lazy object' (only when a key gets accessed the first time its value gets evaluated and then frozen for subsequent accesses) and passing it in as the The other way of course would be to parse the query before creating that So you are right, it is not necessary to have a root-resolve and more or less everything it already possible today it would imo be very nice to be able to access the parsed query before graphql resolves anything. What you are writing under But that yeah that is making me think of a good approach (which might be what you had in mind already). This |
Great thoughts, thanks for this perspective, @Globegitter. My intuition was that the graphql() function is a good default, but any more sophisticated usage would import the individual steps directly and run them with the sophistication introduced as necessary between parse/validate/execute. I can see why if you wanted to do some "query planning" that you would want to do that between validate and execute and using the parsed query. Does importing the parse/validate/execute functions directly unblock this use case? What should we do to enable this sort of sophistication while keeping the base graphql function very simple? |
In our implement we could pass the filed or resolved field of the current context as a resolved parameter to the field which accept parameters,at the sametime ,we have globally function/field too,I don't want to argue whether that is great or needed,we both have different use cases,yes,we did that way. |
Having worked with GraphQL for a while now, I find that having resolve functions for fields is the really nice abstraction at the core of GraphQL that makes it so powerful. However, there seems to be one point in the query execution where the abstraction breaks down, and I'm not sure why so I thought I'd ask here to be enlightened:
Why is there no resolve function at the root?
Having no resolve function at the root essentially means that there are as many entry points into the graph as there are fields of the query, mutation and subscription types. If you need to do any logic common to all queries (eg. authenticating a user) you either have to create an extra type and make all your queries a field of that, or you have to do it outside of GraphQL and pass the result in as the root value or context. Why is that? Wouldn't it be much nicer, if mutation and subscriptions were considered just like fields of the schema type? By doing that, the resolve tree would be rooted and no longer a forest (it would still be three trees, actually, but if that's an issue we could consider adding a root for that).
With schema-level resolve functions, there would in many cases be no more need for writing custom code that runs outside the resolve functions on the server. Instead, the server (eg. express-graphql) could just pass in the request object (and whatever else it needs) to the root resolve function, which would be the logical place for containing any logic that has to run before any other resolvers run.
I know there will most likely still be a need to run some checks etc. before a request is passed to validation and execution, but I think having a root resolve function would be really nice to have nevertheless because it makes the resolver abstraction more consistent.
The text was updated successfully, but these errors were encountered: