# useProduct
# Features
useProduct composable is responsible for fetching a list of products.
# API
search- the main querying function used to query products from the eCommerce platform and populate theproductsobject with the result. Every time you invoke this function API request is made. This method accepts a singleparamsobject. Theparamshas the following options:searchParams: ProductsSearchParamscustomQuery?: CustomQuery- If presentproductswill be a raw response from GraphQL QueryenhanceProduct?: EnhanceProductinterface ProductsSearchParams { perPage?: number; page?: number; sort?: any; term?: any; filters?: Record<string, Filter>; catId?: string | string[]; skus?: string[]; slug?: string; id?: string; ids?: string[]; customFilters?: string; } type CustomQuery = { products: string } type EnhanceProduct = ( productResponse: ApolloQueryResult<ProductData>, context: Context ) => ApolloQueryResult<ProductData>Option
customFilterscan contain a custom string with all the filters you want. Please refer to commercetools documentation (opens new window) to learn more about query predicates.
products: ProductVariant[] | QueryResponse<'products', ProductQueryResult>- the main data object that contains an array of products fetched bysearchmethod.type ProductVariant = { __typename?: "ProductVariant"; id: Scalars["Int"]; key?: Maybe<Scalars["String"]>; sku?: Maybe<Scalars["String"]>; prices?: Maybe<Array<ProductPrice>>; price?: Maybe<ProductPrice>; images: Array<Image>; assets: Array<Asset>; availability?: Maybe<ProductVariantAvailabilityWithChannels>; attributesRaw: Array<RawProductAttribute>; attributes: ProductType; attributeList: Array<Attribute>; }type ProductQueryResult = { __typename?: 'ProductQueryResult'; offset: Scalars['Int']; count: Scalars['Int']; total: Scalars['Long']; exists: Scalars['Boolean']; results: Array<Product>; };loading: boolean- a reactive object containing information about the loading state of yoursearchmethod.error: UseProductErrors- reactive object containing the error message, ifsearchfailed for any reason.interface UseProductErrors { search: Error; }
# Getters
getName- returns product name.getSlug- returns product slug.getPrice- returns product price.getGallery- returns product gallery.getCoverImage- returns cover image of product.getFiltered- returns filtered product.getAttributes- returns product attributes.getDescription- returns product description.getCategoryIds- returns all product categories IDs.getCategorySlugs- returns all product categories slugs.getId- returns product ID.getSku- returns product sku.getFormattedPrice- returns product price with currency sign.getTotalReviews- returns total number of reviews product has.getAverageRating- returns average rating from all reviews.getBreadcrumbs- returns breadcrumbs.interface ProductGetters { getName: (product: ProductVariant | Readonly<ProductVariant>) => string; getSlug: (product: ProductVariant | Readonly<ProductVariant>) => string; getPrice: (product: ProductVariant | Readonly<ProductVariant>) => AgnosticPrice; getGallery: (product: ProductVariant) => AgnosticMediaGalleryItem[]; getCoverImage: (product: ProductVariant) => string; getFiltered: (products: ProductVariant[], filters: ProductVariantFilters | any = {}) => ProductVariant[]; getAttributes: (products: ProductVariant[] | ProductVariant, filterByAttributeName?: string[]) => Record<string, AgnosticAttribute | string>; getDescription: (product: ProductVariant) => string; getCategoryIds: (product: ProductVariant) => string[]; getCategorySlugs: (product: ProductVariant) => string[]; getId: (product: ProductVariant) => string; getSku: (product: ProductVariant) => any; getFormattedPrice: (price: number) => string; getTotalReviews: (product: ProductVariant) => number; getAverageRating: (product: ProductVariant) => number; getBreadcrumbs: (product: Record<string, any>) => AgnosticBreadcrumb[]; } interface AgnosticPrice { regular: number | null; special?: number | null; } interface AgnosticMediaGalleryItem { small: string; normal: string; big: string; } interface AgnosticAttribute { name?: string; value: string | Record<string, any>; label: string; } type ProductVariant = { __typename?: "ProductVariant"; id: Scalars["Int"]; key?: Maybe<Scalars["String"]>; sku?: Maybe<Scalars["String"]>; prices?: Maybe<Array<ProductPrice>>; price?: Maybe<ProductPrice>; images: Array<Image>; assets: Array<Asset>; availability?: Maybe<ProductVariantAvailabilityWithChannels>; attributesRaw: Array<RawProductAttribute>; attributes: ProductType; attributeList: Array<Attribute>; } interface ProductVariantFilters { master?: boolean; attributes?: Record<string, string>; }
# Examples
// search single product
import { computed } from '@nuxtjs/composition-api';
import { useProduct, productGetters } from '@vsf-enterprise/commercetools';
import { onSSR } from '@vue-storefront/core';
export default {
setup () {
const { products, search, loading, error } = useProduct('<UNIQUE_ID>');
onSSR(async () => {
await search({ slug: 'super-t-shirt' })
})
return {
loading,
error,
product: computed(() => productGetters.getFiltered(products.value, { master: true, attributes: context.root.$route.query })[0]),
option: computed(() => productGetters.getAttributes(products.value, ['color', 'size'])),
configuration: computed(() => productGetters.getCategoryIds(product.value))
}
}
}
// search products by ids
import { computed } from '@nuxtjs/composition-api';
import { useProduct, productGetters } from '@vsf-enterprise/commercetools';
import { onSSR } from '@vue-storefront/core';
export default {
setup () {
const { products, search } = useProduct('<UNIQUE_ID>');
onSSR(async () => {
await search({ ids: ['id-1', 'id-2'] });
});
return {
products,
masterProducts: computed(() => productGetters.getFiltered(products.value, { master: true }))
};
}
}
# Using enhanceProduct
Under the hood the search method of the useProduct composable uses the enhanceProduct function to model the returned data. The function looks as follows:
import type { ApolloQueryResult } from '@apollo/client/core';
import type { ProductQueryResult } from '@vsf-enterprise/commercetools-types';
import type { Context } from '@vue-storefront/core';
interface ProductData {
products: ProductQueryResult;
}
const getTranslated = (rawAttribute, context) => {
const { locale } = context.$ct.config;
if (rawAttribute.attributeDefinition.type.name === 'ltext') {
return rawAttribute.value[locale];
}
if (rawAttribute.attributeDefinition.type.name === 'lenum') {
return rawAttribute.value.label[locale];
}
return rawAttribute.value;
};
export type EnhanceProduct = (productResponse: ApolloQueryResult<ProductData>, context: Context) => ApolloQueryResult<ProductData>;
export const enhanceProduct: EnhanceProduct = (productResponse, context) => {
(productResponse.data as any)._variants = productResponse.data.products.results
.map((product) => {
const current = product.masterData.current;
return current.allVariants.map((variant) => ({
...variant,
attributesRaw: variant.attributesRaw.map((raw) => ({
...raw,
_translated: getTranslated(raw, context)
})),
_name: current.name,
_slug: current.slug,
_id: product.id,
_key: product.key,
_master: current.masterVariant.id === variant.id,
_description: current.description,
_categoriesRef: current.categoriesRef.map((cr) => cr.id),
_categories: current.categories.map(category => ({ id: category.id, name: category.name, slug: category.slug })),
_rating: (product as any).reviewRatingStatistics,
_original: product
}));
})
.reduce((prev, curr) => [...prev, ...curr], []);
return productResponse;
};
You can replace the original enhanceProduct function when calling the search method from the useProduct composable by passing your own enhanceProduct function as an argument.
This can be very useful if you're working with many variants and want to slim down the returned object or want to best tailor the returned data to your specific use case.
import { useProduct } from '@vue-storefront/commercetools';
import { onSSR } from '@vue-storefront/core';
import { myEnhanceProduct } from './myEnhanceProduct.ts';
export default {
setup () {
const { products, search } = useProduct('<UNIQUE_ID>');
onSSR(async () => {
await search({ id: 'product-id', enhanceProduct: myEnhanceProduct });
});
return {
products,
};
}
}