TinyCloud provides a key-value storage API for persisting user data. All data is stored in the user’s space and fully controlled by them. The API uses tc.kv in both SDKs:
- Web SDK:
tc.kv.put / get / list / del
- Node SDK:
tc.kv.put / get / list / del
Basic Operations
Put
Store a value at a key.
Get
Retrieve a value by key.
const result = await tc.kv.get('user/profile');
if (result.ok) {
console.log(result.data.data);
// { name: 'Alice', email: '[email protected]' }
}
List
List keys matching a prefix.
const result = await tc.kv.list('user/');
if (result.ok) {
console.log(result.data.keys);
// ['user/profile', 'user/settings', 'user/preferences']
}
Delete
Remove a key and its value.
await tc.kv.del('user/profile');
Data Types
The KV API accepts multiple data types as values.
Strings
JSON Objects
Arrays
Binary Data
await tc.kv.put('greeting', 'Hello, World!');
const result = await tc.kv.get('greeting');
if (result.ok) {
console.log(result.data.data);
// "Hello, World!"
}
await tc.kv.put('config', {
theme: 'dark',
notifications: true,
language: 'en',
});
const result = await tc.kv.get('config');
if (result.ok) {
console.log(result.data.data);
// { theme: 'dark', notifications: true, language: 'en' }
}
await tc.kv.put('tags', ['typescript', 'react', 'tinycloud']);
const result = await tc.kv.get('tags');
if (result.ok) {
console.log(result.data.data);
// ['typescript', 'react', 'tinycloud']
}
const imageBuffer = new Uint8Array([...]); // Binary data
await tc.kv.put('avatar.png', imageBuffer, {
contentType: 'image/png',
});
const result = await tc.kv.get('avatar.png');
The examples in the Data Types section use tc.kv. The method signatures are identical across both Web and Node SDKs.
Organizing Keys with Prefixes
Use path-like keys to organize your data logically. The / character has no special meaning to the storage engine, but it provides a natural way to group related data and query by prefix.
// User data
await tc.kv.put('user/profile', { name: 'Alice' });
await tc.kv.put('user/settings', { theme: 'dark' });
await tc.kv.put('user/preferences', { lang: 'en' });
// Application data
await tc.kv.put('app/cache/items', [...]);
await tc.kv.put('app/state', { lastSync: Date.now() });
// Documents organized by year
await tc.kv.put('documents/2025/report.json', { ... });
await tc.kv.put('documents/2025/summary.json', { ... });
await tc.kv.put('documents/2026/plan.json', { ... });
// List all 2025 documents
const docs2025 = await tc.kv.list('documents/2025/');
// ['documents/2025/report.json', 'documents/2025/summary.json']
// List all user keys
const userKeys = await tc.kv.list('user/');
// ['user/preferences', 'user/profile', 'user/settings']
Design your key structure upfront. A consistent prefix scheme like {category}/{subcategory}/{item} makes it easy to query related data with list().
Store and retrieve metadata alongside values. Metadata is useful for tracking authorship, versioning, content types, and other attributes without embedding them in the value itself.
// Put with metadata
await tc.kv.put('document', content, {
metadata: {
author: 'Alice',
version: '1.0',
createdAt: new Date().toISOString(),
},
});
// Get metadata only (without fetching the value)
const meta = await tc.kv.getMetadata('document');
console.log(meta);
// { author: 'Alice', version: '1.0', createdAt: '2026-...' }
Error Handling
Handle common error cases when working with the KV API.
const result = await tc.kv.get('nonexistent-key');
if (!result.ok) {
if (result.error.code === 'NOT_FOUND') {
console.log('Key does not exist');
} else if (result.error.code === 'UNAUTHORIZED') {
console.log('Session expired or invalid');
} else {
console.error('Unexpected error:', result.error);
}
}
Practical Examples
User Preferences
Store and load user preferences with sensible defaults.
// Save preferences
await tc.kv.put('preferences', {
theme: 'dark',
fontSize: 14,
notifications: { email: true, push: false },
});
// Load preferences with defaults
const defaults = {
theme: 'light',
fontSize: 12,
notifications: { email: true, push: true },
};
const result = await tc.kv.get('preferences');
const prefs = result.ok ? result.data.data : defaults;
Document Storage with Versioning
Track document versions using key prefixes.
// Save a new version of a document
async function saveDocument(docId: string, content: any) {
const version = Date.now();
// Save the current version
await tc.kv.put(`docs/${docId}/current`, content);
// Save to version history
await tc.kv.put(`docs/${docId}/history/${version}`, content);
}
// List all versions of a document
async function getDocumentHistory(docId: string) {
const result = await tc.kv.list(`docs/${docId}/history/`);
return result.ok ? result.data.keys : [];
}
// Load a specific version
async function loadVersion(docId: string, version: string) {
const result = await tc.kv.get(`docs/${docId}/history/${version}`);
return result.ok ? result.data.data : null;
}
Manage a simple shopping cart.
// Add an item to the cart
async function addToCart(item: { id: string; name: string; price: number }) {
const result = await tc.kv.get('cart');
const cart = result.ok ? result.data.data : [];
cart.push(item);
await tc.kv.put('cart', cart);
}
// Get cart contents
async function getCart() {
const result = await tc.kv.get('cart');
return result.ok ? result.data.data : [];
}
// Clear the cart
async function clearCart() {
await tc.kv.del('cart');
}
Multi-Tenant Key Isolation
Use the kvPrefix configuration to isolate keys across different tenants or environments without changing your application code.
// Production environment
const tcProd = new TinyCloudWeb({
kvPrefix: 'prod',
});
// Staging environment
const tcStaging = new TinyCloudWeb({
kvPrefix: 'staging',
});
// Both write to 'config', but the actual keys are different:
// prod/config vs staging/config
await tcProd.kv.put('config', { env: 'production' });
await tcStaging.kv.put('config', { env: 'staging' });
API Quick Reference
| Method | Web SDK | Node SDK | Description |
|---|
| Store a value | tc.kv.put(key, value) | tc.kv.put(key, value) | Write data at the given key |
| Retrieve a value | tc.kv.get(key) | tc.kv.get(key) | Read data at the given key (returns Result) |
| List keys | tc.kv.list(prefix) | tc.kv.list(prefix) | List keys matching a prefix (returns Result) |
| Delete a value | tc.kv.del(key) | tc.kv.del(key) | Remove a key and its data |
| Get metadata | tc.kv.getMetadata(key) | tc.kv.getMetadata(key) | Retrieve metadata without the value |