CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-langchain--langgraph-sdk

TypeScript/JavaScript SDK for interacting with the LangGraph REST API server

Overview
Eval results
Files

store.mddocs/

Key-Value Store

The StoreClient provides distributed key-value store operations with namespace support, TTL management, and search capabilities. It enables persistent data storage and retrieval across LangGraph applications, perfect for maintaining application state, caching data, and sharing information between different components and runs.

Core Functionality

The StoreClient supports:

  • Item Operations: Store, retrieve, and delete key-value items with rich metadata
  • Namespace Management: Organize data using hierarchical namespaces
  • Search Capabilities: Find items using flexible search criteria and filters
  • TTL Support: Automatic expiration of stored items with refresh capabilities
  • Indexing: Custom indexing for efficient search operations
  • Pagination: Handle large datasets with built-in pagination support

StoreClient API

class StoreClient extends BaseClient {
  /**
   * Store an item in the key-value store with optional indexing and TTL
   * @param namespace - Hierarchical namespace array for organization
   * @param key - Unique key within the namespace
   * @param value - Data to store (must be JSON-serializable object)
   * @param options - Storage options including indexing and TTL
   * @returns Promise resolving when storage completes
   */
  putItem(
    namespace: string[],
    key: string,
    value: Record<string, unknown>,
    options?: PutItemOptions
  ): Promise<void>;

  /**
   * Retrieve an item from the key-value store
   * @param namespace - Hierarchical namespace array
   * @param key - Unique key within the namespace
   * @param options - Retrieval options including TTL refresh
   * @returns Promise resolving to item data or null if not found
   */
  getItem(
    namespace: string[],
    key: string,
    options?: GetItemOptions
  ): Promise<Item | null>;

  /**
   * Delete an item from the key-value store
   * @param namespace - Hierarchical namespace array
   * @param key - Unique key within the namespace
   * @returns Promise resolving when deletion completes
   */
  deleteItem(namespace: string[], key: string): Promise<void>;

  /**
   * Search for items within a namespace with flexible filtering and pagination
   * @param namespacePrefix - Namespace prefix to search within
   * @param options - Search criteria, filters, and pagination options
   * @returns Promise resolving to search results with items and metadata
   */
  searchItems(
    namespacePrefix: string[],
    options?: SearchItemsOptions
  ): Promise<SearchItemsResponse>;

  /**
   * List all available namespaces with optional filtering and pagination
   * @param options - Listing options including filters and pagination
   * @returns Promise resolving to namespace information
   */
  listNamespaces(options?: ListNamespacesOptions): Promise<ListNamespaceResponse>;
}

Core Types

Item Interface

interface Item {
  /** Hierarchical namespace array */
  namespace: string[];
  /** Unique key within namespace */
  key: string;
  /** Stored value data */
  value: Record<string, unknown>;
  /** Item creation timestamp */
  created_at: string;
  /** Item last update timestamp */
  updated_at: string;
  /** TTL expiration timestamp (if applicable) */
  expires_at?: string;
  /** Custom index fields for search */
  index?: string[] | null;
}

interface SearchItem extends Item {
  /** Search relevance score */
  score?: number;
  /** Highlighted search matches */
  highlights?: Record<string, string[]>;
}

Response Types

interface SearchItemsResponse {
  /** Array of matching items */
  items: SearchItem[];
  /** Total number of matching items */
  total: number;
  /** Current page offset */
  offset: number;
  /** Items per page limit */
  limit: number;
  /** Whether more results are available */
  hasMore: boolean;
  /** Search query metadata */
  query: {
    namespace_prefix: string[];
    filter?: Record<string, any>;
    query?: string;
  };
}

interface ListNamespaceResponse {
  /** Array of namespace information */
  namespaces: Array<{
    /** Namespace path */
    namespace: string[];
    /** Number of items in namespace */
    item_count: number;
    /** Total size in bytes */
    size_bytes: number;
    /** Last activity timestamp */
    last_activity: string;
  }>;
  /** Total number of namespaces */
  total: number;
  /** Current page offset */
  offset: number;
  /** Namespaces per page limit */
  limit: number;
}

Options Types

Storage Options

interface PutItemOptions {
  /**
   * Time-to-live in seconds (null for no expiration)
   * Item will be automatically deleted after this duration
   */
  ttl?: number | null;

  /**
   * Custom index fields for search optimization
   * - false: No indexing
   * - string[]: Index specific fields
   * - null: Index all fields (default)
   */
  index?: false | string[] | null;

  /**
   * Whether to overwrite existing item
   * @default true
   */
  overwrite?: boolean;

  /**
   * Custom metadata for the item
   */
  metadata?: Record<string, any>;
}

interface GetItemOptions {
  /**
   * Whether to refresh TTL on access
   * Extends the expiration time by the original TTL duration
   */
  refreshTtl?: boolean | null;

  /**
   * Include item metadata in response
   * @default false
   */
  includeMetadata?: boolean;
}

Search Options

interface SearchItemsOptions {
  /** Text query for full-text search across item values */
  query?: string;

  /** Filter criteria for item values */
  filter?: Record<string, any>;

  /** Maximum number of results to return */
  limit?: number;

  /** Pagination offset */
  offset?: number;

  /** Sort configuration */
  sort?: {
    /** Field to sort by */
    field: string;
    /** Sort direction */
    direction: "asc" | "desc";
  }[];

  /** Include expired items in results */
  includeExpired?: boolean;

  /** Search only within specific namespaces */
  exactNamespaceMatch?: boolean;

  /** Minimum relevance score for results */
  minScore?: number;

  /** Fields to include in highlights */
  highlightFields?: string[];

  /** Include item metadata in results */
  includeMetadata?: boolean;
}

interface ListNamespacesOptions {
  /** Filter namespaces by prefix */
  prefix?: string[];

  /** Maximum number of namespaces to return */
  limit?: number;

  /** Pagination offset */
  offset?: number;

  /** Include item count statistics */
  includeStats?: boolean;

  /** Sort by field */
  sortBy?: "namespace" | "item_count" | "size_bytes" | "last_activity";

  /** Sort direction */
  sortDirection?: "asc" | "desc";
}

Usage Examples

Basic Item Operations

import { Client } from "@langchain/langgraph-sdk";

const client = new Client({
  apiUrl: "https://api.langgraph.example.com",
  apiKey: "your-api-key"
});

// Store user profile data
await client.store.putItem(
  ["users", "profiles"],
  "user_123",
  {
    name: "Alice Johnson",
    email: "alice@example.com",
    preferences: {
      theme: "dark",
      notifications: true,
      language: "en"
    },
    lastLogin: new Date().toISOString()
  },
  {
    ttl: 86400, // Expire after 24 hours
    index: ["name", "email", "preferences.theme"] // Index specific fields
  }
);

// Retrieve user profile
const userProfile = await client.store.getItem(
  ["users", "profiles"],
  "user_123",
  { refreshTtl: true } // Extend TTL on access
);

if (userProfile) {
  console.log("User:", userProfile.value.name);
  console.log("Theme:", userProfile.value.preferences?.theme);
  console.log("Expires at:", userProfile.expires_at);
} else {
  console.log("User profile not found or expired");
}

// Update user profile
await client.store.putItem(
  ["users", "profiles"],
  "user_123",
  {
    ...userProfile?.value,
    lastLogin: new Date().toISOString(),
    preferences: {
      ...userProfile?.value.preferences,
      theme: "light" // User changed theme
    }
  },
  { ttl: 86400, index: ["name", "email", "preferences.theme"] }
);

Session and Cache Management

// Store session data with automatic expiration
await client.store.putItem(
  ["sessions", "user_sessions"],
  "session_abc123",
  {
    userId: "user_456",
    startTime: new Date().toISOString(),
    permissions: ["read", "write"],
    metadata: {
      userAgent: "Mozilla/5.0...",
      ipAddress: "192.168.1.1"
    }
  },
  {
    ttl: 3600, // 1 hour session
    index: ["userId", "permissions"]
  }
);

// Cache expensive computation results
await client.store.putItem(
  ["cache", "computations"],
  "analysis_result_789",
  {
    input: { dataset: "monthly_sales", parameters: { aggregation: "sum" } },
    result: {
      total: 150000,
      breakdown: { "Q1": 35000, "Q2": 42000, "Q3": 38000, "Q4": 35000 },
      computedAt: new Date().toISOString()
    },
    executionTime: 2.5 // seconds
  },
  {
    ttl: 1800, // 30 minutes cache
    index: ["input.dataset", "result.total"],
    metadata: { cacheVersion: "1.0" }
  }
);

// Store conversation context
await client.store.putItem(
  ["conversations", "contexts"],
  "conv_456",
  {
    participants: ["user_123", "assistant_ai"],
    messages: [
      { role: "user", content: "Hello", timestamp: "2024-01-01T10:00:00Z" },
      { role: "assistant", content: "Hi! How can I help?", timestamp: "2024-01-01T10:00:01Z" }
    ],
    context: {
      topic: "general_chat",
      mood: "friendly",
      preferences: { verbosity: "normal" }
    }
  },
  {
    ttl: 86400 * 7, // 1 week
    index: ["participants", "context.topic"]
  }
);

Advanced Search Operations

// Search for users by name
const userSearchResults = await client.store.searchItems(
  ["users", "profiles"],
  {
    query: "alice johnson",
    limit: 10,
    highlightFields: ["name", "email"],
    includeMetadata: true
  }
);

console.log(`Found ${userSearchResults.total} users`);
userSearchResults.items.forEach(item => {
  console.log(`User: ${item.value.name}`);
  if (item.highlights?.name) {
    console.log(`Matched: ${item.highlights.name.join(", ")}`);
  }
});

// Filter-based search for active sessions
const activeSessions = await client.store.searchItems(
  ["sessions"],
  {
    filter: {
      permissions: { $contains: "write" },
      metadata: {
        ipAddress: { $regex: "192\\.168\\.*" }
      }
    },
    sort: [
      { field: "startTime", direction: "desc" }
    ],
    limit: 50,
    includeExpired: false
  }
);

// Complex search across multiple namespaces
const analyticsData = await client.store.searchItems(
  ["analytics"],
  {
    filter: {
      result: {
        total: { $gte: 100000 }, // Greater than or equal to 100k
        computedAt: {
          $gte: "2024-01-01T00:00:00Z",
          $lt: "2024-02-01T00:00:00Z"
        }
      }
    },
    sort: [
      { field: "result.total", direction: "desc" },
      { field: "executionTime", direction: "asc" }
    ],
    minScore: 0.5
  }
);

console.log(`Found ${analyticsData.total} analytics records`);

Namespace Management and Organization

// List all namespaces to understand data organization
const namespaces = await client.store.listNamespaces({
  includeStats: true,
  sortBy: "item_count",
  sortDirection: "desc",
  limit: 20
});

console.log("Top namespaces by item count:");
namespaces.namespaces.forEach(ns => {
  console.log(`${ns.namespace.join('/')}: ${ns.item_count} items (${ns.size_bytes} bytes)`);
  console.log(`Last activity: ${ns.last_activity}`);
});

// Organize data by application domains
const domains = [
  { namespace: ["app", "users"], description: "User data and profiles" },
  { namespace: ["app", "sessions"], description: "Session management" },
  { namespace: ["app", "cache"], description: "Application cache" },
  { namespace: ["analytics", "reports"], description: "Generated reports" },
  { namespace: ["temp", "uploads"], description: "Temporary file data" }
];

// Store domain configuration
for (const domain of domains) {
  await client.store.putItem(
    ["meta", "domains"],
    domain.namespace.join("_"),
    {
      namespace: domain.namespace,
      description: domain.description,
      createdAt: new Date().toISOString(),
      access: "internal"
    },
    { index: ["namespace", "description"] }
  );
}

TTL and Expiration Management

// Store temporary data with different TTL patterns
await client.store.putItem(
  ["temp", "quick_cache"],
  "result_123",
  { data: "quick computation result" },
  { ttl: 300 } // 5 minutes
);

await client.store.putItem(
  ["temp", "medium_cache"],
  "analysis_456",
  { analysis: "detailed analysis results" },
  { ttl: 3600 } // 1 hour
);

await client.store.putItem(
  ["persistent", "config"],
  "app_settings",
  { version: "1.0", settings: { theme: "auto" } }
  // No TTL - permanent storage
);

// Refresh TTL for frequently accessed items
const frequentlyUsedItem = await client.store.getItem(
  ["cache", "popular"],
  "trending_data",
  { refreshTtl: true } // Extends expiration
);

// Clean up expired items (handled automatically, but can check)
const expiredItems = await client.store.searchItems(
  ["temp"],
  {
    filter: {
      expires_at: { $lt: new Date().toISOString() }
    },
    includeExpired: true,
    limit: 100
  }
);

console.log(`Found ${expiredItems.total} expired items for cleanup`);

Bulk Operations and Data Migration

// Bulk data insertion for migration
const bulkUserData = [
  { id: "user_001", name: "John Doe", email: "john@example.com" },
  { id: "user_002", name: "Jane Smith", email: "jane@example.com" },
  { id: "user_003", name: "Bob Wilson", email: "bob@example.com" }
];

// Store multiple items (sequential for simplicity, can be parallelized)
for (const user of bulkUserData) {
  await client.store.putItem(
    ["migration", "users"],
    user.id,
    {
      ...user,
      migratedAt: new Date().toISOString(),
      source: "legacy_system"
    },
    {
      index: ["name", "email", "source"],
      metadata: { batch: "migration_2024_01" }
    }
  );
}

// Parallel bulk operations for better performance
const migrationPromises = bulkUserData.map(user =>
  client.store.putItem(
    ["migration", "users_parallel"],
    user.id,
    { ...user, migratedAt: new Date().toISOString() },
    { index: ["name", "email"] }
  )
);

await Promise.all(migrationPromises);
console.log("Bulk migration completed");

// Archive old data
const oldData = await client.store.searchItems(
  ["app", "users"],
  {
    filter: {
      lastLogin: { $lt: "2023-01-01T00:00:00Z" } // Older than 1 year
    },
    limit: 1000
  }
);

for (const item of oldData.items) {
  // Archive to different namespace
  await client.store.putItem(
    ["archive", "users"],
    item.key,
    {
      ...item.value,
      archivedAt: new Date().toISOString(),
      originalNamespace: item.namespace
    },
    { ttl: 86400 * 365 * 2 } // Keep archives for 2 years
  );

  // Delete from active namespace
  await client.store.deleteItem(item.namespace, item.key);
}

Error Handling and Data Integrity

async function robustStoreOperations() {
  try {
    // Attempt to store critical data with validation
    const criticalData = {
      id: "critical_001",
      value: 1000000,
      timestamp: new Date().toISOString(),
      checksum: "abc123" // Simple integrity check
    };

    await client.store.putItem(
      ["critical", "financial"],
      criticalData.id,
      criticalData,
      {
        index: ["id", "timestamp"],
        overwrite: false, // Fail if exists
        metadata: { integrity: "validated" }
      }
    );

    // Verify storage
    const stored = await client.store.getItem(
      ["critical", "financial"],
      criticalData.id
    );

    if (!stored || stored.value.checksum !== criticalData.checksum) {
      throw new Error("Data integrity check failed");
    }

    console.log("Critical data stored and verified successfully");

  } catch (error) {
    console.error("Critical operation failed:", error);

    // Attempt cleanup or recovery
    try {
      await client.store.deleteItem(["critical", "financial"], "critical_001");
      console.log("Cleaned up partial data");
    } catch (cleanupError) {
      console.error("Cleanup also failed:", cleanupError);
    }

    throw error;
  }
}

// Transactional-like operations (manual implementation)
async function pseudoTransactionalUpdate() {
  const backupKey = `backup_${Date.now()}`;

  try {
    // Create backup before update
    const original = await client.store.getItem(["data", "important"], "record_123");
    if (original) {
      await client.store.putItem(
        ["backups", "important"],
        backupKey,
        original.value,
        { ttl: 86400 } // 24h backup retention
      );
    }

    // Perform update
    await client.store.putItem(
      ["data", "important"],
      "record_123",
      {
        ...original?.value,
        updated_at: new Date().toISOString(),
        version: (original?.value.version || 0) + 1
      }
    );

    console.log("Update completed successfully");

  } catch (error) {
    console.error("Update failed, attempting rollback:", error);

    // Rollback using backup
    const backup = await client.store.getItem(["backups", "important"], backupKey);
    if (backup) {
      await client.store.putItem(["data", "important"], "record_123", backup.value);
      console.log("Rollback completed");
    }

    throw error;
  } finally {
    // Cleanup backup
    await client.store.deleteItem(["backups", "important"], backupKey);
  }
}

The StoreClient provides comprehensive key-value store capabilities with hierarchical organization, flexible search, TTL management, and robust indexing, making it ideal for persistent data storage, caching, and state management in LangGraph applications.

Install with Tessl CLI

npx tessl i tessl/npm-langchain--langgraph-sdk

docs

assistants.md

auth.md

client.md

crons.md

index.md

react.md

runs.md

store.md

threads.md

tile.json