Build/Storage

Key-Value Storage

Fast, ephemeral storage for caching, session data, and configuration

Key-value ("KV") storage provides fast data access for agents. Use it for caching, configuration, rate limiting, and data that needs quick lookups.

When to Use Key-Value Storage

Storage TypeBest For
Key-ValueFast lookups, caching, configuration, rate limits
VectorSemantic search, embeddings, RAG
Object (S3)Files, images, documents, media
DatabaseStructured data, complex queries, transactions
Durable StreamsLarge exports, audit logs, real-time data

KV vs Built-in State

Use built-in state (ctx.state, ctx.thread.state, ctx.session.state) for data tied to active requests and conversations. Use KV when you need custom TTL, persistent data across sessions, or shared state across agents.

Basic Operations

Access key-value storage through ctx.kv in agents or c.var.kv in routes. Buckets are auto-created on first use.

Storing Data

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('CacheManager', {
  handler: async (ctx, input) => {
    // Store with optional TTL (minimum 60 seconds)
    await ctx.kv.set('cache', 'api-response', responseData, {
      ttl: 3600,  // expires in 1 hour
      contentType: 'application/json',
    });
 
    // Store without TTL (persists indefinitely)
    await ctx.kv.set('config', 'feature-flags', {
      darkMode: true,
      betaFeatures: false,
    });
 
    return { success: true };
  },
});

Retrieving Data

const agent = createAgent('CacheRetriever', {
  handler: async (ctx, input) => {
    const result = await ctx.kv.get('cache', 'api-response');
 
    if (result.exists) {
      ctx.logger.info('Cache hit', { contentType: result.contentType });
      return { data: result.data };
    }
 
    ctx.logger.info('Cache miss');
    return { data: null };
  },
});

Deleting Data

const agent = createAgent('SessionCleaner', {
  handler: async (ctx, input) => {
    await ctx.kv.delete('sessions', input.sessionId);
    return { deleted: true };
  },
});

Type Safety

Use generics for type-safe data access:

import { type } from 'arktype';
 
const UserPreferences = type({
  theme: "'light' | 'dark'",
  language: 'string',
  notifications: 'boolean',
});
 
type UserPreferences = typeof UserPreferences.infer;
 
const agent = createAgent('PreferenceLoader', {
  handler: async (ctx, input) => {
    const result = await ctx.kv.get<UserPreferences>('prefs', input.userId);
 
    if (result.exists) {
      // TypeScript knows the shape of result.data
      const theme = result.data.theme;  // Type: 'light' | 'dark'
      return { theme };
    }
 
    return { theme: 'light' };  // default
  },
});

Additional Methods

const agent = createAgent('StorageExplorer', {
  handler: async (ctx, input) => {
    // Search keys by keyword
    const matches = await ctx.kv.search('cache', 'user-');
 
    // List all keys in a bucket
    const keys = await ctx.kv.getKeys('cache');
 
    // List all buckets
    const buckets = await ctx.kv.getNamespaces();
 
    // Get bucket statistics
    const stats = await ctx.kv.getStats('cache');
 
    return { keys, buckets, stats };
  },
});

TTL Strategy

Keys persist indefinitely by default. Use TTL for temporary data:

Data TypeSuggested TTL
API cache5-60 minutes (300-3600s)
Session data24-48 hours (86400-172800s)
Rate limit countersUntil period reset
Feature flagsNo TTL (persistent)
interface UserSession {
  userId: string;
  email: string;
  loginAt: string;
  preferences: { theme: string };
}
 
const agent = createAgent('SessionManager', {
  handler: async (ctx, input) => {
    const sessionKey = `session:${input.token}`;
 
    // Check for existing session
    const existing = await ctx.kv.get<UserSession>('sessions', sessionKey);
    if (existing.exists) {
      return { session: existing.data };
    }
 
    // Create new session with 24-hour TTL
    const session: UserSession = {
      userId: input.userId,
      email: input.email,
      loginAt: new Date().toISOString(),
      preferences: { theme: 'light' },
    };
 
    await ctx.kv.set('sessions', sessionKey, session, {
      ttl: 86400,  // 24 hours
    });
 
    return { session };
  },
});

Using in Routes

Routes have the same KV access via c.var.kv:

import { createRouter } from '@agentuity/runtime';
 
const router = createRouter();
 
router.get('/session/:id', async (c) => {
  const sessionId = c.req.param('id');
  const result = await c.var.kv.get('sessions', sessionId);
 
  if (!result.exists) {
    return c.json({ error: 'Session not found' }, 404);
  }
 
  return c.json({ session: result.data });
});
 
export default router;

Best Practices

  • Use descriptive keys: user:{userId}:prefs instead of u123
  • Set appropriate TTLs: Prevent storage bloat with expiring cache entries
  • Handle missing keys: Always check result.exists before accessing data
  • Keep values small: KV is optimized for small-to-medium values; use Object Storage for large files

Next Steps

Need Help?

Join our DiscordCommunity for assistance or just to hang with other humans building agents.

Send us an email at hi@agentuity.com if you'd like to get in touch.

Please Follow us on

If you haven't already, please Signup for your free account now and start building your first agent!