# 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 theproducts
object with the result. Every time you invoke this function API request is made. This method accepts a singleparams
object. Theparams
has the following options:searchParams: ProductsSearchParams
customQuery?: CustomQuery
- If presentproducts
will be a raw response from GraphQL QueryenhanceProduct?: EnhanceProduct
interface 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
customFilters
can 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 bysearch
method.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 yoursearch
method.error: UseProductErrors
- reactive object containing the error message, ifsearch
failed 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,
};
}
}