> ## Documentation Index
> Fetch the complete documentation index at: https://hyrex.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Key-Value Store

> Distributed key-value storage with TypeScript

Hyrex provides a distributed key-value store for sharing state between tasks and workflows.

## Basic Usage

The `HyrexKV` class provides static methods for key-value operations:

```typescript theme={null}
import { HyrexKV } from '@hyrex/hyrex';

// Set a value
await HyrexKV.set('user-123', 'John Doe');

// Get a value
const userName = await HyrexKV.get('user-123');
console.log(userName); // 'John Doe'
```

## Value Size Limits

The TypeScript SDK enforces a 1MB size limit for values:

```typescript theme={null}
// This will work
const smallData = JSON.stringify({ items: [1, 2, 3] });
await HyrexKV.set('small-data', smallData);

// This will throw an error if > 1MB
const largeData = 'x'.repeat(1024 * 1024 + 1); // 1MB + 1 byte
try {
    await HyrexKV.set('large-data', largeData);
} catch (error) {
    // Error: Value size exceeds maximum allowed size of 1048576 bytes
}
```

## Working with JSON Data

Store complex objects by serializing to JSON:

```typescript theme={null}
interface UserData {
    id: string;
    name: string;
    preferences: Record<string, any>;
}

// Store object
const userData: UserData = {
    id: '123',
    name: 'Alice',
    preferences: { theme: 'dark', notifications: true }
};
await HyrexKV.set('user-data', JSON.stringify(userData));

// Retrieve and parse
const storedData = await HyrexKV.get('user-data');
const parsed = JSON.parse(storedData) as UserData;
console.log(parsed.name); // 'Alice'
```

## Use Cases

### Task Communication

Share data between parent and child tasks:

```typescript theme={null}
const parentTask = hy.task({
    name: 'parentTask',
    func: async () => {
        const ctx = getHyrexContext();
        
        // Store data for child tasks
        await HyrexKV.set(`task-${ctx.taskId}-config`, JSON.stringify({
            batchSize: 100,
            retryCount: 3
        }));
        
        // Launch child tasks
        await childTask.send({ parentId: ctx.taskId });
        
        return { status: 'launched' };
    }
});

const childTask = hy.task({
    name: 'childTask',
    func: async ({ parentId }: { parentId: string }) => {
        // Retrieve parent's configuration
        const configStr = await HyrexKV.get(`task-${parentId}-config`);
        const config = JSON.parse(configStr);
        
        console.log(`Processing with batch size: ${config.batchSize}`);
        
        return { processed: true };
    }
});
```

### Workflow State

Share state between workflow tasks:

```typescript theme={null}
const dataWorkflow = hy.workflow({
    name: 'dataWorkflow',
    body: (workflowBuilder) => {
        workflowBuilder
            .start(fetchDataTask)
            .next(processDataTask)
            .next(saveResultsTask);
        
        return workflowBuilder;
    },
    config: {}
});

const fetchDataTask = hy.task({
    name: 'fetchData',
    func: async () => {
        const ctx = getHyrexContext();
        const data = await fetchFromAPI();
        
        // Store for next task in workflow
        await HyrexKV.set(
            `workflow-${ctx.workflowRunId}-data`,
            JSON.stringify(data)
        );
        
        return { fetched: true };
    }
});

const processDataTask = hy.task({
    name: 'processData',
    func: async () => {
        const ctx = getHyrexContext();
        
        // Retrieve data from previous task
        const dataStr = await HyrexKV.get(
            `workflow-${ctx.workflowRunId}-data`
        );
        const data = JSON.parse(dataStr);
        
        const processed = transformData(data);
        
        // Store for final task
        await HyrexKV.set(
            `workflow-${ctx.workflowRunId}-result`,
            JSON.stringify(processed)
        );
        
        return { processed: true };
    }
});
```

### Distributed Locks

Implement simple distributed locks:

```typescript theme={null}
const lockTask = hy.task({
    name: 'lockTask',
    func: async ({ resourceId }: { resourceId: string }) => {
        const lockKey = `lock-${resourceId}`;
        const lockValue = `${getHyrexContext().taskId}-${Date.now()}`;
        
        try {
            // Try to acquire lock
            const existing = await HyrexKV.get(lockKey).catch(() => null);
            if (existing) {
                throw new Error('Resource is locked');
            }
            
            // Set lock
            await HyrexKV.set(lockKey, lockValue);
            
            // Do work with exclusive access
            await processExclusiveResource(resourceId);
            
            // Note: No delete method - use TTL or workflow cleanup
            
        } catch (error) {
            console.error('Failed to acquire lock:', error);
            throw error;
        }
        
        return { completed: true };
    }
});
```

### Caching

Cache expensive computation results:

```typescript theme={null}
const computeTask = hy.task({
    name: 'computeTask',
    func: async ({ input }: { input: string }) => {
        const cacheKey = `cache-compute-${input}`;
        
        // Check cache
        try {
            const cached = await HyrexKV.get(cacheKey);
            if (cached) {
                console.log('Cache hit!');
                return JSON.parse(cached);
            }
        } catch (error) {
            // Cache miss, continue
        }
        
        // Expensive computation
        console.log('Cache miss, computing...');
        const result = await expensiveComputation(input);
        
        // Cache result
        await HyrexKV.set(cacheKey, JSON.stringify(result));
        
        return result;
    }
});
```

## Best Practices

1. **Use Namespaced Keys**
   ```typescript theme={null}
   // Good - clear namespace and purpose
   const key = `workflow-${workflowId}-step-${stepName}`;
   const key = `cache-api-${endpoint}-${params}`;
   const key = `user-${userId}-preferences`;

   // Avoid - ambiguous keys
   const key = 'data';
   const key = '123';
   ```

2. **Handle Missing Keys**
   ```typescript theme={null}
   // Get with error handling
   const getValue = async (key: string): Promise<string | null> => {
       try {
           return await HyrexKV.get(key);
       } catch (error) {
           // Key doesn't exist
           return null;
       }
   };
   ```

3. **Clean Up Workflow Data**
   ```typescript theme={null}
   // Store with workflow-specific keys
   const workflowKey = `workflow-${ctx.workflowRunId}-temp`;
   await HyrexKV.set(workflowKey, data);

   // Consider cleanup in final task
   // Note: TypeScript SDK doesn't have delete method
   ```

4. **Validate Data Size**
   ```typescript theme={null}
   const storeData = async (key: string, data: any) => {
       const serialized = JSON.stringify(data);
       
       // Check size before storing (1MB limit)
       const sizeInBytes = new TextEncoder().encode(serialized).length;
       if (sizeInBytes > 1024 * 1024) {
           throw new Error(`Data too large: ${sizeInBytes} bytes`);
       }
       
       await HyrexKV.set(key, serialized);
   };
   ```

## Limitations

* **No Delete Method**: Keys cannot be explicitly deleted in the TypeScript SDK
* **1MB Size Limit**: Values are limited to 1MB (1,048,576 bytes)
* **String Values Only**: All values must be strings (serialize objects to JSON)
* **No TTL Support**: Keys don't expire automatically

## Next Steps

<CardGroup cols={2}>
  <Card title="Workflows" icon="diagram-project" href="/docs/reference/typescript/workflows">
    Share data in workflows
  </Card>

  <Card title="Tasks" icon="list-check" href="/docs/reference/typescript/tasks">
    Pass data between tasks
  </Card>

  <Card title="Cron" icon="clock" href="/docs/reference/typescript/cron">
    Store state for scheduled tasks
  </Card>
</CardGroup>
