npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dwwoelfel/graphql-parse-resolve-info

v4.4.5-alpha.0

Published

Parse GraphQLResolveInfo (the 4th argument of resolve) into a simple tree

Downloads

11

Readme

graphql-parse-resolve-info

Discord chat room Package on npm MIT license Follow

Parses a GraphQLResolveInfo object into a tree of the fields that are being requested to enable optimisations to your GraphQL schema (e.g. to determine which fields are required from the SQL database).

Useful for optimising your GraphQL resolvers by allowing them to look ahead in the request, reducing the number of SQL queries or HTTP requests required to complete the GraphQL request.

Also provides a quick way to get the alias of the current field being resolved.

API

parseResolveInfo(resolveInfo)

Alias: parse

Gets the tree of subfields of the current field that is being requested, returning the following properties (recursively):

  • name: the name of the GraphQL field
  • alias: the alias this GraphQL field has been requested as, or if no alias was specified then the name
  • args: the arguments this field was called with; at the root level this will be equivalent to the args that the resolve(data, args, context, resolveInfo) {} method receives, at deeper levels this allows you to get the args for the nested fields without waiting for their resolvers to be called.
  • fieldsByTypeName: an object keyed by GraphQL object type names, where the values are another object keyed by the aliases of the fields requested with values of the same format as the root level (i.e. {alias, name, args, fieldsByTypeName}); see below for an example

Note that because GraphQL supports interfaces a resolver may return items of different types. For this reason, we key the fields by the GraphQL type name of the various fragments that were requested into the fieldsByTypeName field.

Once you know which specific type the result is going to be, you can then use this type (and its interfaces) to determine which sub-fields were requested - we provide a simplifyParsedResolveInfoFragmentWithType helper to aid you with this. In many cases you will know what type the result will be (because it can only be one type) so you will probably use this helper heavily.

Example usage:

const {
	parseResolveInfo,
	simplifyParsedResolveInfoFragmentWithType
} = require('graphql-parse-resolve-info');
// or import { parseResolveInfo, simplifyParsedResolveInfoFragmentWithType } from 'graphql-parse-resolve-info';

new GraphQLObjectType({
  name: ...
  fields: {
    ...
    foo: {
      type: new GraphQLNonNull(ComplexType),
      resolve(data, args, context, resolveInfo) {
        const parsedResolveInfoFragment = parseResolveInfo(resolveInfo);
        const { fields } = simplifyParsedResolveInfoFragmentWithType(
					parsedResolveInfoFragment,
					ComplexType
				);
        console.dir(fields);
        ...
      }
    }
  }
});

simplifyParsedResolveInfoFragmentWithType(parsedResolveInfoFragment, ReturnType)

Alias: simplify

Given an object of the form returned by parseResolveInfo(...) (which can be the root-level instance, or it could be one of the nested subfields) and a GraphQL type this method will return an object of the form above, with an additional field fields which only contains the fields compatible with the specified ReturnType.

Or, in other words, this simplifies the fieldsByTypeName to an object of only the fields compatible with ReturnType.

Example usage:

const {
	parseResolveInfo,
	simplifyParsedResolveInfoFragmentWithType
} = require('graphql-parse-resolve-info');

new GraphQLObjectType({
  name: ...
  fields: {
    ...
    foo: {
      type: new GraphQLNonNull(ComplexType),
      resolve(data, args, context, resolveInfo) {
        const parsedResolveInfoFragment = parseResolveInfo(resolveInfo);

        const { fields } = simplifyParsedResolveInfoFragmentWithType(
					parsedResolveInfoFragment,
					ComplexType
				);
        ...
      }
    }
  }
});

getAliasFromResolveInfo(resolveInfo)

Alias: getAlias

Returns the alias of the field being requested (or, if no alias was specified, then the name of the field).

Example:

const { getAliasFromResolveInfo } = require('graphql-parse-resolve-info');

new GraphQLObjectType({
  name: ...
  fields: {
    ...
    foo: {
      type: new GraphQLNonNull(GraphQLString),
      resolve(data, args, context, resolveInfo) {
        const alias = getAliasFromResolveInfo(resolveInfo);
        return alias;
      }
    }
  }
});

Example

For the following GraphQL query:

{
  allPosts {
    edges {
      cursor
      node {
        ...PostDetails
        author: personByAuthorId {
          firstPost {
            ...PostDetails
          }
          friends {
            nodes {
              ...PersonDetails
            }
            totalCount
            pageInfo {
              startCursor
            }
          }
        }
      }
    }
  }
}

fragment PersonDetails on Person {
  id
  name
  firstName
}

fragment PostDetails on Post {
  id
  headline
  headlineTrimmed
  author: personByAuthorId {
    ...PersonDetails
  }
}

The following resolver in the allPosts field:

const Query = new GraphQLObjectType({
  name: "Query",
  fields: {
    allPosts: {
      type: new GraphQLNonNull(PostsConnection),
      resolve(parent, args, context, resolveInfo) {
        const parsedResolveInfoFragment = parseResolveInfo(resolveInfo);
        const simplifiedFragment = simplifyParsedResolveInfoFragmentWithType(
          parsedResolveInfoFragment,
          resolveInfo.returnType
        );
        // ...
      },
    },

    // ...
  },
});

has parsedResolveInfoFragment:

{ alias: 'allPosts',
  name: 'allPosts',
  args: {},
  fieldsByTypeName:
   { PostsConnection:
      { edges:
         { alias: 'edges',
           name: 'edges',
           args: {},
           fieldsByTypeName:
            { PostsEdge:
               { cursor:
                  { alias: 'cursor',
                    name: 'cursor',
                    args: {},
                    fieldsByTypeName: {} },
                 node:
                  { alias: 'node',
                    name: 'node',
                    args: {},
                    fieldsByTypeName:
                     { Post:
                        { id: { alias: 'id', name: 'id', args: {}, fieldsByTypeName: {} },
                          headline:
                           { alias: 'headline',
                             name: 'headline',
                             args: {},
                             fieldsByTypeName: {} },
                          headlineTrimmed:
                           { alias: 'headlineTrimmed',
                             name: 'headlineTrimmed',
                             args: {},
                             fieldsByTypeName: {} },
                          author:
                           { alias: 'author',
                             name: 'personByAuthorId',
                             args: {},
                             fieldsByTypeName:
                              { Person:
                                 { id: { alias: 'id', name: 'id', args: {}, fieldsByTypeName: {} },
                                   name: { alias: 'name', name: 'name', args: {}, fieldsByTypeName: {} },
                                   firstName:
                                    { alias: 'firstName',
                                      name: 'firstName',
                                      args: {},
                                      fieldsByTypeName: {} },
                                   firstPost:
                                    { alias: 'firstPost',
                                      name: 'firstPost',
                                      args: {},
                                      fieldsByTypeName:
                                       { Post:
                                          { id: { alias: 'id', name: 'id', args: {}, fieldsByTypeName: {} },
                                            headline:
                                             { alias: 'headline',
                                               name: 'headline',
                                               args: {},
                                               fieldsByTypeName: {} },
                                            headlineTrimmed:
                                             { alias: 'headlineTrimmed',
                                               name: 'headlineTrimmed',
                                               args: {},
                                               fieldsByTypeName: {} },
                                            author:
                                             { alias: 'author',
                                               name: 'personByAuthorId',
                                               args: {},
                                               fieldsByTypeName:
                                                { Person:
                                                   { id: { alias: 'id', name: 'id', args: {}, fieldsByTypeName: {} },
                                                     name: { alias: 'name', name: 'name', args: {}, fieldsByTypeName: {} },
                                                     firstName:
                                                      { alias: 'firstName',
                                                        name: 'firstName',
                                                        args: {},
                                                        fieldsByTypeName: {} } } } } } } },
                                   friends:
                                    { alias: 'friends',
                                      name: 'friends',
                                      args: {},
                                      fieldsByTypeName:
                                       { PeopleConnection:
                                          { nodes:
                                             { alias: 'nodes',
                                               name: 'nodes',
                                               args: {},
                                               fieldsByTypeName:
                                                { Person:
                                                   { id: { alias: 'id', name: 'id', args: {}, fieldsByTypeName: {} },
                                                     name: { alias: 'name', name: 'name', args: {}, fieldsByTypeName: {} },
                                                     firstName:
                                                      { alias: 'firstName',
                                                        name: 'firstName',
                                                        args: {},
                                                        fieldsByTypeName: {} } } } },
                                            totalCount:
                                             { alias: 'totalCount',
                                               name: 'totalCount',
                                               args: {},
                                               fieldsByTypeName: {} },
                                            pageInfo:
                                             { alias: 'pageInfo',
                                               name: 'pageInfo',
                                               args: {},
                                               fieldsByTypeName:
                                                { PageInfo:
                                                   { startCursor:
                                                      { alias: 'startCursor',
                                                        name: 'startCursor',
                                                        args: {},
                                                        fieldsByTypeName: {} } } } } } } } } } } } } } } } } } },

and the simplified simplifiedFragment is the same as parsedResolveInfoFragment, but with the additional root-level property fields which compresses the root-level property fieldsByTypeName to a single-level object containing only the fields compatible with resolveInfo.returnType (in this case: only edges):

{ alias: 'allPosts',
  name: 'allPosts',
  args: {},
  fieldsByTypeName:
		...as before...
  fields:
   { edges:
      { alias: 'edges',
        name: 'edges',
        args: {},
        fieldsByTypeName:
           ...as before...

Thanks

This project was originally based on https://github.com/tjmehta/graphql-parse-fields, but has evolved a lot since then.