Extracting all children belongs to specific parent in graphql

Extracting all children belongs to specific parent in graphql

I am using GrapgQL and Java. I need to extract all the children belongs to specific parent. I have used the below way but it will fetch only the parent and it does not fetch any children.
schema {
query: Query
}

type LearningResource{
id: ID
name: String
type: String
children: [LearningResource]
}

type Query {
fetchLearningResource: LearningResource
}

@Component

public class LearningResourceDataFetcher implements DataFetcher{
@Override
public LearningResource get(DataFetchingEnvironment dataFetchingEnvironment) {

LearningResource lr3 = new LearningResource();
lr3.setId(“id-03”);
lr3.setName(“Resource-3”);
lr3.setType(“Book”);

LearningResource lr2 = new LearningResource();
lr2.setId(“id-02”);
lr2.setName(“Resource-2”);
lr2.setType(“Paper”);

LearningResource lr1 = new LearningResource();
lr1.setId(“id-01”);
lr1.setName(“Resource-1”);
lr1.setType(“Paper”);

List learningResources = new ArrayList<>();
learningResources.add(lr2);
learningResources.add(lr3);

learningResource1.setChildren(learningResources);

return lr1;
}
}

return RuntimeWiring.newRuntimeWiring().type(“Query”, typeWiring -> typeWiring.dataFetcher(“fetchLearningResource”, learningResourceDataFetcher)).build();

My Controller endpoint
@RequestMapping(value = “/queryType”, method = RequestMethod.POST)
public ResponseEntity query(@RequestBody String query) {
System.out.println(query);
ExecutionResult result = graphQL.execute(query);
System.out.println(result.getErrors());
System.out.println(result.getData().toString());
return ResponseEntity.ok(result.getData());
}

My request would be like below
{
fetchLearningResource
{
name
}
}

Can anybody please help me to sort this ?

Solutions/Answers:

Solution 1:

Because I get asked this question a lot in real life, I’ll answer it in detail here so people have easier time googling (and I have something to point at).

As noted in the comments, the selection for each level has to be explicit and there is no notion of an infinitely recursive query like get everything under a node to the bottom (or get all children of this parent recursively to the bottom).

The reason is mostly that allowing such queries could easily put you in a dangerous situation: a user would be able to request the entire object graph from the server in one easy go! For any non-trivial data size, this would kill the server and saturate the network in no time. Additionally, what would happen once a recursive relationship is encountered?

Related:  GraphQL + Django: resolve queries using raw PostgreSQL query

Still, there is a semi-controlled escape-hatch you could use here. If the scope in which you need everything is limited (and it really should be), you could map the output type of a specific query as a (complex) scalar.

In your case, this would mean mapping LearningResource as a scalar. Then, fetchLearningResource would effectively be returning a JSON blob, where the blob would happen to be all the children and their children recursively. Query resolution doesn’t descent deeper once a scalar field is reached, as scalars are leaf nodes, so it can’t keep resolving the children level-by-level. This means you’d have to recursively fetch everything in one go, by yourself, as GraphQL engine can’t help you here. It also means sub-selections become impossible (as scalars can’t have sub-selections – again, they’re leaf nodes), so the client would always get all the children and all the fields from each child back. If you still need the ability to limit the selection in certain cases, you can expose 2 different queries e.g. fetchLearningResource and fetchAllLearningResources, where the former would be mapped as it is now, and the latter would return the scalar as explained.

An object scalar implementation is provided by the graphql-java ExtendedScalars project.

The schema could then look like:

schema {
    query: Query
}

scalar Object

type Query {
    fetchLearningResource: Object
}

And you’d use the method above to produce the scalar implementation:

RuntimeWiring.newRuntimeWiring()
    .scalar(ExtendedScalars.Object) //register the scalar impl
    .type("Query", typeWiring -> typeWiring.dataFetcher("fetchLearningResource", learningResourceDataFetcher)).build();

Depending on how you process the results of this query, the DataFetcher for fetchLearningResource may need to turn the resulting object into a map-of-maps (JSON-like object) before returning to the client. If you simply JSON-serialize the result anyway, you can likely skip this. Note that you’re side-stepping all safety mechanisms here and must take care not to produce enormous results. By extension, if you need this in many places, you’re very likely using a completely wrong technology for your problem.

Related:  GraphQL: How to implement pagination with graphQL-java?

I have not tested this with your code myself, so I might have skipped something important, but this should be enough to get you (or anyone googling) onto the right track (if you’re sure this is the right track).

UPDATE: I’ve seen someone implement a custom Instrumentation that rewrites the query immediately after it’s parsed, and adds all fields to the selection set if no field had already been selected, recursively. This effectively allows them to select everything implicitly.
In graphql-java v11 and prior, you could mutate the parsed query (represented by the Document class), but as of v12, it will no longer be possible, but instrumentations in turn gain the ability to replace the Document explicitly via the new instrumentDocument method.
Of course, this only makes sense if your schema is such that it can not be exploited or you fully control the client so there’s no danger. You could also only do it selectively for some types, but it would be extremely confusing to use.

References