Skip to main content

commandkit/kv

CommandKit provides a built-in key-value store that offers simple, persistent storage using SQLite. It supports storing any JSON-serializable data types directly, including objects, arrays, dates, maps, sets, and more.

Basic usage

The simplest way to use the key-value store is to create an instance and start storing data:

import { KV } from 'commandkit/kv';

// Create a new KV store
const kv = new KV('data.db');

// Store data directly
kv.set('user:123', { name: 'John', age: 30 });
kv.set('counter', 42);
kv.set('active', true);

// Retrieve data
const user = kv.get('user:123'); // { name: 'John', age: 30 }
const counter = kv.get('counter'); // 42

Configuration options

You can customize the key-value store with various options:

import { openKV } from 'commandkit/kv';

// Create with custom database file
const kv = openKV('my-bot-data.db');

// In-memory store for testing
const testKv = openKV(':memory:');

// Create with specific namespace
const userKv = openKV('data.db', {
namespace: 'users',
});

Supported data types

The KV store supports storing and retrieving these data types:

  • Primitives: string, number, boolean, bigint, null, undefined
  • Objects: Plain objects, nested objects
  • Arrays: Any array of supported types
  • Dates: JavaScript Date objects
  • Collections: Map, Set
  • Buffers: Node.js Buffer objects
  • Regular Expressions: RegExp objects
  • Functions: Function objects (serialized as strings)
// Store different data types
kv.set('user', { name: 'John', preferences: { theme: 'dark' } });
kv.set('tags', ['javascript', 'typescript', 'discord']);
kv.set('created_at', new Date());
kv.set(
'permissions',
new Map([
['admin', true],
['user', false],
]),
);
kv.set('unique_ids', new Set([1, 2, 3]));

Dot notation for nested access

Access and modify nested properties using dot notation:

// Store an object
kv.set('user:123', {
name: 'John Doe',
settings: {
theme: 'dark',
notifications: { email: true, push: false },
},
});

// Access nested properties
const theme = kv.get('user:123.settings.theme'); // 'dark'
const emailNotifications = kv.get(
'user:123.settings.notifications.email',
); // true

// Set nested properties
kv.set('user:123.settings.theme', 'light');
kv.set('user:123.settings.notifications.push', true);

Namespaces

Organize your data into logical groups using namespaces:

const kv = openKV('bot-data.db');

// Create namespace instances
const userKv = kv.namespace('users');
const configKv = kv.namespace('config');
const guildKv = kv.namespace('guilds');

// Store data in different namespaces
userKv.set('123', { name: 'John', level: 5 });
configKv.set('theme', 'dark');
guildKv.set('456', { name: 'My Server', premium: true });

// Same key in different namespaces
userKv.set('settings', { theme: 'light' });
configKv.set('settings', { maintenance: false });

Namespace operations

Each namespace has its own isolated operations:

// Check current namespace
console.log(userKv.getCurrentNamespace()); // 'users'

// List all namespaces
const namespaces = kv.namespaces();
console.log('Available namespaces:', namespaces);

// Namespace-specific counts and data
console.log(`Users: ${userKv.count()}`);
console.log('User keys:', userKv.keys());
console.log('All users:', userKv.all());

Expiration support

Set automatic expiration for temporary data:

// Set data with expiration (1 hour)
kv.setex(
'session:123',
{ userId: 123, token: 'abc123' },
60 * 60 * 1000,
);

// Set expiration for existing keys
kv.set('user:123', { name: 'John' });
kv.expire('user:123', 30 * 60 * 1000); // 30 minutes

// Check time to live
const ttl = kv.ttl('user:123');
if (ttl > 0) {
console.log(`Expires in ${Math.floor(ttl / 1000)} seconds`);
}

Transactions

Execute multiple operations atomically:

// Basic transaction
kv.transaction(() => {
kv.set('user:123', { balance: 100 });
kv.set('user:456', { balance: 200 });
// If any operation fails, all changes are rolled back
});

// Async transactions
await kv.transaction(async () => {
const user = kv.get('user:123') || { balance: 0 };
user.balance += 50;
kv.set('user:123', user);

// Log the transaction
kv.set(`transaction:${Date.now()}`, {
type: 'deposit',
amount: 50,
timestamp: new Date(),
});
});

Bulk operations

Work with multiple entries efficiently:

// Get all data
const allData = kv.all();
const keys = kv.keys();
const values = kv.values();
const count = kv.count();

// Iterate over entries
for (const [key, value] of kv) {
console.log(`${key}:`, value);
}

// Filter entries
const userEntries = [...kv].filter(([key]) =>
key.startsWith('user:'),
);

// Check existence
if (kv.has('user:123')) {
console.log('User exists');
}

// Delete data
kv.delete('user:123');
kv.clear(); // Clear all data in current namespace

Resource management

Use proper resource management to ensure database connections are closed:

// Automatic cleanup with using statement
{
using kv = openKV('data.db');
kv.set('key', 'value');
// kv is automatically closed when the block ends
}

// Manual cleanup
const kv = openKV('data.db');
try {
kv.set('key', 'value');
} finally {
kv.close();
}