mirror of
https://github.com/vercel/commerce.git
synced 2025-06-19 13:41:22 +00:00
Moved getAllProducts
This commit is contained in:
parent
c2a5904e01
commit
be5d47bac3
@ -1,6 +1,5 @@
|
|||||||
import { Product } from '@commerce/types/product'
|
import { Product } from '@commerce/types/product'
|
||||||
import { ProductsEndpoint } from '.'
|
import { ProductsEndpoint } from '.'
|
||||||
import getAllProducts from '../../../../product/get-all-products'
|
|
||||||
|
|
||||||
const SORT: { [key: string]: string | undefined } = {
|
const SORT: { [key: string]: string | undefined } = {
|
||||||
latest: 'id',
|
latest: 'id',
|
||||||
@ -15,6 +14,7 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
|||||||
res,
|
res,
|
||||||
body: { search, category, brand, sort },
|
body: { search, category, brand, sort },
|
||||||
config,
|
config,
|
||||||
|
commerce,
|
||||||
}) => {
|
}) => {
|
||||||
// Use a dummy base as we only care about the relative path
|
// Use a dummy base as we only care about the relative path
|
||||||
const url = new URL('/v3/catalog/products', 'http://a')
|
const url = new URL('/v3/catalog/products', 'http://a')
|
||||||
@ -47,18 +47,18 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
|||||||
url.pathname + url.search
|
url.pathname + url.search
|
||||||
)
|
)
|
||||||
|
|
||||||
const entityIds = data.map((p) => p.id)
|
const ids = data.map((p) => String(p.id))
|
||||||
const found = entityIds.length > 0
|
const found = ids.length > 0
|
||||||
|
|
||||||
// We want the GraphQL version of each product
|
// We want the GraphQL version of each product
|
||||||
const graphqlData = await getAllProducts({
|
const graphqlData = await commerce.getAllProducts({
|
||||||
variables: { first: LIMIT, entityIds },
|
variables: { first: LIMIT, ids },
|
||||||
config,
|
config,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Put the products in an object that we can use to get them by id
|
// Put the products in an object that we can use to get them by id
|
||||||
const productsById = graphqlData.products.reduce<{
|
const productsById = graphqlData.products.reduce<{
|
||||||
[k: number]: Product
|
[k: string]: Product
|
||||||
}>((prods, p) => {
|
}>((prods, p) => {
|
||||||
prods[Number(p.id)] = p
|
prods[Number(p.id)] = p
|
||||||
return prods
|
return prods
|
||||||
@ -68,7 +68,7 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
|||||||
|
|
||||||
// Populate the products array with the graphql products, in the order
|
// Populate the products array with the graphql products, in the order
|
||||||
// assigned by the list of entity ids
|
// assigned by the list of entity ids
|
||||||
entityIds.forEach((id) => {
|
ids.forEach((id) => {
|
||||||
const product = productsById[id]
|
const product = productsById[id]
|
||||||
if (product) products.push(product)
|
if (product) products.push(product)
|
||||||
})
|
})
|
||||||
|
@ -22,6 +22,7 @@ import getPage from './operations/get-page'
|
|||||||
import getSiteInfo from './operations/get-site-info'
|
import getSiteInfo from './operations/get-site-info'
|
||||||
import getCustomerWishlist from './operations/get-customer-wishlist'
|
import getCustomerWishlist from './operations/get-customer-wishlist'
|
||||||
import getAllProductPaths from './operations/get-all-product-paths'
|
import getAllProductPaths from './operations/get-all-product-paths'
|
||||||
|
import getAllProducts from './operations/get-all-products'
|
||||||
|
|
||||||
export interface BigcommerceConfig extends CommerceAPIConfig {
|
export interface BigcommerceConfig extends CommerceAPIConfig {
|
||||||
// Indicates if the returned metadata with translations should be applied to the
|
// Indicates if the returned metadata with translations should be applied to the
|
||||||
@ -124,6 +125,7 @@ export const provider = {
|
|||||||
getSiteInfo,
|
getSiteInfo,
|
||||||
getCustomerWishlist,
|
getCustomerWishlist,
|
||||||
getAllProductPaths,
|
getAllProductPaths,
|
||||||
|
getAllProducts,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
160
framework/bigcommerce/api/operations/get-all-products.ts
Normal file
160
framework/bigcommerce/api/operations/get-all-products.ts
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import type {
|
||||||
|
OperationContext,
|
||||||
|
OperationOptions,
|
||||||
|
} from '@commerce/api/operations'
|
||||||
|
import type {
|
||||||
|
GetAllProductsQuery,
|
||||||
|
GetAllProductsQueryVariables,
|
||||||
|
} from '../../schema'
|
||||||
|
import type { GetAllProductsOperation } from '../../types/product'
|
||||||
|
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||||
|
import filterEdges from '../utils/filter-edges'
|
||||||
|
import setProductLocaleMeta from '../utils/set-product-locale-meta'
|
||||||
|
import { productConnectionFragment } from '../fragments/product'
|
||||||
|
import { BigcommerceConfig, Provider } from '..'
|
||||||
|
import { normalizeProduct } from '../../lib/normalize'
|
||||||
|
|
||||||
|
export const getAllProductsQuery = /* GraphQL */ `
|
||||||
|
query getAllProducts(
|
||||||
|
$hasLocale: Boolean = false
|
||||||
|
$locale: String = "null"
|
||||||
|
$entityIds: [Int!]
|
||||||
|
$first: Int = 10
|
||||||
|
$products: Boolean = false
|
||||||
|
$featuredProducts: Boolean = false
|
||||||
|
$bestSellingProducts: Boolean = false
|
||||||
|
$newestProducts: Boolean = false
|
||||||
|
) {
|
||||||
|
site {
|
||||||
|
products(first: $first, entityIds: $entityIds) @include(if: $products) {
|
||||||
|
...productConnnection
|
||||||
|
}
|
||||||
|
featuredProducts(first: $first) @include(if: $featuredProducts) {
|
||||||
|
...productConnnection
|
||||||
|
}
|
||||||
|
bestSellingProducts(first: $first) @include(if: $bestSellingProducts) {
|
||||||
|
...productConnnection
|
||||||
|
}
|
||||||
|
newestProducts(first: $first) @include(if: $newestProducts) {
|
||||||
|
...productConnnection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${productConnectionFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export type ProductEdge = NonNullable<
|
||||||
|
NonNullable<GetAllProductsQuery['site']['products']['edges']>[0]
|
||||||
|
>
|
||||||
|
|
||||||
|
export type ProductNode = ProductEdge['node']
|
||||||
|
|
||||||
|
export type GetAllProductsResult<
|
||||||
|
T extends Record<keyof GetAllProductsResult, any[]> = {
|
||||||
|
products: ProductEdge[]
|
||||||
|
}
|
||||||
|
> = T
|
||||||
|
|
||||||
|
const FIELDS = [
|
||||||
|
'products',
|
||||||
|
'featuredProducts',
|
||||||
|
'bestSellingProducts',
|
||||||
|
'newestProducts',
|
||||||
|
]
|
||||||
|
|
||||||
|
export type ProductTypes =
|
||||||
|
| 'products'
|
||||||
|
| 'featuredProducts'
|
||||||
|
| 'bestSellingProducts'
|
||||||
|
| 'newestProducts'
|
||||||
|
|
||||||
|
export type ProductVariables = { field?: ProductTypes } & Omit<
|
||||||
|
GetAllProductsQueryVariables,
|
||||||
|
ProductTypes | 'hasLocale'
|
||||||
|
>
|
||||||
|
|
||||||
|
function getProductsType(
|
||||||
|
relevance?: GetAllProductsOperation['variables']['relevance']
|
||||||
|
): ProductTypes {
|
||||||
|
switch (relevance) {
|
||||||
|
case 'featured':
|
||||||
|
return 'featuredProducts'
|
||||||
|
case 'best_selling':
|
||||||
|
return 'bestSellingProducts'
|
||||||
|
case 'newest':
|
||||||
|
return 'newestProducts'
|
||||||
|
default:
|
||||||
|
return 'products'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getAllProductsOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<Provider>) {
|
||||||
|
async function getAllProducts<T extends GetAllProductsOperation>(opts?: {
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: BigcommerceConfig
|
||||||
|
preview?: boolean
|
||||||
|
}): Promise<T['data']>
|
||||||
|
|
||||||
|
async function getAllProducts<T extends GetAllProductsOperation>(
|
||||||
|
opts: {
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: BigcommerceConfig
|
||||||
|
preview?: boolean
|
||||||
|
} & OperationOptions
|
||||||
|
): Promise<T['data']>
|
||||||
|
|
||||||
|
async function getAllProducts<T extends GetAllProductsOperation>({
|
||||||
|
query = getAllProductsQuery,
|
||||||
|
variables: vars = {},
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: BigcommerceConfig
|
||||||
|
preview?: boolean
|
||||||
|
} = {}): Promise<T['data']> {
|
||||||
|
config = commerce.getConfig(config)
|
||||||
|
|
||||||
|
const locale = config.locale
|
||||||
|
const field = getProductsType(vars.relevance)
|
||||||
|
const variables: GetAllProductsQueryVariables = {
|
||||||
|
locale,
|
||||||
|
hasLocale: !!locale,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FIELDS.includes(field)) {
|
||||||
|
throw new Error(
|
||||||
|
`The field variable has to match one of ${FIELDS.join(', ')}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
variables[field] = true
|
||||||
|
|
||||||
|
if (vars.first) variables.first = vars.first
|
||||||
|
if (vars.ids) variables.entityIds = vars.ids.map((id) => Number(id))
|
||||||
|
|
||||||
|
// RecursivePartial forces the method to check for every prop in the data, which is
|
||||||
|
// required in case there's a custom `query`
|
||||||
|
const { data } = await config.fetch<RecursivePartial<GetAllProductsQuery>>(
|
||||||
|
query,
|
||||||
|
{ variables }
|
||||||
|
)
|
||||||
|
const edges = data.site?.[field]?.edges
|
||||||
|
const products = filterEdges(edges as RecursiveRequired<typeof edges>)
|
||||||
|
|
||||||
|
if (locale && config.applyLocale) {
|
||||||
|
products.forEach((product: RecursivePartial<ProductEdge>) => {
|
||||||
|
if (product.node) setProductLocaleMeta(product.node)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
products: products.map(({ node }) => normalizeProduct(node as any)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllProducts
|
||||||
|
}
|
@ -8,7 +8,7 @@ import type {
|
|||||||
} from '../../types/wishlist'
|
} from '../../types/wishlist'
|
||||||
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||||
import { BigcommerceConfig, Provider } from '..'
|
import { BigcommerceConfig, Provider } from '..'
|
||||||
import getAllProducts, { ProductEdge } from '../../product/get-all-products'
|
import getAllProducts, { ProductEdge } from './get-all-products'
|
||||||
|
|
||||||
export default function getCustomerWishlistOperation({
|
export default function getCustomerWishlistOperation({
|
||||||
commerce,
|
commerce,
|
||||||
@ -47,13 +47,13 @@ export default function getCustomerWishlistOperation({
|
|||||||
const wishlist = data[0]
|
const wishlist = data[0]
|
||||||
|
|
||||||
if (includeProducts && wishlist?.items?.length) {
|
if (includeProducts && wishlist?.items?.length) {
|
||||||
const entityIds = wishlist.items
|
const ids = wishlist.items
|
||||||
?.map((item) => item?.product_id)
|
?.map((item) => (item?.product_id ? String(item?.product_id) : null))
|
||||||
.filter((id): id is number => !!id)
|
.filter((id): id is string => !!id)
|
||||||
|
|
||||||
if (entityIds?.length) {
|
if (ids?.length) {
|
||||||
const graphqlData = await getAllProducts({
|
const graphqlData = await commerce.getAllProducts({
|
||||||
variables: { first: 100, entityIds },
|
variables: { first: 100, ids },
|
||||||
config,
|
config,
|
||||||
})
|
})
|
||||||
// Put the products in an object that we can use to get them by id
|
// Put the products in an object that we can use to get them by id
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { ProductNode } from '../../product/get-all-products'
|
import type { ProductNode } from '../operations/get-all-products'
|
||||||
import type { RecursivePartial } from './types'
|
import type { RecursivePartial } from './types'
|
||||||
|
|
||||||
export default function setProductLocaleMeta(
|
export default function setProductLocaleMeta(
|
||||||
|
@ -1,135 +0,0 @@
|
|||||||
import type {
|
|
||||||
GetAllProductsQuery,
|
|
||||||
GetAllProductsQueryVariables,
|
|
||||||
} from '../schema'
|
|
||||||
import type { Product } from '@commerce/types'
|
|
||||||
import type { RecursivePartial, RecursiveRequired } from '../api/utils/types'
|
|
||||||
import filterEdges from '../api/utils/filter-edges'
|
|
||||||
import setProductLocaleMeta from '../api/utils/set-product-locale-meta'
|
|
||||||
import { productConnectionFragment } from '../api/fragments/product'
|
|
||||||
import { BigcommerceConfig, getConfig } from '../api'
|
|
||||||
import { normalizeProduct } from '../lib/normalize'
|
|
||||||
|
|
||||||
export const getAllProductsQuery = /* GraphQL */ `
|
|
||||||
query getAllProducts(
|
|
||||||
$hasLocale: Boolean = false
|
|
||||||
$locale: String = "null"
|
|
||||||
$entityIds: [Int!]
|
|
||||||
$first: Int = 10
|
|
||||||
$products: Boolean = false
|
|
||||||
$featuredProducts: Boolean = false
|
|
||||||
$bestSellingProducts: Boolean = false
|
|
||||||
$newestProducts: Boolean = false
|
|
||||||
) {
|
|
||||||
site {
|
|
||||||
products(first: $first, entityIds: $entityIds) @include(if: $products) {
|
|
||||||
...productConnnection
|
|
||||||
}
|
|
||||||
featuredProducts(first: $first) @include(if: $featuredProducts) {
|
|
||||||
...productConnnection
|
|
||||||
}
|
|
||||||
bestSellingProducts(first: $first) @include(if: $bestSellingProducts) {
|
|
||||||
...productConnnection
|
|
||||||
}
|
|
||||||
newestProducts(first: $first) @include(if: $newestProducts) {
|
|
||||||
...productConnnection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
${productConnectionFragment}
|
|
||||||
`
|
|
||||||
|
|
||||||
export type ProductEdge = NonNullable<
|
|
||||||
NonNullable<GetAllProductsQuery['site']['products']['edges']>[0]
|
|
||||||
>
|
|
||||||
|
|
||||||
export type ProductNode = ProductEdge['node']
|
|
||||||
|
|
||||||
export type GetAllProductsResult<
|
|
||||||
T extends Record<keyof GetAllProductsResult, any[]> = {
|
|
||||||
products: ProductEdge[]
|
|
||||||
}
|
|
||||||
> = T
|
|
||||||
|
|
||||||
const FIELDS = [
|
|
||||||
'products',
|
|
||||||
'featuredProducts',
|
|
||||||
'bestSellingProducts',
|
|
||||||
'newestProducts',
|
|
||||||
]
|
|
||||||
|
|
||||||
export type ProductTypes =
|
|
||||||
| 'products'
|
|
||||||
| 'featuredProducts'
|
|
||||||
| 'bestSellingProducts'
|
|
||||||
| 'newestProducts'
|
|
||||||
|
|
||||||
export type ProductVariables = { field?: ProductTypes } & Omit<
|
|
||||||
GetAllProductsQueryVariables,
|
|
||||||
ProductTypes | 'hasLocale'
|
|
||||||
>
|
|
||||||
|
|
||||||
async function getAllProducts(opts?: {
|
|
||||||
variables?: ProductVariables
|
|
||||||
config?: BigcommerceConfig
|
|
||||||
preview?: boolean
|
|
||||||
}): Promise<{ products: Product[] }>
|
|
||||||
|
|
||||||
async function getAllProducts<
|
|
||||||
T extends Record<keyof GetAllProductsResult, any[]>,
|
|
||||||
V = any
|
|
||||||
>(opts: {
|
|
||||||
query: string
|
|
||||||
variables?: V
|
|
||||||
config?: BigcommerceConfig
|
|
||||||
preview?: boolean
|
|
||||||
}): Promise<GetAllProductsResult<T>>
|
|
||||||
|
|
||||||
async function getAllProducts({
|
|
||||||
query = getAllProductsQuery,
|
|
||||||
variables: { field = 'products', ...vars } = {},
|
|
||||||
config,
|
|
||||||
}: {
|
|
||||||
query?: string
|
|
||||||
variables?: ProductVariables
|
|
||||||
config?: BigcommerceConfig
|
|
||||||
preview?: boolean
|
|
||||||
// TODO: fix the product type here
|
|
||||||
} = {}): Promise<{ products: Product[] | any[] }> {
|
|
||||||
config = getConfig(config)
|
|
||||||
|
|
||||||
const locale = vars.locale || config.locale
|
|
||||||
const variables: GetAllProductsQueryVariables = {
|
|
||||||
...vars,
|
|
||||||
locale,
|
|
||||||
hasLocale: !!locale,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FIELDS.includes(field)) {
|
|
||||||
throw new Error(
|
|
||||||
`The field variable has to match one of ${FIELDS.join(', ')}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
variables[field] = true
|
|
||||||
|
|
||||||
// RecursivePartial forces the method to check for every prop in the data, which is
|
|
||||||
// required in case there's a custom `query`
|
|
||||||
const { data } = await config.fetch<RecursivePartial<GetAllProductsQuery>>(
|
|
||||||
query,
|
|
||||||
{ variables }
|
|
||||||
)
|
|
||||||
const edges = data.site?.[field]?.edges
|
|
||||||
const products = filterEdges(edges as RecursiveRequired<typeof edges>)
|
|
||||||
|
|
||||||
if (locale && config.applyLocale) {
|
|
||||||
products.forEach((product: RecursivePartial<ProductEdge>) => {
|
|
||||||
if (product.node) setProductLocaleMeta(product.node)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return { products: products.map(({ node }) => normalizeProduct(node as any)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getAllProducts
|
|
@ -1,4 +1,4 @@
|
|||||||
export { default as usePrice } from './use-price'
|
export { default as usePrice } from './use-price'
|
||||||
export { default as useSearch } from './use-search'
|
export { default as useSearch } from './use-search'
|
||||||
export { default as getProduct } from './get-product'
|
export { default as getProduct } from './get-product'
|
||||||
export { default as getAllProducts } from './get-all-products'
|
export { default as getAllProducts } from '../api/operations/get-all-products'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as Core from '@commerce/types/wishlist'
|
import * as Core from '@commerce/types/wishlist'
|
||||||
import { definitions } from '../api/definitions/wishlist'
|
import { definitions } from '../api/definitions/wishlist'
|
||||||
import type { ProductEdge } from '../product/get-all-products'
|
import type { ProductEdge } from '../api/operations/get-all-products'
|
||||||
|
|
||||||
export * from '@commerce/types/wishlist'
|
export * from '@commerce/types/wishlist'
|
||||||
|
|
||||||
|
@ -3,7 +3,10 @@ import type { LoginOperation } from '../types/login'
|
|||||||
import type { GetAllPagesOperation, GetPageOperation } from '../types/page'
|
import type { GetAllPagesOperation, GetPageOperation } from '../types/page'
|
||||||
import type { GetSiteInfoOperation } from '../types/site'
|
import type { GetSiteInfoOperation } from '../types/site'
|
||||||
import type { GetCustomerWishlistOperation } from '../types/wishlist'
|
import type { GetCustomerWishlistOperation } from '../types/wishlist'
|
||||||
import type { GetAllProductPathsOperation } from '../types/product'
|
import type {
|
||||||
|
GetAllProductPathsOperation,
|
||||||
|
GetAllProductsOperation,
|
||||||
|
} from '../types/product'
|
||||||
import type { APIProvider, CommerceAPI } from '.'
|
import type { APIProvider, CommerceAPI } from '.'
|
||||||
|
|
||||||
const noop = () => {
|
const noop = () => {
|
||||||
@ -109,6 +112,22 @@ export type Operations<P extends APIProvider> = {
|
|||||||
} & OperationOptions
|
} & OperationOptions
|
||||||
): Promise<T['data']>
|
): Promise<T['data']>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllProducts: {
|
||||||
|
<T extends GetAllProductsOperation>(opts: {
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: P['config']
|
||||||
|
preview?: boolean
|
||||||
|
}): Promise<T['data']>
|
||||||
|
|
||||||
|
<T extends GetAllProductsOperation>(
|
||||||
|
opts: {
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: P['config']
|
||||||
|
preview?: boolean
|
||||||
|
} & OperationOptions
|
||||||
|
): Promise<T['data']>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type APIOperations<P extends APIProvider> = {
|
export type APIOperations<P extends APIProvider> = {
|
||||||
|
@ -72,3 +72,12 @@ export type GetAllProductPathsOperation<
|
|||||||
data: { products: Pick<T['product'], 'path'>[] }
|
data: { products: Pick<T['product'], 'path'>[] }
|
||||||
variables: { first?: number }
|
variables: { first?: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
|
||||||
|
data: { products: T['product'][] }
|
||||||
|
variables: {
|
||||||
|
relevance?: 'featured' | 'best_selling' | 'newest'
|
||||||
|
ids?: string[]
|
||||||
|
first?: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { ProductCard } from '@components/product'
|
|||||||
import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
||||||
|
|
||||||
import { getConfig } from '@framework/api'
|
import { getConfig } from '@framework/api'
|
||||||
import getAllProducts from '@framework/product/get-all-products'
|
|
||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
preview,
|
preview,
|
||||||
@ -14,7 +13,7 @@ export async function getStaticProps({
|
|||||||
}: GetStaticPropsContext) {
|
}: GetStaticPropsContext) {
|
||||||
const config = getConfig({ locale })
|
const config = getConfig({ locale })
|
||||||
|
|
||||||
const { products } = await getAllProducts({
|
const { products } = await commerce.getAllProducts({
|
||||||
variables: { first: 12 },
|
variables: { first: 12 },
|
||||||
config,
|
config,
|
||||||
preview,
|
preview,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user