GraphQL queries with tables join using Node.js GraphQL queries with tables join using Node.js sql sql

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));}