Catch the highlights of GraphQLConf 2023! Click for recordings. Or check out our recap blog post.
Docs
Transforms
Operational

Operational

It may be sometimes useful to add additional transforms to manually change an operation request or result when using delegateToSchema. Common use cases may be moving selections around or to wrap them. The following built-in transforms may be useful in those cases.

  • WrapFields('ParentType', ['scope'], ['ScopeType'], ['field1', 'field2', ...]) wraps a collection of fields on a parent type in one or more wrapping scopes with given namespaces and object types.
  • ExtractField({ from: Array<string>, to: Array<string> }) move selection at from path to to path.
  • WrapQuery(path: Array<string>, wrapper: QueryWrapper, extractor: (result: any) => any) wrap a selection at path using function wrapper. Apply extractor at the same path to get the result. This is used to get a result nested inside another result.
import { WrapQuery } from '@graphql-tools/wrap'
 
const schema = wrapSchema({
  // ...
  transforms: [
    // Wrap document takes a subtree as an AST node
    new WrapQuery(
      // path at which to apply wrapping and extracting
      ['userById'],
      (subtree: SelectionSetNode) => ({
        // we create a wrapping AST Field
        kind: Kind.FIELD,
        name: {
          kind: Kind.NAME,
          // that field is `address`
          value: 'address'
        },
        // Inside the field selection
        selectionSet: subtree
      }),
      // how to process the data result at path
      result => result?.address
    )
  ]
})

WrapQuery can also be used to expand multiple top-level query fields

import { SelectionSetNode } from 'graphql'
import { WrapQuery } from '@graphql-tools/wrap'
 
const schema = wrapSchema({
  // ...
  transforms: [
    // Wrap document takes a subtree as an AST node
    new WrapQuery(
      // path at which to apply wrapping and extracting
      ['userById'],
      (subtree: SelectionSetNode) => {
        const newSelectionSet = {
          kind: Kind.SELECTION_SET,
          selections: subtree.selections.map(selection => {
            // just append fragments, not interesting for this
            // test
            if (
              selection.kind === Kind.INLINE_FRAGMENT ||
              selection.kind === Kind.FRAGMENT_SPREAD
            ) {
              return selection
            }
            // prepend `address` to name and camelCase
            const oldFieldName = selection.name.value
            return {
              kind: Kind.FIELD,
              name: {
                kind: Kind.NAME,
                value: 'address' + oldFieldName.charAt(0).toUpperCase() + oldFieldName.slice(1)
              }
            }
          })
        }
        return newSelectionSet
      },
      // how to process the data result at path
      result => ({
        streetAddress: result.addressStreetAddress,
        zip: result.addressZip
      })
    )
  ]
})