or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assistants.mdauth.mdclient.mdcrons.mdindex.mdreact.mdruns.mdstore.mdthreads.md

store.mddocs/

0

# Key-Value Store

1

2

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.

3

4

## Core Functionality

5

6

The StoreClient supports:

7

8

- **Item Operations**: Store, retrieve, and delete key-value items with rich metadata

9

- **Namespace Management**: Organize data using hierarchical namespaces

10

- **Search Capabilities**: Find items using flexible search criteria and filters

11

- **TTL Support**: Automatic expiration of stored items with refresh capabilities

12

- **Indexing**: Custom indexing for efficient search operations

13

- **Pagination**: Handle large datasets with built-in pagination support

14

15

## StoreClient API

16

17

```typescript { .api }

18

class StoreClient extends BaseClient {

19

/**

20

* Store an item in the key-value store with optional indexing and TTL

21

* @param namespace - Hierarchical namespace array for organization

22

* @param key - Unique key within the namespace

23

* @param value - Data to store (must be JSON-serializable object)

24

* @param options - Storage options including indexing and TTL

25

* @returns Promise resolving when storage completes

26

*/

27

putItem(

28

namespace: string[],

29

key: string,

30

value: Record<string, unknown>,

31

options?: PutItemOptions

32

): Promise<void>;

33

34

/**

35

* Retrieve an item from the key-value store

36

* @param namespace - Hierarchical namespace array

37

* @param key - Unique key within the namespace

38

* @param options - Retrieval options including TTL refresh

39

* @returns Promise resolving to item data or null if not found

40

*/

41

getItem(

42

namespace: string[],

43

key: string,

44

options?: GetItemOptions

45

): Promise<Item | null>;

46

47

/**

48

* Delete an item from the key-value store

49

* @param namespace - Hierarchical namespace array

50

* @param key - Unique key within the namespace

51

* @returns Promise resolving when deletion completes

52

*/

53

deleteItem(namespace: string[], key: string): Promise<void>;

54

55

/**

56

* Search for items within a namespace with flexible filtering and pagination

57

* @param namespacePrefix - Namespace prefix to search within

58

* @param options - Search criteria, filters, and pagination options

59

* @returns Promise resolving to search results with items and metadata

60

*/

61

searchItems(

62

namespacePrefix: string[],

63

options?: SearchItemsOptions

64

): Promise<SearchItemsResponse>;

65

66

/**

67

* List all available namespaces with optional filtering and pagination

68

* @param options - Listing options including filters and pagination

69

* @returns Promise resolving to namespace information

70

*/

71

listNamespaces(options?: ListNamespacesOptions): Promise<ListNamespaceResponse>;

72

}

73

```

74

75

## Core Types

76

77

### Item Interface

78

79

```typescript { .api }

80

interface Item {

81

/** Hierarchical namespace array */

82

namespace: string[];

83

/** Unique key within namespace */

84

key: string;

85

/** Stored value data */

86

value: Record<string, unknown>;

87

/** Item creation timestamp */

88

created_at: string;

89

/** Item last update timestamp */

90

updated_at: string;

91

/** TTL expiration timestamp (if applicable) */

92

expires_at?: string;

93

/** Custom index fields for search */

94

index?: string[] | null;

95

}

96

97

interface SearchItem extends Item {

98

/** Search relevance score */

99

score?: number;

100

/** Highlighted search matches */

101

highlights?: Record<string, string[]>;

102

}

103

```

104

105

### Response Types

106

107

```typescript { .api }

108

interface SearchItemsResponse {

109

/** Array of matching items */

110

items: SearchItem[];

111

/** Total number of matching items */

112

total: number;

113

/** Current page offset */

114

offset: number;

115

/** Items per page limit */

116

limit: number;

117

/** Whether more results are available */

118

hasMore: boolean;

119

/** Search query metadata */

120

query: {

121

namespace_prefix: string[];

122

filter?: Record<string, any>;

123

query?: string;

124

};

125

}

126

127

interface ListNamespaceResponse {

128

/** Array of namespace information */

129

namespaces: Array<{

130

/** Namespace path */

131

namespace: string[];

132

/** Number of items in namespace */

133

item_count: number;

134

/** Total size in bytes */

135

size_bytes: number;

136

/** Last activity timestamp */

137

last_activity: string;

138

}>;

139

/** Total number of namespaces */

140

total: number;

141

/** Current page offset */

142

offset: number;

143

/** Namespaces per page limit */

144

limit: number;

145

}

146

```

147

148

## Options Types

149

150

### Storage Options

151

152

```typescript { .api }

153

interface PutItemOptions {

154

/**

155

* Time-to-live in seconds (null for no expiration)

156

* Item will be automatically deleted after this duration

157

*/

158

ttl?: number | null;

159

160

/**

161

* Custom index fields for search optimization

162

* - false: No indexing

163

* - string[]: Index specific fields

164

* - null: Index all fields (default)

165

*/

166

index?: false | string[] | null;

167

168

/**

169

* Whether to overwrite existing item

170

* @default true

171

*/

172

overwrite?: boolean;

173

174

/**

175

* Custom metadata for the item

176

*/

177

metadata?: Record<string, any>;

178

}

179

180

interface GetItemOptions {

181

/**

182

* Whether to refresh TTL on access

183

* Extends the expiration time by the original TTL duration

184

*/

185

refreshTtl?: boolean | null;

186

187

/**

188

* Include item metadata in response

189

* @default false

190

*/

191

includeMetadata?: boolean;

192

}

193

```

194

195

### Search Options

196

197

```typescript { .api }

198

interface SearchItemsOptions {

199

/** Text query for full-text search across item values */

200

query?: string;

201

202

/** Filter criteria for item values */

203

filter?: Record<string, any>;

204

205

/** Maximum number of results to return */

206

limit?: number;

207

208

/** Pagination offset */

209

offset?: number;

210

211

/** Sort configuration */

212

sort?: {

213

/** Field to sort by */

214

field: string;

215

/** Sort direction */

216

direction: "asc" | "desc";

217

}[];

218

219

/** Include expired items in results */

220

includeExpired?: boolean;

221

222

/** Search only within specific namespaces */

223

exactNamespaceMatch?: boolean;

224

225

/** Minimum relevance score for results */

226

minScore?: number;

227

228

/** Fields to include in highlights */

229

highlightFields?: string[];

230

231

/** Include item metadata in results */

232

includeMetadata?: boolean;

233

}

234

235

interface ListNamespacesOptions {

236

/** Filter namespaces by prefix */

237

prefix?: string[];

238

239

/** Maximum number of namespaces to return */

240

limit?: number;

241

242

/** Pagination offset */

243

offset?: number;

244

245

/** Include item count statistics */

246

includeStats?: boolean;

247

248

/** Sort by field */

249

sortBy?: "namespace" | "item_count" | "size_bytes" | "last_activity";

250

251

/** Sort direction */

252

sortDirection?: "asc" | "desc";

253

}

254

```

255

256

## Usage Examples

257

258

### Basic Item Operations

259

260

```typescript

261

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

262

263

const client = new Client({

264

apiUrl: "https://api.langgraph.example.com",

265

apiKey: "your-api-key"

266

});

267

268

// Store user profile data

269

await client.store.putItem(

270

["users", "profiles"],

271

"user_123",

272

{

273

name: "Alice Johnson",

274

email: "alice@example.com",

275

preferences: {

276

theme: "dark",

277

notifications: true,

278

language: "en"

279

},

280

lastLogin: new Date().toISOString()

281

},

282

{

283

ttl: 86400, // Expire after 24 hours

284

index: ["name", "email", "preferences.theme"] // Index specific fields

285

}

286

);

287

288

// Retrieve user profile

289

const userProfile = await client.store.getItem(

290

["users", "profiles"],

291

"user_123",

292

{ refreshTtl: true } // Extend TTL on access

293

);

294

295

if (userProfile) {

296

console.log("User:", userProfile.value.name);

297

console.log("Theme:", userProfile.value.preferences?.theme);

298

console.log("Expires at:", userProfile.expires_at);

299

} else {

300

console.log("User profile not found or expired");

301

}

302

303

// Update user profile

304

await client.store.putItem(

305

["users", "profiles"],

306

"user_123",

307

{

308

...userProfile?.value,

309

lastLogin: new Date().toISOString(),

310

preferences: {

311

...userProfile?.value.preferences,

312

theme: "light" // User changed theme

313

}

314

},

315

{ ttl: 86400, index: ["name", "email", "preferences.theme"] }

316

);

317

```

318

319

### Session and Cache Management

320

321

```typescript

322

// Store session data with automatic expiration

323

await client.store.putItem(

324

["sessions", "user_sessions"],

325

"session_abc123",

326

{

327

userId: "user_456",

328

startTime: new Date().toISOString(),

329

permissions: ["read", "write"],

330

metadata: {

331

userAgent: "Mozilla/5.0...",

332

ipAddress: "192.168.1.1"

333

}

334

},

335

{

336

ttl: 3600, // 1 hour session

337

index: ["userId", "permissions"]

338

}

339

);

340

341

// Cache expensive computation results

342

await client.store.putItem(

343

["cache", "computations"],

344

"analysis_result_789",

345

{

346

input: { dataset: "monthly_sales", parameters: { aggregation: "sum" } },

347

result: {

348

total: 150000,

349

breakdown: { "Q1": 35000, "Q2": 42000, "Q3": 38000, "Q4": 35000 },

350

computedAt: new Date().toISOString()

351

},

352

executionTime: 2.5 // seconds

353

},

354

{

355

ttl: 1800, // 30 minutes cache

356

index: ["input.dataset", "result.total"],

357

metadata: { cacheVersion: "1.0" }

358

}

359

);

360

361

// Store conversation context

362

await client.store.putItem(

363

["conversations", "contexts"],

364

"conv_456",

365

{

366

participants: ["user_123", "assistant_ai"],

367

messages: [

368

{ role: "user", content: "Hello", timestamp: "2024-01-01T10:00:00Z" },

369

{ role: "assistant", content: "Hi! How can I help?", timestamp: "2024-01-01T10:00:01Z" }

370

],

371

context: {

372

topic: "general_chat",

373

mood: "friendly",

374

preferences: { verbosity: "normal" }

375

}

376

},

377

{

378

ttl: 86400 * 7, // 1 week

379

index: ["participants", "context.topic"]

380

}

381

);

382

```

383

384

### Advanced Search Operations

385

386

```typescript

387

// Search for users by name

388

const userSearchResults = await client.store.searchItems(

389

["users", "profiles"],

390

{

391

query: "alice johnson",

392

limit: 10,

393

highlightFields: ["name", "email"],

394

includeMetadata: true

395

}

396

);

397

398

console.log(`Found ${userSearchResults.total} users`);

399

userSearchResults.items.forEach(item => {

400

console.log(`User: ${item.value.name}`);

401

if (item.highlights?.name) {

402

console.log(`Matched: ${item.highlights.name.join(", ")}`);

403

}

404

});

405

406

// Filter-based search for active sessions

407

const activeSessions = await client.store.searchItems(

408

["sessions"],

409

{

410

filter: {

411

permissions: { $contains: "write" },

412

metadata: {

413

ipAddress: { $regex: "192\\.168\\.*" }

414

}

415

},

416

sort: [

417

{ field: "startTime", direction: "desc" }

418

],

419

limit: 50,

420

includeExpired: false

421

}

422

);

423

424

// Complex search across multiple namespaces

425

const analyticsData = await client.store.searchItems(

426

["analytics"],

427

{

428

filter: {

429

result: {

430

total: { $gte: 100000 }, // Greater than or equal to 100k

431

computedAt: {

432

$gte: "2024-01-01T00:00:00Z",

433

$lt: "2024-02-01T00:00:00Z"

434

}

435

}

436

},

437

sort: [

438

{ field: "result.total", direction: "desc" },

439

{ field: "executionTime", direction: "asc" }

440

],

441

minScore: 0.5

442

}

443

);

444

445

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

446

```

447

448

### Namespace Management and Organization

449

450

```typescript

451

// List all namespaces to understand data organization

452

const namespaces = await client.store.listNamespaces({

453

includeStats: true,

454

sortBy: "item_count",

455

sortDirection: "desc",

456

limit: 20

457

});

458

459

console.log("Top namespaces by item count:");

460

namespaces.namespaces.forEach(ns => {

461

console.log(`${ns.namespace.join('/')}: ${ns.item_count} items (${ns.size_bytes} bytes)`);

462

console.log(`Last activity: ${ns.last_activity}`);

463

});

464

465

// Organize data by application domains

466

const domains = [

467

{ namespace: ["app", "users"], description: "User data and profiles" },

468

{ namespace: ["app", "sessions"], description: "Session management" },

469

{ namespace: ["app", "cache"], description: "Application cache" },

470

{ namespace: ["analytics", "reports"], description: "Generated reports" },

471

{ namespace: ["temp", "uploads"], description: "Temporary file data" }

472

];

473

474

// Store domain configuration

475

for (const domain of domains) {

476

await client.store.putItem(

477

["meta", "domains"],

478

domain.namespace.join("_"),

479

{

480

namespace: domain.namespace,

481

description: domain.description,

482

createdAt: new Date().toISOString(),

483

access: "internal"

484

},

485

{ index: ["namespace", "description"] }

486

);

487

}

488

```

489

490

### TTL and Expiration Management

491

492

```typescript

493

// Store temporary data with different TTL patterns

494

await client.store.putItem(

495

["temp", "quick_cache"],

496

"result_123",

497

{ data: "quick computation result" },

498

{ ttl: 300 } // 5 minutes

499

);

500

501

await client.store.putItem(

502

["temp", "medium_cache"],

503

"analysis_456",

504

{ analysis: "detailed analysis results" },

505

{ ttl: 3600 } // 1 hour

506

);

507

508

await client.store.putItem(

509

["persistent", "config"],

510

"app_settings",

511

{ version: "1.0", settings: { theme: "auto" } }

512

// No TTL - permanent storage

513

);

514

515

// Refresh TTL for frequently accessed items

516

const frequentlyUsedItem = await client.store.getItem(

517

["cache", "popular"],

518

"trending_data",

519

{ refreshTtl: true } // Extends expiration

520

);

521

522

// Clean up expired items (handled automatically, but can check)

523

const expiredItems = await client.store.searchItems(

524

["temp"],

525

{

526

filter: {

527

expires_at: { $lt: new Date().toISOString() }

528

},

529

includeExpired: true,

530

limit: 100

531

}

532

);

533

534

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

535

```

536

537

### Bulk Operations and Data Migration

538

539

```typescript

540

// Bulk data insertion for migration

541

const bulkUserData = [

542

{ id: "user_001", name: "John Doe", email: "john@example.com" },

543

{ id: "user_002", name: "Jane Smith", email: "jane@example.com" },

544

{ id: "user_003", name: "Bob Wilson", email: "bob@example.com" }

545

];

546

547

// Store multiple items (sequential for simplicity, can be parallelized)

548

for (const user of bulkUserData) {

549

await client.store.putItem(

550

["migration", "users"],

551

user.id,

552

{

553

...user,

554

migratedAt: new Date().toISOString(),

555

source: "legacy_system"

556

},

557

{

558

index: ["name", "email", "source"],

559

metadata: { batch: "migration_2024_01" }

560

}

561

);

562

}

563

564

// Parallel bulk operations for better performance

565

const migrationPromises = bulkUserData.map(user =>

566

client.store.putItem(

567

["migration", "users_parallel"],

568

user.id,

569

{ ...user, migratedAt: new Date().toISOString() },

570

{ index: ["name", "email"] }

571

)

572

);

573

574

await Promise.all(migrationPromises);

575

console.log("Bulk migration completed");

576

577

// Archive old data

578

const oldData = await client.store.searchItems(

579

["app", "users"],

580

{

581

filter: {

582

lastLogin: { $lt: "2023-01-01T00:00:00Z" } // Older than 1 year

583

},

584

limit: 1000

585

}

586

);

587

588

for (const item of oldData.items) {

589

// Archive to different namespace

590

await client.store.putItem(

591

["archive", "users"],

592

item.key,

593

{

594

...item.value,

595

archivedAt: new Date().toISOString(),

596

originalNamespace: item.namespace

597

},

598

{ ttl: 86400 * 365 * 2 } // Keep archives for 2 years

599

);

600

601

// Delete from active namespace

602

await client.store.deleteItem(item.namespace, item.key);

603

}

604

```

605

606

### Error Handling and Data Integrity

607

608

```typescript

609

async function robustStoreOperations() {

610

try {

611

// Attempt to store critical data with validation

612

const criticalData = {

613

id: "critical_001",

614

value: 1000000,

615

timestamp: new Date().toISOString(),

616

checksum: "abc123" // Simple integrity check

617

};

618

619

await client.store.putItem(

620

["critical", "financial"],

621

criticalData.id,

622

criticalData,

623

{

624

index: ["id", "timestamp"],

625

overwrite: false, // Fail if exists

626

metadata: { integrity: "validated" }

627

}

628

);

629

630

// Verify storage

631

const stored = await client.store.getItem(

632

["critical", "financial"],

633

criticalData.id

634

);

635

636

if (!stored || stored.value.checksum !== criticalData.checksum) {

637

throw new Error("Data integrity check failed");

638

}

639

640

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

641

642

} catch (error) {

643

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

644

645

// Attempt cleanup or recovery

646

try {

647

await client.store.deleteItem(["critical", "financial"], "critical_001");

648

console.log("Cleaned up partial data");

649

} catch (cleanupError) {

650

console.error("Cleanup also failed:", cleanupError);

651

}

652

653

throw error;

654

}

655

}

656

657

// Transactional-like operations (manual implementation)

658

async function pseudoTransactionalUpdate() {

659

const backupKey = `backup_${Date.now()}`;

660

661

try {

662

// Create backup before update

663

const original = await client.store.getItem(["data", "important"], "record_123");

664

if (original) {

665

await client.store.putItem(

666

["backups", "important"],

667

backupKey,

668

original.value,

669

{ ttl: 86400 } // 24h backup retention

670

);

671

}

672

673

// Perform update

674

await client.store.putItem(

675

["data", "important"],

676

"record_123",

677

{

678

...original?.value,

679

updated_at: new Date().toISOString(),

680

version: (original?.value.version || 0) + 1

681

}

682

);

683

684

console.log("Update completed successfully");

685

686

} catch (error) {

687

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

688

689

// Rollback using backup

690

const backup = await client.store.getItem(["backups", "important"], backupKey);

691

if (backup) {

692

await client.store.putItem(["data", "important"], "record_123", backup.value);

693

console.log("Rollback completed");

694

}

695

696

throw error;

697

} finally {

698

// Cleanup backup

699

await client.store.deleteItem(["backups", "important"], backupKey);

700

}

701

}

702

```

703

704

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.