Skip to main content
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.
await tc.kv.put('user/profile', {
  name: 'Alice',
  email: '[email protected]',
});

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.
await tc.kv.put('greeting', 'Hello, World!');

const result = await tc.kv.get('greeting');
if (result.ok) {
  console.log(result.data.data);
  // "Hello, World!"
}
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().

Metadata

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;
}

Shopping Cart

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

MethodWeb SDKNode SDKDescription
Store a valuetc.kv.put(key, value)tc.kv.put(key, value)Write data at the given key
Retrieve a valuetc.kv.get(key)tc.kv.get(key)Read data at the given key (returns Result)
List keystc.kv.list(prefix)tc.kv.list(prefix)List keys matching a prefix (returns Result)
Delete a valuetc.kv.del(key)tc.kv.del(key)Remove a key and its data
Get metadatatc.kv.getMetadata(key)tc.kv.getMetadata(key)Retrieve metadata without the value