Skip to main content
This guide covers best practices for SDK setup, controller lifecycle management, and common mistakes to avoid.

SDK Initialization

Create SDK Instance Once

Create a single SDK instance and share it across your application. Creating multiple instances wastes resources and breaks caching.
// ❌ Bad - creates multiple instances
function SearchComponent() {
  const result = createSdk({ 
    layersPublicToken: 'token',
    sorts: [{ label: 'Featured', code: 'featured' }],
    facets: ['options.color']
  })
  // Creates new SDK instance on every render
}

// ✅ Good - create once, share globally
const result = createSdk({ 
  layersPublicToken: 'token',
  sorts: [{ label: 'Featured', code: 'featured' }],
  facets: ['options.color', 'vendor']
})

if (result.data) {
  const sdk = result.data
  
  function SearchComponent() {
    const search = sdk.search()
    // ...\n  }
}

Required Configuration

Always provide sorts and facets when creating the SDK:
const result = createSdk({
  layersPublicToken: 'your-token',
  sorts: [
    { label: 'Featured', code: 'featured' },
    { label: 'Price: Low to High', code: 'price_asc' },
    { label: 'Price: High to Low', code: 'price_desc' }
  ],
  facets: [
    'options.color',
    'options.size',
    'vendor',
    'product_type'
  ]
})

Controller Lifecycle

Always Dispose Controllers

Controllers maintain subscriptions and state. Dispose them when components unmount to prevent memory leaks.
// ❌ Bad - memory leak
function SearchComponent() {
  const search = sdk.search()
  // Component unmounts but controller keeps running
}

// ✅ Good - cleanup on unmount
function SearchComponent() {
  const search = sdk.search()
  
  useEffect(() => {
    return () => search.dispose()
  }, [])
}

Controller Reuse

Controllers can be reused for multiple operations:
const search = sdk.search()

// Use the same controller for multiple searches
await search.execute({ query: 'ring' })
await search.execute({ query: 'necklace' })
await search.execute({ query: 'bracelet' })

// Dispose when done
search.dispose()

Filter Handling

Use Filter DSL for Complex Filters

For complex filter conditions, use the filter DSL:
import { filter, and, eq } from '@commerce-blocks/sdk'

// Use filter DSL for explicit control
const collection = sdk.collection({ handle: 'shirts' })
await collection.execute({ 
  filters: filter(and(
    eq('options.color', 'Red'),
    eq('options.size', 'M')
  ))
})

Error Handling

Handle Errors Gracefully

Always check for errors in the Result object:
const search = sdk.search()
const result = await search.execute({ query: 'ring' })

if (result.error) {
  switch (result.error._tag) {
    case 'NetworkError':
      showError('Connection issue. Please try again.')
      break
    case 'ApiError':
      if (result.error.status === 429) {
        showError('Too many requests. Please wait a moment.')
      }
      break
    case 'ValidationError':
      showError('Invalid search parameters.')
      break
  }
  return
}

// Safe to use result.data
const products = result.data.products

Prepare Failures Are Non-Fatal

If prepare fails, execute will still work (but may be slower):
const search = sdk.search()
const prepareResult = await search.prepare({ query: 'ring' })

if (prepareResult.error) {
  console.warn('Prepare failed, execute will work but may be slower')
}

// Execute still works
const result = await search.execute({ query: 'ring' })

Next Steps