TypeScript/JavaScript SDK for interacting with the LangGraph REST API server
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.
The StoreClient supports:
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>;
}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[]>;
}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;
}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;
}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";
}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"] }
);// 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"]
}
);// 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`);// 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"] }
);
}// 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 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);
}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