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 Type | Best For |
|---|---|
| Key-Value | Fast lookups, caching, configuration, rate limits |
| Vector | Semantic search, embeddings, RAG |
| Object (S3) | Files, images, documents, media |
| Database | Structured data, complex queries, transactions |
| Durable Streams | Large 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 Type | Suggested TTL |
|---|---|
| API cache | 5-60 minutes (300-3600s) |
| Session data | 24-48 hours (86400-172800s) |
| Rate limit counters | Until period reset |
| Feature flags | No 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}:prefsinstead ofu123 - Set appropriate TTLs: Prevent storage bloat with expiring cache entries
- Handle missing keys: Always check
result.existsbefore accessing data - Keep values small: KV is optimized for small-to-medium values; use Object Storage for large files
Next Steps
- Vector Storage: Semantic search and embeddings
- Object Storage (S3): File and media storage
- Database: Relational data with queries and transactions
- Durable Streams: Large data exports
- State Management: Built-in request/thread/session state
Need Help?
Join our Community 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!