Skip to main content

TypeScript SDK: Advanced Topics

Error handling, interceptors, pagination, types, and best practices.

Error Handling

The SDK provides typed error classes for precise error handling:

error-handling.ts
import {
  TrixError,
  AuthenticationError,
  PermissionError,
  NotFoundError,
  ValidationError,
  RateLimitError,
  NetworkError,
  TimeoutError,
  ServerError
} from '@trixdb/client';

try {
  await trix.memories.create({ content: '' });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log('Validation failed:', error.message);
    console.log('Field errors:', error.errors);
  } else if (error instanceof RateLimitError) {
    console.log('Rate limited. Retry after:', error.retryAfter, 'seconds');
  } else if (error instanceof AuthenticationError) {
    console.log('Invalid API key');
  } else if (error instanceof NotFoundError) {
    console.log('Resource not found');
  } else if (error instanceof PermissionError) {
    console.log('Insufficient permissions');
  } else if (error instanceof NetworkError) {
    console.log('Network error:', error.message);
  } else if (error instanceof TimeoutError) {
    console.log('Request timed out');
  } else if (error instanceof ServerError) {
    console.log('Server error:', error.statusCode);
  } else if (error instanceof TrixError) {
    console.log('Trix error:', error.message);
  }
}

Error Properties

Error TypeProperties
ValidationErrormessage, errors (field-level errors)
RateLimitErrormessage, retryAfter (seconds)
ServerErrormessage, statusCode, response
All othersmessage

Interceptors

Interceptors let you hook into the request/response lifecycle for logging, authentication, or custom logic.

Request Interceptor

request-interceptor.ts
const removeInterceptor = trix.addRequestInterceptor((context) => {
  console.log(`Request: ${context.method} ${context.url}`);

  return {
    ...context,
    headers: {
      ...context.headers,
      'X-Request-ID': generateRequestId()
    }
  };
});

removeInterceptor();

Response Interceptor

response-interceptor.ts
trix.addResponseInterceptor((context) => {
  console.log(`Response: ${context.status} ${context.statusText}`);

  if (context.headers.get('x-response-time') > 1000) {
    console.warn('Slow response detected');
  }

  return context;
});

Error Interceptor

error-interceptor.ts
trix.addErrorInterceptor((error, request) => {
  console.error(`Error on ${request.method} ${request.url}:`, error.message);

  errorTracker.captureException(error, {
    extra: { request }
  });

  return error;
});

Pagination

List endpoints return paginated responses. Use async iteration for the simplest approach, or handle pagination manually.

Async Iteration (Recommended)

async-iteration.ts
for await (const memory of trix.memories.listAll({ limit: 100 })) {
  processMemory(memory);
}

for await (const cluster of trix.clusters.listAll()) { }
for await (const webhook of trix.webhooks.listAll()) { }
for await (const session of trix.agent.listSessionsAll()) { }

Manual Pagination

manual-pagination.ts
interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    total: number;
    page: number;
    limit: number;
    hasMore: boolean;
  };
}

let page = 1;
let hasMore = true;

while (hasMore) {
  const response = await trix.memories.list({ page, limit: 100 });
  for (const memory of response.data) {
    processMemory(memory);
  }
  hasMore = response.pagination.hasMore;
  page++;
}

Pagination Helper

pagination-helper.ts
import { paginateAll } from '@trixdb/client';

const allMemories = await paginateAll(
  (params) => trix.memories.list(params),
  { limit: 100 }
);

console.log(`Total memories: ${allMemories.length}`);

Retry & Backoff

The SDK automatically retries failed requests with exponential backoff.

Default Retry Behavior

default-retry.ts
{
  maxRetries: 3,
  initialDelay: 1000,
  maxDelay: 60000,
  backoffMultiplier: 2,
  jitter: true
}

// Retryable errors:
// - RateLimitError (429)
// - NetworkError
// - TimeoutError
// - ServerError (5xx)

Manual Retry

manual-retry.ts
import { retry } from '@trixdb/client';

const result = await retry(
  () => trix.memories.create({ content: 'test' }),
  {
    maxRetries: 5,
    initialDelay: 2000,
    maxDelay: 30000
  }
);

TypeScript Types

The SDK exports all types for full TypeScript support:

types.ts
import type {
  Memory,
  MemoryType,
  Relationship,
  Cluster,
  Space,
  Session,
  Webhook,
  Job,
  Highlight,
  Transcript,
  CreateMemoryRequest,
  UpdateMemoryRequest,
  ListMemoriesRequest,
  PaginatedResponse,
  TrixConfig,
  TrixError,
  ValidationError,
  RateLimitError,
  RequestInterceptor,
  ResponseInterceptor,
  ErrorInterceptor
} from '@trixdb/client';

Memory Type

memory-type.ts
interface Memory {
  id: string;
  spaceId: string;
  type: 'text' | 'markdown' | 'url' | 'audio';
  content: string;
  embedding?: number[];
  tags?: string[];
  metadata?: Record<string, unknown>;
  createdAt: string;
  updatedAt: string;
  transcriptStatus?: 'pending' | 'processing' | 'completed' | 'failed';
}

Relationship Type

relationship-type.ts
interface Relationship {
  id: string;
  sourceId: string;
  targetId: string;
  relationshipType: string;
  weight: number;
  metadata?: Record<string, unknown>;
  createdAt: string;
  updatedAt: string;
}

Session Type

session-type.ts
interface Session {
  id: string;
  spaceId?: string;
  name?: string;
  metadata?: Record<string, unknown>;
  startedAt: string;
  endedAt?: string;
  memoryCount: number;
}

Debugging

Enable debug logging to troubleshoot issues:

debugging.ts
import { enableDebugLogging, disableDebugLogging } from '@trixdb/client';

enableDebugLogging();

enableDebugLogging((message) => {
  console.log(`[Trix ${new Date().toISOString()}] ${message}`);
});

disableDebugLogging();

Debug logging outputs request/response details including URLs, headers, and timing information. Sensitive data like API keys is automatically redacted.

Best Practices

1. Use Environment Variables

env-vars.ts
const trix = Trix.fromEnv();
// TRIX_API_KEY=gdb_your_api_key

2. Handle Errors Gracefully

graceful-errors.ts
async function safeCreateMemory(content: string) {
  try {
    return await trix.memories.create({ content });
  } catch (error) {
    if (error instanceof RateLimitError) {
      await sleep(error.retryAfter * 1000);
      return await trix.memories.create({ content });
    }
    if (error instanceof ValidationError) {
      console.error('Invalid memory:', error.errors);
      return null;
    }
    throw error;
  }
}

3. Use Async Iteration for Large Datasets

async-iteration-best.ts
for await (const memory of trix.memories.listAll()) {
  await processMemory(memory);
}

const all = await paginateAll(trix.memories.list);
all.forEach(processMemory);

4. Batch Operations When Possible

batch-operations.ts
await trix.memories.bulkCreate([
  { content: 'Memory 1' },
  { content: 'Memory 2' },
  { content: 'Memory 3' }
]);

await trix.memories.create({ content: 'Memory 1' });
await trix.memories.create({ content: 'Memory 2' });
await trix.memories.create({ content: 'Memory 3' });

5. Use Spaces for Isolation

space-isolation.ts
const userSpace = await trix.spaces.create({
  name: `User ${userId}`,
  metadata: { userId }
});

const memories = await trix.memories.list({
  spaceId: userSpace.id,
  q: 'search query'
});

Complete Example

Here's a complete example demonstrating common SDK operations:

complete-example.ts
import { Trix, ValidationError, RateLimitError } from '@trixdb/client';

async function main() {
  const trix = Trix.fromEnv();

  try {
    const memory1 = await trix.memories.create({
      content: 'TypeScript provides static type checking',
      tags: ['typescript', 'programming']
    });

    const memory2 = await trix.memories.create({
      content: 'React uses a virtual DOM for efficient updates',
      tags: ['react', 'frontend']
    });

    console.log('Created memories:', memory1.id, memory2.id);

    const relationship = await trix.relationships.create(
      memory1.id, memory2.id,
      { relationshipType: 'related_to', weight: 0.75 }
    );
    console.log('Created relationship:', relationship.id);

    const cluster = await trix.clusters.create({
      name: 'Web Development',
      memoryIds: [memory1.id, memory2.id]
    });
    console.log('Created cluster:', cluster.id);

    const similar = await trix.search.similar(memory1.id, {
      limit: 5, threshold: 0.5
    });
    for (const { memory, similarity } of similar.results) {
      console.log(`  ${similarity.toFixed(2)}: ${memory.content.slice(0, 40)}...`);
    }

    const context = await trix.graph.getContext({
      memoryId: memory1.id, depth: 2
    });
    console.log('Context includes', context.related.length, 'related memories');

    const session = await trix.agent.createSession({ name: 'Learning Session' });
    await trix.agent.addSessionMemory(session.id, {
      content: 'User is learning about web development',
      tags: ['session-note']
    });
    await trix.agent.endSession(session.id, { consolidate: true });
    console.log('Session completed');

    await trix.feedback.quick({ memoryId: memory1.id, type: 'thumbs_up' });
    console.log('Feedback submitted');

  } catch (error) {
    if (error instanceof ValidationError) {
      console.error('Validation error:', error.errors);
    } else if (error instanceof RateLimitError) {
      console.error('Rate limited. Retry after:', error.retryAfter);
    } else {
      throw error;
    }
  }
}

main().catch(console.error);

Next Steps