Examples
These patterns show how to use @lightspeed/ecom-headless in Vue composables within a Crane theme.
All examples below assume you have a shared init composable in your project at shared/composables/useInitStorefrontApi.ts:
typescript
// shared/composables/useInitStorefrontApi.ts
import { useInstantsiteJsApi } from '@lightspeed/crane'
import { initStorefrontApi } from '@lightspeed/ecom-headless'
const ECWID_CONFIG = {
clientId: 'your-app-client-id',
baseURL: undefined, // only set for non-production environments
}
export async function useInitStorefrontApi() {
const publicToken = useInstantsiteJsApi()?.getAppPublicToken(ECWID_CONFIG.clientId) ?? ''
const storeId = useInstantsiteJsApi()?.getSiteId()
if (!publicToken) {
throw new Error('Missing public token — ensure the app is installed and has a valid client ID')
}
await initStorefrontApi({
publicToken,
storeId,
...(ECWID_CONFIG.baseURL ? { baseURL: ECWID_CONFIG.baseURL } : {}),
})
return { publicToken, storeId }
}This calls initStorefrontApi once with credentials from the Crane runtime. All composables below call it before making API requests. See Overview & Setup for what each config field does.
Fetching Products
A composable that fetches products on mount and provides reactive data:
typescript
import { searchProducts, type Product } from '@lightspeed/ecom-headless'
import { ref, computed, onMounted } from 'vue'
import { useInitStorefrontApi } from '../shared/composables/useInitStorefrontApi'
export function useProducts(
filter: (product: Product) => boolean = () => true,
limit?: number,
) {
const productsList = ref<Product[]>([])
const isLoading = ref(true)
const hasError = ref(false)
const errorMessage = ref<string | null>(null)
const products = computed(() => {
const filtered = productsList.value.filter(filter)
const limited = limit ? filtered.slice(0, limit) : filtered
return limited.map((product) => ({
id: product.id,
name: product.name,
price: product.price,
formattedPrice: product.defaultDisplayedPriceFormatted,
imageUrl: product.originalImage?.url,
url: product.url,
inStock: product.inStock,
}))
})
const fetchProducts = async (lang?: string) => {
isLoading.value = true
hasError.value = false
errorMessage.value = null
try {
await useInitStorefrontApi()
const response = await searchProducts({
...(lang ? { lang } : {}),
enabled: true,
})
productsList.value = (response.items || []) as Product[]
} catch (error) {
hasError.value = true
errorMessage.value = error instanceof Error ? error.message : 'Unknown error'
} finally {
isLoading.value = false
}
}
onMounted(() => fetchProducts())
return { products, isLoading, hasError, errorMessage, fetchProducts }
}Key patterns:
- Always call
useInitStorefrontApi()before API calls - Use
response.items || []defensively - Pass
enabled: trueto only get published products - Pass
langfor localized content - Map raw API data to a view-friendly shape in a
computed
Fetching Categories
Same pattern as products, adapted for categories:
typescript
import { searchCategories, type Category } from '@lightspeed/ecom-headless'
import { ref, computed, onMounted } from 'vue'
import { useInitStorefrontApi } from '../shared/composables/useInitStorefrontApi'
export function useCategories(limit?: number, rootOnly = true) {
const categoriesList = ref<Category[]>([])
const isLoading = ref(true)
const hasError = ref(false)
const errorMessage = ref<string | null>(null)
const categories = computed(() => {
let filtered = categoriesList.value.filter((cat) => cat.enabled)
if (rootOnly) {
filtered = filtered.filter((cat) => !cat.parentId)
}
const limited = limit ? filtered.slice(0, limit) : filtered
return limited.map((cat) => ({
id: cat.id,
name: cat.name,
url: cat.url,
imageUrl: cat.originalImageUrl || cat.imageUrl,
productCount: cat.productCount,
}))
})
const fetchCategories = async (lang?: string) => {
isLoading.value = true
hasError.value = false
errorMessage.value = null
try {
await useInitStorefrontApi()
const response = await searchCategories({ lang })
categoriesList.value = (response.items || []) as Category[]
} catch (error) {
hasError.value = true
errorMessage.value = error instanceof Error ? error.message : 'Unknown error'
} finally {
isLoading.value = false
}
}
onMounted(() => fetchCategories())
return { categories, isLoading, hasError, errorMessage, fetchCategories }
}Related
- REST API Client — Function reference
- Storefront JS API — Cart, customer, callbacks
- Overview & Setup — Installation and initialization