graphql-query-path icon indicating copy to clipboard operation
graphql-query-path copied to clipboard

Library to use field selection to mitigate the N+1 problem of GraphQL queries

graphql-query-path

A library that allows you to smartly execute database queries by looking at the field selection. This can mitigate the N+1 and even 1+1 problem of GraphQL queries.

follow on Twitter

This repo contains two projects:

  • graphql-query-path that has two functions: getPaths and getPathsFromAST. They return a list of paths reflecting the graphql-query
  • graphql-query-path-contains the same as above and extends Array with contains(glob: string): boolean method that you can use to do glob matching. This one is ~17k bigger because of a dependency on picomatch.

What is it

Given the following query

query {
  user {
    name
    posts {
      title
      content
    }
  }
}

the getPaths(info) returns the following array

[
  '/user',
  '/user/name',
  '/user/posts/',
  '/user/posts/title',
  '/user/posts/content',
];

This array can give the information you need to execute a querybuilder or call other APIs in an efficient way.

Usage

Install the package

npm i graphql-query-path

Use it in your graphql-resolver:

import { getPaths } from 'graphql-query-paths';
// or
// const { getPaths } = require('graphql-query-paths');
const resolvers = {
  user(args, context, info) {
    // for example this query comes in
    // query: {
    //   user {
    //     name
    //     posts {
    //       title
    //       content
    //     }
    //   }
    // }
    const paths = getPaths(info);
    // paths: [
    //   '/user',
    //   '/user/name',
    //   '/user/posts/',
    //   '/user/posts/title',
    //   '/user/posts/content'
    // ]
    if (paths.find((p) => p.indexOf('/user/posts/') > -1)) {
      db.getUsersWithPosts();
    } else {
      db.getUsers();
    }
  },
};

Use the extended version to match glob pattern with contains from graphql-query-paths-contains. This includes picomatch but increases the lib size by ~17k.

npm i graphql-query-paths-contains
import { getPaths } from 'graphql-query-paths-contains';
// or
// const { getPaths } = require('graphql-query-paths-contains');
const resolvers = {
  user(args, context, info) {
    if (getPaths(info).contains("/users/posts/"))) {
      db.getUsersWithPosts();
    } else {
      db.getUsers();
    }
  },
};

Interface docs

Library graphql-query-paths

function/argument type description
getPathsFromAST(ast) string[][] Returns a list of subqueries with paths reflected in the sub query per subquery
ast DocumentNode link The DocumentNode from import { parse } from 'graphql'
getPaths(info) string[] Returns a list of paths reflected in the query
info GraphQLResolveInfo link The last argument in a resolver

Library graphql-query-paths-contains extends the library above with a contains function

function/argument type description
Array.prototype.contains(glob) boolean Extends Array with contains function. To know if a result contains a path you can execute getPaths(info).contains("/user/**"). This returns a boolean
glob string a string representing a glob to filter the array with

Potential features

  • [ ] Create a pathContains(info, pattern) function that can lazily find instead of extracting all paths firsts

Author

Albert Groothedde

Contributing

Contributions, issues and feature requests are welcome!

Feel free to check issues page.

Show your support

Give a ⭐️ if this project helped you!


This README was generated by readme-md-generator