graphql-fields-list
![License](https://img.shields.io/badge/license-ISC-blue.svg)
Add-on to work with GraphQLResolveInfo which helps to extract requested
fields list for a particular object resolver. This helps to bypass
requested fields data to underlying services or data sources to extract
only those minimal parts of data which was requested by end-user.
TypeScript Included!!!
Install
npm i graphql-fields-list
With JavaScript:
const { fieldsList, fieldsMap } = require('graphql-fields-list');
With TypeScript:
import { fieldsList, fieldsMap } from 'graphql-fields-list';
Motivation and Usage
Let's assume we have the following GraphQL schema:
interface Node {
id: ID!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Query {
node(id: ID!): Node
viewer: Viewer
}
type User implements Node {
id: ID!
firstName: String
lastName: String
phoneNumber: String
email: String
}
type UserConnection {
pageInfo: PageInfo!
edges: [UserEdge]
}
type UserEdge {
node: User
cursor: String!
}
type Viewer {
users(
after: String,
first: Int,
before: String,
last: Int
): UserConnection
}
And using the query:
query UserNames query {
viewer {
users {
pageInfo {
startCursor
endCursor
}
edges {
cursor
node {
id
firstName
lastName
}
}
}
}
}
Our goal is to extract and return ONLY id
, firstName
and lastName
fields from the user data. To achieve that we would need to bypass
required fields information to underlying service or database, for
example, let's assume we want to select that kind of data from mongodb.
In this case we will need to implement a resolver which will fetch only
requested fields from our database like this:
const { connectionFromArray } from 'graphql-relay';
const { fieldsList } = require('graphql-fields-list');
async resolve(src, args, context, info) {
const fields = fieldsList(info, { path: 'users.edges.node' });
const users = await userDb.find().select(fields.join(' ')).exec();
return { viewer: { users: connectionFromArray(users, args) } };
}
In the example above we assume our user model in database contains the
same field names as defined by a graphql request. BTW, in a real world,
there could be a need to re-map field names from a graphql query to
some different names stored in a database. For example, we would need
to use automatically created _id
field in mongodb as id
field in
a graphql request. This can be easily achieved specifying a transform
map option:
const fields = fieldsList(info, {
path: 'users.edges.node',
transform: { id: '_id' },
});
By the way, in some particular cases there could be a need to retrieve
a whole fields name hierarchy from a graphql request. This could be
achieved using fieldsMap
function:
const { fieldsMap } = require('graphql-fields-list');
const map = fieldsMap(info);
Function fieldsMap
also accepts path
optional argument, which allows
to retrieve only a required part of the map:
const map = fieldsMap(info, 'users.pageInfo');
For leafs of the fields tree it will return false
value, which is
usable when you need to detect that the end of a tree branch is reached
during traversal.
Both fieldsMap
and fieldsList
works as expected with graphql query
fragmentation, so can be safely used within any possible queries.
License
ISC Licence