Customize GraphQL Queries in Commerce React Components
Introduction
As of v14.7.1, the Commerce React Components allow applications to customize the internal GraphQL queries and variables. When using a custom hook provided by one of the Commerce React Components, application components may provide an optional CustomQueryOptionsProcessor implementation in the input properties. If provided, the Commerce React Component will invoke the CustomQueryOptionsProcessor implementation in order to pre-process the internal GraphQL query and variables before making any GraphQL queries. If the CustomQueryOptionsProcessor implementation updates the GraphQL query document or variables, then the updated GraphQL query or variables will be used in the Commerce React Component instead of the default ones.
CustomQueryOptionsProcessor Interface
Since v14.7.1, most of the Commerce React Components take an optional CustomQueryOptionsProcessor implementation object in their input properties as shown below:
/** * Interface for the input and the output of the [[`CustomQueryOptionsProcessor`]] * which can be supplied by the application code in order to customize the original * GraphQL query document and query options object including variables inside this interface. * @category CustomQueryOptionsProcessor */ export interface CustomQueryOptions<TData = any, TVariables = OperationVariables> { query: DocumentNode | TypedDocumentNode<TData, TVariables | OperationVariables>; options?: QueryHookOptions<TData, TVariables | OperationVariables>; } /** * The interface to process the original GraphQL query document and query options object * while using the [[`useQueryCustom`]] hook function. * @category CustomQueryOptionsProcessor */ export interface CustomQueryOptionsProcessor<TData, TVariables = OperationVariables> { /** * Process the original GraphQL query document and query options object to affect the default internal * query execution * @param customQueryOptions the original GraphQL query document and query options * @returns a processed GraphQL query document and query options if updated; or null if no update necessary */ process(customQueryOptions: CustomQueryOptions<TData, TVariables>): CustomQueryOptions; }
For example, the useProductGridSearch hook in the Commerce React Components accepts the input properties like the following, and the input properties now contains an optional customQueryOptionsProcessor property of type CustomQueryOptionsProcessor<TData, TVariables>, where TData generic type is specified as Items and TVariables generic type as ItemsVariables for the product search feature.
/** * @category Product */ export interface ProductGridSearchInputProps extends CommonProductInputProps { /** * Initial keyword to search products by. */ searchText?: string; // ... /** * Optional Custom Query and Options Processor to update the original query document and variables if necessary */ customQueryOptionsProcessor?: CustomQueryOptionsProcessor<Items, ItemsVariables>; } // ... export function useProductGridSearch({ searchText, // ... customQueryOptionsProcessor, }: ProductGridSearchInputProps): [ (offset?: number) => Promise<void>, ItemsResponse | undefined, boolean, ApolloError | undefined, ] { // ... }
As shown in the example above, most of Commerce React Components support the optional customQueryOptionsProcessor input property which can be supplied by the application. If provided, the Commerce React Component will do the following:
- The Commerce React Components create an CustomQueryOptions object containing the internal GraphQL query document object (query property) and the internal GraphQL query options containing variables (options property).
- Now the customQueryOptionsProcessor has a chance to look up the CustomQueryOptions input object containing the internal, original GraphQL query document object and the internal GraphQL query options containing variables.
- If the application doesn't need to customize anything, then it may just return the CustomQueryOptions input object without any updates.
- Or if the application wants to update the GraphQL query document or query options/variables, then it may return an updated CustomQueryOptions object.
- The Commerce React Components receive the CustomQueryOptions object returned by the customQueryOptionsProcessor and uses the GraphQL queries and options/variables returned by the customQueryOptionsProcessor afterward.
Therefore, an application may customize the internal GraphQL query documents or the internal GraphQL query variables if necessary by implementing a CustomQueryOptionsProcessor and providing it as the optional input property to the hook of the Commerce React Component.
Example
Suppose you want to customize the GraphQL query when using the useCategory hook which is used when retrieving a commerce category.
For example, the following code will initialize a hook to query a commerce category by a category ID:
const [category, loading, catError] = useCategory({ categoryId });
Now, suppose you want to optimize the GraphQL query in your application with fewer fields and add a new variable to the query variables. You can do that like the following example:
//... import { gql } from '@apollo/client'; //... // A custom GraphQL query document example to be used instead below... const myNewQuery = gql` query Category($id: String!, $queryHint: QueryHintInput) { findCategoryById(id: $id, queryHint: $queryHint) { id displayName slug } } `; // A CustomQueryOptionsProcessor implementation to replace the GraphQL query and append a new variable... const customQueryOptionsProcessor = { process: (customQueryOptions: CustomQueryOptions<Category | CategoryBySlug, CategoryVariables | CategoryBySlugVariables>) => { // You can look up the original, internal GraphQL query document and query options containing variables like the following... const originalQuery = customQueryOptions.query; const originalVars = customQueryOptions.options?.variables ?? {} as CategoryVariables | CategoryBySlugVariables; // You can append your new variable like this... const newVars = {...originalVars, newVar: 'newValue' }; // Finally return a new CustomQueryOptions object with the new custom query object and updated variables... return { query: newQuery, options: { ...customQueryOptions.options, ...{ variables: newVars } } }; }, };