@yonderbox/graphql-fields
v1.7.0
Published
Turns GraphQLResolveInfo into a map of the requested fields
Downloads
150
Maintainers
Readme
Deprecation Notice
There are many use-cases that are not supported by this library, it is recommended to use something else like graphql-parse-resolve-info
@yonderbox/graphql-fields
Turns GraphQLResolveInfo into a map of the requested fields. Flattens all fragments and duplicated fields into a neat object to easily see which fields were requested at any level. Takes into account any @include
or @skip
directives, excluding fields/fragments which are @include(if: $false)
or @skip(if: $true)
.
Usage
Schema Type definition
const graphqlFields = require('@yonderbox/graphql-fields');
const graphql = require('graphql')
const UserType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
profile: {type: new graphql.GraphQLObjectType({
name: 'Profile',
fields: {
firstName: {type: graphql.GraphQLString},
lastName: {type: graphql.GraphQLString},
middleName: {type: graphql.GraphQLString},
nickName: {type: graphql.GraphQLString},
maidenName: {type: graphql.GraphQLString}
}
}),
email: {type: graphql.GraphQLString},
id: {type: graphql.GraphQLID}
}
});
module.exports = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: () =>
Object.assign({
user: {
type: UserType,
resolve(root, args, context, info) {
console.log(
JSON.stringify(graphqlFields(info), null, 2)
);
...
}
}
})
})
})
Query
{
user {
...A
profile {
...B
firstName
}
}
}
fragment A on User {
...C
id,
profile {
lastName
}
}
Fragment B on Profile {
firstName
nickName @skip(if: true)
}
Fragment C on User {
email,
profile {
middleName
maidenName @include(if: false)
}
}
will log
{
"profile": {
"firstName": {},
"lastName": {},
"middleName": {}
},
"email": {},
"id": {}
}
subfields arguments
To enable subfields arguments parsing, you'll have to provide an option object to the function. This feature is disable by default.
const graphqlFields = require('@yonderbox/graphql-fields');
const fieldsWithSubFieldsArgs = graphqlFields(info, {}, { processArguments: true });
For each subfield w/ arguments, a __arguments
property will be created.
It will be an array with the following format:
[
{
arg1Name: {
kind: ARG1_KIND,
value: ARG1_VALUE,
},
},
{
arg2Name: {
kind: ARG2_KIND,
value: ARG2_VALUE,
}
}
]
The kind property is here to help differentiate value cast to strings by javascript clients, such as enum values.
Exclude specific fields
Most of the time we don't need __typename
to be sent to backend/rest api, we can exclude __typename
using this:
const graphqlFields = require('@yonderbox/graphql-fields');
const fieldsWithoutTypeName = graphqlFields(info, {}, { excludedFields: ['__typename'] });
Include alias name
This option returns alias with field name:
const graphqlFields = require('@yonderbox/graphql-fields');
const fieldsWithoutTypeName = graphqlFields(info, {}, { includeAliasName: true });
Request:
{
person {
aliasName: name
}
}
Parsed info
object:
{
aliasName:name: {}
}
Why
An underlying REST api may only return fields based on query params.
{
user {
profile {
firstName
},
id
}
}
should request /api/user?fields=profile,id
while
{
user {
email
}
}
should request /api/user?fields=email
Implement your resolve method like so:
resolve(root, args, context, info) {
const topLevelFields = Object.keys(graphqlFields(info));
return fetch(`/api/user?fields=${topLevelFields.join(',')}`);
}
Tests
npm test