Key Value (KV)
Overview
The Raindrop framework provides a key-value storage interface for storing and retrieving data by unique keys. KV stores are ideal for caching, session storage, configuration management, and simple data persistence.
The KV interface supports multiple data types including text, JSON objects, binary data, and streams. It also provides metadata support, expiration controls, and listing capabilities for managing your stored data.
Creating a KV Store
Add a KV store to your Raindrop project by defining it in your application manifest:
application "demo-app" { kv_store "demo-cache" {}}
After running raindrop build generate
, the store becomes available as env.DEMO_CACHE
in your services and actors.
Core Methods
put(key, value, options?)
Stores a value with the given key.
// Store textawait env.DEMO_CACHE.put("user:123", "John Doe");
// Store with expiration (TTL in seconds)await env.DEMO_CACHE.put("session:abc", sessionData, { expirationTtl: 3600 // 1 hour});
// Store with metadataawait env.DEMO_CACHE.put("file:456", fileData, { metadata: { contentType: "image/png", size: 1024 }});
Options:
expirationTtl
: Expire after N seconds (minimum 60 seconds)expiration
: Expire at Unix timestamp (must be in the future)metadata
: Custom metadata object
get(key, type?)
Retrieves a value by key with optional type conversion.
// Get as text (default)const value = await env.DEMO_CACHE.get("user:123");
// Get as JSONconst data = await env.DEMO_CACHE.get("config:settings", "json");
// Get as binary dataconst buffer = await env.DEMO_CACHE.get("file:456", "arrayBuffer");
// Get as streamconst stream = await env.DEMO_CACHE.get("large:file", "stream");
Types:
"text"
(default): Returns string"json"
: Parses JSON and returns object"arrayBuffer"
: Returns ArrayBuffer"stream"
: Returns ReadableStream
Returns null
if key doesn’t exist or has expired.
getWithMetadata(key, type?)
Retrieves value along with metadata and cache information.
const result = await env.DEMO_CACHE.getWithMetadata("file:456", "json");
console.log(result.value); // The stored valueconsole.log(result.metadata); // Custom metadataconsole.log(result.cacheStatus); // Cache status string or null
Response:
value
: The stored value ornull
if not foundmetadata
: Custom metadata object ornull
cacheStatus
: Cache status string ornull
list(options?)
Lists stored keys with optional filtering and pagination.
// List all keysconst allKeys = await env.DEMO_CACHE.list();
// List with prefix filterconst userKeys = await env.DEMO_CACHE.list({ prefix: "user:" });
// List with limit and paginationconst batch = await env.DEMO_CACHE.list({ prefix: "session:", limit: 100, cursor: "..." // from previous call});
Options:
prefix
: Filter keys by prefixlimit
: Maximum number of keys to returncursor
: Pagination cursor from previous call
Response:
keys
: Array of key objects withname
,expiration
,metadata
list_complete
: Boolean indicating if more results existcursor
: Use for next page (iflist_complete
is false)
delete(key)
Removes a key from storage.
await env.DEMO_CACHE.delete("user:123");
The operation is idempotent - deleting a non-existent key doesn’t throw an error.
Data Types
Text Storage
Store and retrieve simple string values like configuration settings, user preferences, or short text data.
await env.DEMO_CACHE.put("config:theme", "dark");const theme = await env.DEMO_CACHE.get("config:theme"); // "dark"
JSON Storage
Store complex objects and data structures by serializing them as JSON strings.
const user = { id: 123, name: "John", active: true };await env.DEMO_CACHE.put("user:123", JSON.stringify(user));const userData = await env.DEMO_CACHE.get("user:123", "json");
Binary Storage
Store binary data like images, files, or any raw bytes using ArrayBuffer.
const imageData = new ArrayBuffer(1024);await env.DEMO_CACHE.put("image:456", imageData);const retrievedData = await env.DEMO_CACHE.get("image:456", "arrayBuffer");
Stream Storage
Store large data as streams for memory-efficient handling of big files or continuous data.
const stream = new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode("chunk1")); controller.enqueue(new TextEncoder().encode("chunk2")); controller.close(); }});
await env.DEMO_CACHE.put("file:789", stream);const retrievedStream = await env.DEMO_CACHE.get("file:789", "stream");
Examples
Basic Usage
Simple session management using KV storage with automatic expiration.
export default class extends Service<Env> { async fetch(request: Request): Promise<Response> { // Store user session const sessionId = "abc123"; await this.env.DEMO_CACHE.put(`session:${sessionId}`, JSON.stringify({ userId: 123, loginTime: new Date().toISOString() }), { expirationTtl: 24 * 60 * 60 // 24 hours });
// Retrieve session const sessionData = await this.env.DEMO_CACHE.get(`session:${sessionId}`, "json");
if (!sessionData) { return new Response("Session not found", { status: 401 }); }
return new Response(`Welcome user ${sessionData.userId}`); }}
Cache Pattern
Implement read-through caching to reduce database load and improve response times.
async getUser(userId: string) { // Check cache first let user = await this.env.DEMO_CACHE.get(`user:${userId}`, "json");
if (!user) { // Cache miss - fetch from database user = await this.database.getUser(userId);
// Cache for 5 minutes await this.env.DEMO_CACHE.put(`user:${userId}`, JSON.stringify(user), { expirationTtl: 300 }); }
return user;}
Cleanup Pattern
Manually clean up expired entries by listing and filtering based on expiration times.
async cleanupExpiredSessions() { const sessions = await this.env.DEMO_CACHE.list({ prefix: "session:" }); const now = Date.now() / 1000;
for (const key of sessions.keys) { if (key.expiration && key.expiration < now) { await this.env.DEMO_CACHE.delete(key.name); } }}