GraphQL queries with tables join using Node.js
The concept you are refering to is called batching. There are several libraries out there that offer this. For example:
Dataloader: generic utility maintained by Facebook that provides "a consistent API over various backends and reduce requests to those backends via batching and caching"
join-monster: "A GraphQL-to-SQL query execution layer for batch data fetching."
To anyone using .NET and the GraphQL for .NET package, I have made an extension method that converts the GraphQL Query into Entity Framework Includes.
public static class ResolveFieldContextExtensions{ public static string GetIncludeString(this ResolveFieldContext<object> source) { return string.Join(',', GetIncludePaths(source.FieldAst)); } private static IEnumerable<Field> GetChildren(IHaveSelectionSet root) { return root.SelectionSet.Selections.Cast<Field>() .Where(x => x.SelectionSet.Selections.Any()); } private static IEnumerable<string> GetIncludePaths(IHaveSelectionSet root) { var q = new Queue<Tuple<string, Field>>(); foreach (var child in GetChildren(root)) q.Enqueue(new Tuple<string, Field>(child.Name.ToPascalCase(), child)); while (q.Any()) { var node = q.Dequeue(); var children = GetChildren(node.Item2).ToList(); if (children.Any()) { foreach (var child in children) q.Enqueue(new Tuple<string, Field> (node.Item1 + "." + child.Name.ToPascalCase(), child)); } else { yield return node.Item1; } }}}
Lets say we have the following query:
query { getHistory { id product { id category { id subCategory { id } subAnything { id } } } }}
We can create a variable in "resolve" method of the field:
var include = context.GetIncludeString();
which generates the following string:
"Product.Category.SubCategory,Product.Category.SubAnything"
and pass it to Entity Framework:
public Task<TEntity> Get(TKey id, string include){ var query = Context.Set<TEntity>(); if (!string.IsNullOrEmpty(include)) { query = include.Split(',', StringSplitOptions.RemoveEmptyEntries) .Aggregate(query, (q, p) => q.Include(p)); } return query.SingleOrDefaultAsync(c => c.Id.Equals(id));}