How to add a field to a GraphQL document?

Viewed 26

Let's say this is my query:

mutation Test ($input: UpdateUserAccountInput!) {
  updateUserAccount(input: $input) {
    ... on UpdateUserAccountPayload {
      userAccount {
        name
      }
    }
  }
}

I want to modify to have the following fragment:

... on Error {
  message
}

I was able to figure out that I can get AST using parse from graphql package, i.e.

import {
  parse,
  gql,
} from 'graphql';

parse(gql`
  mutation Test ($input: UpdateUserAccountInput!) {
    updateUserAccount(input: $input) {
      ... on UpdateUserAccountPayload {
        userAccount {
          name
        }
      }
    }
  }
`)

Now I am trying to figure out how to add ... on Error { message } to this query.

The problem that I am trying to solve is that my tests sometimes quietly fail because mutation returns an error that I did not capturing. I am extending my GraphQL test client to automatically request errors for every mutation and throw if error is returned.

I assume there exists some utilities that allow me to inject fields into AST, but so far I was not able to find them.

1 Answers

I think I figured it out.

Here is my solution:

const appendErrorFieldToMutation = (query: string) => {
  const inlineErrorFragmentTemplate = {
    kind: 'InlineFragment',
    selectionSet: {
      kind: 'SelectionSet',
      selections: [
        {
          kind: 'Field',
          name: {
            kind: 'Name',
            value: 'message',
          },
        },
      ],
    },
    typeCondition: {
      kind: 'NamedType',
      name: {
        kind: 'Name',
        value: 'Error',
      },
    },
  };

  const document = parseGraphQL(query);

  if (document.definitions.length !== 1) {
    throw new Error('Expected only one definition');
  }

  const definition = document.definitions[0] as OperationDefinitionNode;

  if (definition.operation !== 'mutation') {
    return query;
  }

  if (definition.selectionSet.selections.length !== 1) {
    throw new Error('Expected only one document selection');
  }

  const documentSelection = definition.selectionSet.selections[0] as InlineFragmentNode;

  const errorField = documentSelection.selectionSet.selections.find((selection) => {
    return (selection as InlineFragmentNode).typeCondition?.name.value === 'Error';
  });

  if (!errorField) {
    // @ts-expect-error – Intentionally mutating the AST.
    documentSelection.selectionSet.selections.unshift(inlineErrorFragmentTemplate);
  }

  return printGraphQL(document);
};

I've not used any utilities, so perhaps there is a smarter way to do the same.

Related