Custom Queries in Unified Data Layer
When working with integrations that use GraphQL APIs, you might encounter scenarios where you need to fetch additional or custom fields that are not included in the default queries. To locate the methods and queries being used, refer to the documentation for your specific integration. For example, see the Commercetools API documentation on GraphQL queries. This guide provides step-by-step instructions on how to configure custom queries within the Unified Data Layer (UDL) of your integration.
Introduction
In some cases, the methods provided by your integration may need to be adjusted to accommodate specific requirements, such as fetching additional fields or altering input parameters. This is a common scenario when dealing with customized data needs. This guide will help you add a custom query to your integration and use it within your integration.
Custom Queries vs. Normalizers
If the default query already contains the data you are looking for, consider using normalizers to transform the data as needed. Custom queries should be used primarily for fetching additional fields that are not included in the default queries.
Configuring Custom Queries
Custom queries are defined in the integrations/<integration>/config.ts
file of your integration. Each custom query function provides the flexibility to modify the default query and variables. Currently, this functionality is available for the Commercetools and Magento 2 integrations.
Understanding Custom Query Functions
Custom query functions receive the following arguments:
query
: the default GraphQL queryvariables
: the default variables passed to the querymetadata
: additional parameters that provide context or extra data for the custom query. The metadata variable is of type unknown and requires either a type definition or type casting to function correctly in TypeScript
The function must return an object with query
and variables
keys. Within the function body, you can modify or replace these parameters as needed.
Example structure:
// integrations/<integration>/config.ts
export const config = {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
/* ... existing configuration ... */
},
customQueries: {
'custom-query-name': ({ query, variables, metadata }) => {
// Modify query and variables as needed
return { query, variables };
}
}
}
}
Keeping the Configuration Tidy
To maintain a clean and manageable configuration, it's recommended to extract custom queries into separate files or folders. This prevents the config.ts
file from becoming cluttered.
Example
Create a separate file for custom queries:
// <integration>/customQueries/myCustomQueries.ts
export const myCustomQueries = {
'my-custom-products-query': ({ variables, metadata }) => ({
// Modify variables or use metadata as needed
const typedMetadata = metadata as { size?: number };
variables.size = typedMetadata.size || 10;
query: `
query products($where: String, $locale: String, $size: Int) {
products(where: $where, locale: $locale, size: $size) {
results {
id
name
// Fields...
}
}
}
`,
variables,
}),
// ...other custom queries
};
Import it in config.ts
:
// <integration>/config.ts
import { myCustomQueries } from './customQueries/myCustomQueries';
export const config = {
integrations: {
ct: {
location: '@vue-storefront/commercetools-api/server',
configuration: {
/* ... existing configuration ... */
},
customQueries: {
...myCustomQueries,
// ...other custom queries
},
},
},
};
Creating new custom queries
Step 1: Create a Custom Query
Define a new custom query with a unique name and adjust the parameters and fields as required. Using the example structure from the Keeping the Configuration Tidy section.
Step 2: Use the custom query in a method
Create a new method in your extension that uses the custom query. Extending the Middleware
// apps/storefront-middleware/api/yourExtension/customGetProducts.ts
export async function customGetProducts(context, args) {
const customQuery = {
products: 'my-custom-products-query',
variables: {
locale: args.locale || 'en',
// Include other variables as needed
},
metadata: {
size: 10, // Hardcoded 'size' in metadata
},
};
/*
* `context.api.getProducts` is a method that fetches product data from the API.
* It takes two arguments:
* 1. An object containing search parameters (e.g., `where`, `skus`).
* 2. A custom query object that specifies the GraphQL query to be used.
*/
const response = await context.api.getProducts(
buildProductSearchParams(args),
customQuery
);
const products = response.products.results;
if (!products || products.length === 0) {
return [];
}
return products;
}
function buildProductSearchParams(args) {
return {
where: args.where,
// Include other parameters as needed
};
}
Note:
- If the query specifies additional fields not contained in the typedef, you need to extend the existing type definitions to include these new fields
- You don't need to override the existing UDL method; you can create a new method that uses your custom query
Warnings and Best Practices
- Testing: thoroughly test your custom queries and methods to confirm they work as intended and do not introduce regressions
- Documentation: keep your custom queries and extensions well-documented to facilitate maintenance and updates
- Avoid Overcomplicating: Ooerriding too many default behaviors can make your integration harder to maintain