@Recurse GraphQL

Hi, is a way to obtain subgraph after making @recurse query on the client without hitting DQL endpoint? DQL doesn’t respect @auth, so it’s not the option. As a possible solution I consider setting up custom resolvers on remote, but I believe there might be a more convenient way.

1 Like

@pawan @LGalatin is this on our roadmap?

Hey @alexanderkomeiji

It’s not exactly clear to me what the ask is so I’d need more info here. Are you trying to use @recurse as part of a GraphQL query? Could you share what query you plan to run and what is the output that you expect?

Sorry for not beign clear. The question is about crud protection of certain nodes in graph.
What’s I’m trying to do, is to having a json/GraphQL response describing subgraph after making recursive query against supergraph. This is pretty trivial with DQL itself, you’re making @recurse and free to go. The question is, how to pass the result to authorized to do it client. In current situation, you’re either exposing /admin endpoint with unrestricted access, or making type-level auth in GraphQL schema and then nest each level of the recursion manually on the client side, fields inside fields…
A possible workaround is to setting up a recursive resolver in the middle, like a server with Apollo and direct queries from DGraph to Apollo (@custom directive), then back with modified recursive query. I considered the @lambda and custom DQL inside the GraphQL schema, but it didn’t add up much.

Sorry, still not very clear. Are you trying to apply auth on fields while doing a recurse query. Can you share a sample schema, the query that you want to run and the result that you expect back?

type User {
id: ID!
has: [Node] @hasInverse(field: user)
}
type Node {
descendant_of: Node @hasInverse(field: descendants)
descendant: Node @hasInverse(field: descendant_of)
user: User@hasInverse(field: has)
}

Desired result: a branch of a tree. With given 6 descendants of 1 node the query for DQL looks like:

{query(func: uid(0x2)) @recurse {
uid
Node.descendant}}

And response:


{"data": {"query": [{
"uid": "0x2",
"Node.descendant": {
"uid": "0x3",
"Node.descendant": {
"uid": "0x4",
"Node.descendant": {
"uid": "0x5",
"Node.descendant": {
"uid": "0x6",
"Node.descendant": {
"uid": "0x7",
"Node.descendant": {
"uid": "0x8" }}}}}}}]}

In GraphQL query:

query {queryNode(filter:{id:["0x2"]}){
id
descendant: {
id
descendant: {
id
descendant: {
id
descendant: {
id
descendant: {
id
descendant: {
id }}}}}}}}

The response is the same. Regarding this,
Issue 1, GraphQL approach: In GraphQL it’s a known issue. To query a tree you have to nest each level in the query and implement pagination and write fragments in the case of a large multibranch tree.
Issue 2, DQL approach: It’s possible to take another approach and “just” issue DQL queries against the database. Then you need to write authorization layer in the case of a conventional app with many users and with access rules for each node. Dgraph provides built-in auth for GraphQL already, so it’d would be sweet to take advantage of it.

Originally I tagged the topic with GraphQL Feature tag, and simply meant to ask to implement @recurse directive.
Tonight I’ve read a bit more issues and realized that it goes down the rabbit hole. With each unreflected in GraphQL feature, like cascade deletion, you have to switch to DQL, and then it’s going back to the square one (look up “authorization” issue). So as far as I understand, in my case it’s obligatory to set up intermediate authorization layer in between Dgraph and the client. Sorry for taking your time.

FYI, @has_reverse should be @hasInverse

Indeed.

If you have not already, I highly recommend watching/reading:

The rabbit whole is why DQL (originally GraphQL±) needed to be created in the first place. There are things that GraphQL just simply cannot do and you found one of them.

GraphQL is great because you only get back what you ask for and no more, but GraphQL is awful because you only get back what you ask for and no more. It all depends on the use case if this is a pro or a con. In rdbs we are use to saying SELECT * FROM ... which even though we knew was a bad practice, we still did it anyways. Then we came to GraphQL and fell in love, but realized some of the things that are easy in a rdbs are impossible. We can no longer just say select all, which is something that we want. This and a boatload of other reasons and restrictions on the GraphQL standard led the Dgraph devs to develop their own GraphQL like language giving them the best of both worlds. Not only can you then do things like expand(_all_) but they also added in things that rdbs systems could never even have thought of @recurse just think of the join statement that would require in a rdbs.

And here is the big kicker… When Dgraph decided to make the trek back to GraphQL and support both a native GraphQL layer and DQL endpoints they added additional functionality on the GraphQL endpoint such as auth that is an abstracted function (I don’t like to use the term layer because it is not a whole layer). The problem with this though is that now we have an awesome feature that works on one endpoint but not the others. So it becomes a trade-off. What features do you really need the most and what can you do without or work around.

I had a conversation last week about my work project about how hard it would be to take our auth rules and actually make them available in our DQL queries… that was interesting. Basically rebuilding everything the Dgraph team has done on the GraphQL endpoints so we could have X feature. hmm… benefit vs. cost was not there, so we will just work around it until more features are developed which the team is doing a great job on!

2 Likes

Thanks for the detailed answer.

FYI, @has_reverse should be @hasInverse

Yes, thanks, typed manually, forgot to fix.

There are things that GraphQL just simply cannot do and you found one of them.

Yes, I remember it was the first major cool off about GraphQL for me.

so we will just work around it until more features are developed which the team is doing a great job on!

For 4 y.o. database it’s an incredible pace of development.

1 Like