How to add an additional field to an existing query with a plugin?

Assuming there is an already existing query CartQuery , which has GraphQL fields defined in the function getAddProductToCartMutation , imagine you want to add additional customResults field with customErrors subfields:

The Field object returns a Field when addArgument or addField is called on it, so all that you need to do actually is call the original getAddProductToCartMutation function, which will return a field, and add a field to it.

<aside> ➡️ How to call functions with plugins? According to plugin docs, since the function we’re plugging into is inside a class, we need a member-function plugin.

</aside>

The initial idea might be to re-create all fields and return the new fields in function result like so:

const getCustomResultsField = () => {
		return new Field('customResults')
			.addField('customErrors');
}

export default {
  'Query/Cart/Query': {
    'member-function': {
			getAddProductToCartMutation: (args, callback) => {
		    return new Field('addProductsToCart')
          .addArgument('cartId', 'String!', cartId)
          .addArgument('cartItems', '[CartItemInput!]!', cartItems)
          .addField(this._getUserErrorsField())
					// vvv new Custom field
					.addField(this.getCustomResultsField());
			}	
		}
  }
}

But, remember that you should not overwrite functions logics with plugins, always extend them.

<aside> ℹ️ Ideally, you should consider the result of render function execution as a black box and operate on it assuming it returns a valid type, in our case ReactElement.

</aside>

So let’s replace the original function’s result with callback(...args), which is the original function being called:

const getCustomResultsField = () => {
		return new Field('customResults')
			.addField('customErrors');
}

export default {
  'Query/Cart/Query': {
    'member-function': {
			getAddProductToCartMutation: (args, callback) => {
				return callback(...args)
					.addField(this.getCustomResultsField());
			}	
		}
  }
}

This is it! But, there are a few things to consider. You need to refactor the plugin itself into a separate function, and getCustomResultsField should not be defined inside plugin**, it should be defined inside src/query folder as a separate class** (To keep up with consistency of core project).

class CartCustomResultsQuery {
	getCustomResultsField() {
		return new Field('customResults')
			.addField('customErrors');
	}
}

export default new CartCustomResultsQuery();

And finally, refactor the plugin to a separate function, so you improve the readability and maintenance of the code:

import CartCustomResultsQuery from '../query/CartCustomResults.query';

const addCustomResultsFieldToCartMutationFields = (args, callback) => {
	return callback(...args)
		.addField(CartCustomResultsQuery.getCustomResultsField());
};

export default {
  'Query/Cart/Query': {
    'member-function': {
			getAddProductToCartMutation: addCustomResultsFieldToCartMutationFields
		}
  }
}

What are the real examples of this?

In this example, additional field dimensions is added to existing _getCartProductField .

How to add multiple fields to existing query fields using a plugin?

Let’s say you want to add multiple fields called customRating, customReviewCounts, customReactions into _getCartProductInterfaceFields to extend a product data with more fields.

To achieve this, you can plugin into _getCartProductInterfaceFields and extend the result array with additional fields. And according to plugin docs, for that, you should use also member-function plugin:

const getCustomFields = () => {
		return [
					'customRating',
					'customReviewCounts',
					'customReactions'
		];
}

export default {
  'Query/ProductList/Query': {
    'member-function': {
			_getCartProductInterfaceFields: (args, callback) => {
				return [
					...callback(...args),
					...getCustomFields()
				];
			}	
		}
  }
}

Do not forget to refactor query fields into the appropriate files and to refactor plugin to a separate function for descriptiveness, readability, maintenance and consistency:

class ProductCustomResultsQuery {
	getCustomFields() {
			return [
						'customRating',
						'customReviewCounts',
						'customReactions'
			];
	}
}

export default new ProductCustomResultsQuery();

And finally, refactoring the plugin to a separate function:

import ProductCustomResultsQuery from '../query/ProductCustomResults.query';

const addCustomFieldsToProductInterfaceFields = (args, callback) => [
		...callback(...args)
		.addField(ProductCustomResultsQuery.getCustomFields());
];

export default {
  'Query/ProductList/Query': {
    'member-function': {
			_getCartProductInterfaceFields: addCustomFieldsToProductInterfaceFields
		}
  }
}

That’s it!

What are the real examples of this?

In this example, an additional field turnToRating is added to product interface fields.