Skip to main content

Authentication

Use App Key Auth

Use appKey for writes. Use agentKey for autonomous agent operations. Never expose keys in client-side code.
// Server-side
const client = createClient({
  endpoint: process.env.ONDB_ENDPOINT,
  appId: process.env.ONDB_APP_ID,
  appKey: process.env.ONDB_APP_KEY
});

Indexes

Create Indexes FirstYou 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.
// 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

Handle Payment Callbacks

Implement payment callbacks for store operations. Handle PaymentRequiredError for read operations.
// 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

Use Specific Error Types

Always implement proper error handling with specific error types.
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

Use Query Builder for All Queries

Use the QueryBuilder fluent API for all read operations.
// 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

Use Task Tickets for Long Operations

Use task tickets for long-running operations and monitor progress.
// 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

Inspect Costs via Payment Callback

The payment callback receives a quote with full cost details before you authorize payment.
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

Use PriceIndex for Revenue Sharing

Use PriceIndex when building commerce platforms, ticketing systems, or apps with revenue sharing.
// 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

Leverage Type Safety

Use TypeScript for full type safety and better development experience.
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

Use Batches for Bulk Data

Use batch operations for large datasets to improve efficiency.
// 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

Monitor Transactions in Real-Time

Use event listeners to track transaction status in real-time.
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