Skip to main content

Utilities

Overview of the utilities provided by the @jenyus-org/graphql-utils package.

fieldMapToDot (fieldMap: FieldMap): string[]#

New in v1.2.0

Given a FieldMap, this utility will return the dot notations of the field selections.

Example:

const fieldMap: FieldMap = {
user: {
tasks: {},
activities: {
task: {},
},
},
};
const dot = fieldMapToDot(fieldMap);
console.log(dot);

Output:

["user", "user.tasks", "user.activities", "user.activities.task"];

getFieldMap (fieldMap: FieldMap, parent: string | string[]): FieldMap#

New in v1.1.0

Given a FieldMap and the parent argument, this function will return a sub-selection of the field map. This is useful for optimization purposes if a FieldMap from the GraphQLResolveInfo is already available to retrieve selections and subselections using this helper.

getFieldNode (info: Pick<GraphQLResolveInfo, "fieldNodes" | "fragments">, path: string | string[] = []): FieldNode | undefined#

New in v1.3.0

Given the GraphQLResolveInfo, getFieldNode will retrieve the FieldNode at a specified path. The path may be specified with dot notation or as an array of fields. If no FieldNode was found, undefined will be returned.

Note: This function returns a raw FieldNode and doesn't do any remapping of fields or fragments. It's meant for internal use or more advanced users looking to hook into the GraphQL system directly.

hasFields(info: GraphQLResolveInfo, search: string | string[], atRoot: boolean = false): boolean#

Provided the GraphQLResolveInfo from a given query, hasFields will recursively scan through the field selections, including GraphQL fragments for a subselection of fields given to search. It supports dot notation as well as an array structure for the search.

The atRoot parameter allows users to specify whether the utility should only check for fields at the root level, or also go deeper down the tree in order to find the fields. It is recommended to have this enabled in general, but may be useful to disable at times.

QueryLive

Code

import { hasFields } from "@jenyus-org/graphql-utils";
const resolvers = {
Query: {
posts(_, __, ___, info) {
const requestedAuthor = hasFields(info, "posts.author");
const requestedAuthor = hasFields(info, ["posts", "author"]);
const requestedAuthor = hasFields(info, "author", false);
console.log(requestedAuthor);
},
},
};

Output

true
true
true

resolveFieldMap(info: Pick<GraphQLResolveInfo, "fieldNodes" | "fragments">, deep: boolean = true, parent: string | string[] = ""): FieldMap#

New in v1.1.0

Given the GraphQLResolveInfo, this helper will return a FieldMap with all the nested selectors of the query.

The deep argument allows to be specified whether it should only search through a single layer of field selections, or go further down the tree. It is evaluated after the parent has been found, meaning that this allows you to make targeted searches for e.g. user.posts field selections or the sorts.

resolveFields(info: Pick<GraphQLResolveInfo, "fieldNodes" | "fragments">, deep: boolean = true, parent: string | string[] = ""): string[]#

New in v1.1.0

Similar to resolveFieldMap, resolveFields will return either flat or deep field selections made in the query, under the specified parent which may be passed as a dot-notated string or array of fields. The return value will be the dot-notated field selections which are returned by the fieldMapToDot helper.

resolveSelections(fields: (string | FieldSelections)[], info: GraphQLResolveInfo): string[]#

resolveSelections is an extension of hasFields, designed to aid in generating the most efficient SQL queries possible when using an ORM like TypeORM.

resolveSelections takes as its first argument, a deeply nested list of fields to recursively search through, and add to its final return array of selections made in the actual query. For more details on the structure of FieldSelections, see the section below. Just like hasFields it also expects to be passed the GraphQLResolveInfo from the query made.

QueryLive

Code

import { resolveSelections } from "@jenyus-org/graphql-utils";
const resolvers = {
Query: {
posts(_, __, ___, info) {
const fields: FieldSelection[] = [
{
field: "posts",
selections: [
{
field: "author",
selector: "posts.author",
},
{
field: "comments",
selector: "posts.comments",
selections: [
{
field: "author",
selector: "posts.comments.author",
}
],
},
],
},
];
const relations = resolveSelections(fields, info);
console.log(relations);
},
},
};

Output

["posts.author","posts.comments","posts.comments.author"]

As fields can be represented by both objects and strings, they will be handled differently depending on their type. Using the object syntax resolveSelections will ensure the existence of the field, and if a selector value is present, it will be added to the accumulation of selections.

If instead, a string array is passed to fields or selections, the query will be tested for the field and the results will be accumulated as such.

Note: Whenever fields are being tested in the query, the fields higher above are prepended to the check. This means the posts selection will only resolve if user.posts is found, and not simply posts.

Motivation behind resolveSelections#

ORMs like the aforementioned TypeORM can make use of dot notation arguments to automatically JOIN certain entities. While it would be possible to use hasFields to check if the posts have been requested, and then add user.posts to the list of relations to be resolved, resolveSelections provides a much more convenient and readable interface to achieve the same thing.