> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uselayers.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Best practices

> Essential patterns and common pitfalls when working with the Layers SDK, covering initialization, state management, caching, and request lifecycle handling.

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

## SDK initialization

### Create client once

Create a single client instance and share it across your application. Creating multiple instances wastes resources and breaks caching.

```typescript theme={null}
// ❌ Bad - creates multiple instances
function SearchComponent() {
  const { data: client } = createClient({ 
    token: 'your-token',
    sorts: [{ name: 'Featured', code: 'featured' }],
    facets: [{ name: 'Color', code: 'options.color' }],
  })
  // Creates new client instance on every render
}

// ✅ Good - create once, share globally
const { data: client } = createClient({ 
  token: 'your-token',
  sorts: [{ name: 'Featured', code: 'featured' }],
  facets: [{ name: 'Color', code: 'options.color' }],
})

if (client) {
  function SearchComponent() {
    const search = client.search()
    // ...
  }
}
```

### Required configuration

Always provide `sorts` and `facets` when creating the client:

```typescript theme={null}
const { data: client, error } = createClient({
  token: 'your-token',
  sorts: [
    { name: 'Featured', code: 'featured' },
    { name: 'Price: Low to High', code: 'price_asc' },
    { name: 'Price: High to Low', code: 'price_desc' },
  ],
  facets: [
    { name: 'Color', code: 'options.color' },
    { name: 'Size', code: 'options.size' },
    { name: 'Vendor', code: 'vendor' },
    { name: 'Product Type', code: 'product_type' },
  ],
})
```

## Controller lifecycle

### Always dispose controllers

Controllers maintain subscriptions and state. Dispose them when components unmount to prevent memory leaks.

```typescript theme={null}
// ❌ Bad - memory leak
function SearchComponent() {
  const search = client.search()
  // Component unmounts but controller keeps running
}

// ✅ Good - cleanup on unmount
function SearchComponent() {
  const search = client.search()
  
  onUnmount(() => search.dispose()) // framework-specific cleanup
}
```

### Controller reuse

Controllers can be reused for multiple operations:

```typescript theme={null}
const search = client.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:

```typescript theme={null}
import { filter, and, eq } from '@commerce-blocks/sdk'

// Use filter DSL for explicit control
const collection = client.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:

```typescript theme={null}
const search = client.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):

```typescript theme={null}
const search = client.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

<CardGroup cols={2}>
  <Card title="Search Patterns" icon="magnifying-glass" href="/sdk/search-patterns">
    Learn advanced search patterns including prepare/execute flows.
  </Card>

  <Card title="Caching" icon="database" href="/sdk/caching">
    Understand SDK caching behavior and optimization.
  </Card>
</CardGroup>
