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

# Best Practices

> Recommended patterns for building with OnDB

## Authentication

<Card title="Use App Key Auth" icon="key">
  Use appKey for writes. Use agentKey for autonomous agent operations. Never expose keys in client-side code.
</Card>

```typescript theme={null}
// Server-side
const client = createClient({
  endpoint: process.env.ONDB_ENDPOINT,
  appId: process.env.ONDB_APP_ID,
  appKey: process.env.ONDB_APP_KEY
});
```

## Indexes

<Warning>
  **Create Indexes First**

  You MUST create at least one index per collection. Without indexes, queries perform full collection scans and the dashboard cannot display collections. Create indexes BEFORE storing data.
</Warning>

```typescript theme={null}
// Always create indexes before storing data
const db = client.database('my-app');

await db.createIndex({
  name: 'idx_users_email',
  collection: 'users',
  field_name: 'email',
  index_type: 'hash',
  unique_constraint: true,
  store_values: true
});

// Index fields you'll query
await db.createIndex({
  name: 'idx_users_createdAt',
  collection: 'users',
  field_name: 'createdAt',
  index_type: 'btree'
});

// Now store data
await client.store({ collection: 'users', data: [...] });
```

## Payment Handling

<Card title="Handle Payment Callbacks" icon="credit-card">
  Implement payment callbacks for store operations. Handle PaymentRequiredError for read operations.
</Card>

```typescript theme={null}
// Write operations - server provides quote, caller handles USDC payment
await client.store(
  { collection: 'data', data },
  async (quote) => {
    const txHash = await processPayment(quote);
    return { txHash, network: quote.network, sender: walletAddress, chainType: quote.chainType, paymentMethod: 'native' };
  },
  true
);

// Read operations
try {
  const result = await client.queryBuilder()
    .collection('premium_data')
    .execute();
} catch (error) {
  if (error instanceof PaymentRequiredError) {
    // Handle payment flow
    const paymentInfo = error.paymentRequired;
    // ...
  }
}
```

## Error Handling

<Card title="Use Specific Error Types" icon="triangle-exclamation">
  Always implement proper error handling with specific error types.
</Card>

```typescript theme={null}
import {
  ValidationError,
  TransactionError,
  PaymentRequiredError,
  OnDBError
} from '@ondb/sdk';

try {
  await client.store(data, paymentCallback);
} catch (error) {
  if (error instanceof ValidationError) {
    // Handle validation errors
  } else if (error instanceof TransactionError) {
    // Handle transaction failures
  } else if (error instanceof PaymentRequiredError) {
    // Handle payment flow
  } else if (error instanceof OnDBError) {
    // Handle other SDK errors
  }
}
```

## Query Optimization

<Card title="Use Query Builder for All Queries" icon="magnifying-glass">
  Use the QueryBuilder fluent API for all read operations.
</Card>

```typescript theme={null}
// Simple queries
const user = await client.queryBuilder()
  .collection('users')
  .whereField('email').equals('alice@example.com')
  .executeUnique();

// Complex queries
const results = await client.queryBuilder()
  .collection('orders')
  .whereField('status').equals('completed')
  .whereField('createdAt').greaterThanOrEqual(new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString())
  .joinOne('customer', 'users')
    .onField('id').equals('$data.customerId')
    .selectFields(['name', 'email'])
    .build()
  .selectAll()
  .limit(50)
  .execute();
```

## Async Operations

<Card title="Use Task Tickets for Long Operations" icon="clock">
  Use task tickets for long-running operations and monitor progress.
</Card>

```typescript theme={null}
// Non-blocking store
const response = await client.store(data, paymentCallback, false);

// Monitor progress
const status = await client.getTaskStatus(response.ticket_id);

// Or wait for completion
const task = await client.waitForTaskCompletion(response.ticket_id);
```

## Cost Awareness

<Card title="Inspect Costs via Payment Callback" icon="calculator">
  The payment callback receives a quote with full cost details before you authorize payment.
</Card>

```typescript theme={null}
await client.store(
  { collection: 'users', data },
  async (quote) => {
    if (quote.totalCost > maxBudget) {
      throw new Error(`Operation costs ${quote.totalCost} ${quote.tokenSymbol}, exceeds budget`);
    }
    const txHash = await processPayment(quote);
    return { txHash, network: quote.network, sender: walletAddress, chainType: quote.chainType, paymentMethod: 'native' };
  },
  true
);
```

## PriceIndex for Commerce

<Card title="Use PriceIndex for Revenue Sharing" icon="tags">
  Use PriceIndex when building commerce platforms, ticketing systems, or apps with revenue sharing.
</Card>

```typescript theme={null}
// Create PriceIndex on payment field
await db.createIndex({
  name: 'idx_orders_total',
  collection: 'orders',
  field_name: 'totalPrice',
  index_type: 'price'
});

// Payment = field value, not storage cost
await client.store(
  { collection: 'orders', data: [{ totalPrice: 100000000, items: [...] }] },
  paymentCallback,
  true
);
```

## TypeScript

<Card title="Leverage Type Safety" icon="code">
  Use TypeScript for full type safety and better development experience.
</Card>

```typescript theme={null}
interface User {
  id: string;
  email: string;
  name: string;
  createdAt: string;
}

const user = await client.queryBuilder()
  .collection('users')
  .whereField('email').equals('alice@example.com')
  .executeUnique<User>();
// user is User | null with full IntelliSense

const users = await client.queryBuilder()
  .collection('users')
  .whereField('active').isTrue()
  .execute<User>();
// users.records is User[]
```

## Batch Operations

<Card title="Use Batches for Bulk Data" icon="layer-group">
  Use batch operations for large datasets to improve efficiency.
</Card>

```typescript theme={null}
// Store multiple records in a single call
await client.store(
  { collection: 'products', data: products },
  paymentCallback,
  true
);

// For large datasets, chunk into batches
const BATCH_SIZE = 100;
for (let i = 0; i < products.length; i += BATCH_SIZE) {
  const chunk = products.slice(i, i + BATCH_SIZE);
  await client.store({ collection: 'products', data: chunk }, paymentCallback, true);
  console.log(`${Math.min(i + BATCH_SIZE, products.length)}/${products.length}`);
}
```

## Transaction Monitoring

<Card title="Monitor Transactions in Real-Time" icon="eye">
  Use event listeners to track transaction status in real-time.
</Card>

```typescript theme={null}
client.on('transaction:queued', (ticket) => {
  console.log(`Task ${ticket.ticket_id} queued`);
});

client.on('transaction:confirmed', (tx) => {
  console.log(`Confirmed at block ${tx.block_height}`);
});

client.on('transaction:failed', (tx) => {
  console.error(`Failed: ${tx.error}`);
});
```

## Checklist

* [ ] Create indexes before storing data
* [ ] Use environment variables for keys
* [ ] Implement payment callbacks
* [ ] Handle all error types
* [ ] Use TypeScript for type safety
* [ ] Get cost estimates before operations
* [ ] Monitor transactions with events
* [ ] Use batch operations for bulk data
* [ ] Consider PriceIndex for commerce apps
